java多线程及线程安全详解
为什么要使用多线程:
单线程只能干一件事 而多线程可以同时干好多事(将任务放到线程里执行 效率高)
而所谓同时干并不是真正意义上的同时 只是(这里就叫CPU)cpu在每个线程中随机切换来执行 线程中要干的活
多线程编写:
1)第一种:(线程类)
class Stu1 extends Thread{
//重写 run方法
}
调用:Stu1 su = new Stu1();
su.start()//内部会自动调用run方法 把run方法放到线程上调用
2)第二种:普通任务类(由于第一种类只能是单继承 就没法实现继承其他父类并且继承线程类 所以第一种方法扩展性比较差)
直接创建线程对象 线程内干的事 放到一个自定义对象里面实现 (用到修饰设计模式)
底层代码
class Thread{
private Runnable r
public void Thread(Runnable r){ //利用有参构造将自定义对象传进来
this.r = r;
}
public void start(){
r.run();
}
}
class Stu implements Runnable{} 通过实现接口 线程类底层start调用的run方法 实际就是我们自定义类中的run方法
Thread th = new Thread(new Stu()) //直接创建线程对象
th.start();
我们用第二种方法创建多线程如下:
public static void main(String[] args) {
// 不同线程干同一件事
SaleWindow sw = new SaleWindow();
Thread t1 = new Thread(sw);
Thread t2 = new Thread(sw);
t1.setName("窗口A");
t2.setName("窗口B");
t1.start();
t2.start();
}
public class SaleWindow implements Runnable {
private int ticketCount = 10; @Override
public void run() {
// TODO Auto-generated method stub
// 多个窗口卖票
for(int i = 0;i<10;i++){
if(ticketCount>0){
//字符串拼接信息 变量+"" 就可以拼接成字符串
System.out.println(Thread.currentThread().getName()+"卖出"+ticketCount+"张票");
ticketCount--;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
// e.printStackTrace();
}
}
}
} }
最后结果图:
从运行结果来看:同一张票卖给了二个人 这是在现实生活中不允许的 这样就会产生线程安全问题。
而产生线程安全问题的有三个要素必须同时满足才会产生线程安全:
1、必须有共享资源
2、必须是多线程环境
3、每个线程都适用了共享资源
而上面的例子:票是共享资源、又是多线程环境、线程执行任务的时候又使用了共享资源 所以会产生线程安全
怎么解决线程安全?
解决线程安全其核心:就是将某一个线程中的任务给锁(同步锁)起来,这个时候JVM就不得不等到本任务里的代码执行完以后在去执行另外一个线程里的任务。
(ps:在没加同步锁时jvm可以随时在线程之间随机切换,很有可能本线程的任务没有执行完就切换到别的线程上去了)
二种方法:
1、同步代码块:
public class SaleWindow implements Runnable {
private int ticketCount = 10; @Override
public void run() {
// TODO Auto-generated method stub
// 多个窗口卖票
for (int i = 0; i < 10; i++) {//可以随意设置锁
synchronized (this) {
if (ticketCount > 0) {
// 字符串拼接信息 变量+"" 就可以拼接成字符串
System.out.println(Thread.currentThread().getName() + "卖出"
+ ticketCount + "张票");
ticketCount--;
try {
Thread.sleep(500);//每隔500毫秒 线程休眠 随后自己自动会唤醒(目的是为了调节线程速度)
} catch (InterruptedException e) {
// TODO Auto-generated catch block
// e.printStackTrace();
}
}
}
}
} }
2、同步方法:
public class SaleWindow implements Runnable {
private int ticketCount = 10;
//默认固定的锁对象this
//将产生线程安全的代码封装到方法里并设置成同步方法
public synchronized void syncB() {
if (ticketCount > 0) {
// 字符串拼接信息 变量+"" 就可以拼接成字符串
System.out.println(Thread.currentThread().getName() + "卖出"
+ ticketCount + "张票");
ticketCount--;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
// e.printStackTrace();
}
}
} @Override
public void run() {
// TODO Auto-generated method stub
// 多个窗口卖票
for (int i = 0; i < 10; i++) {
syncB();
}
} }
结果图:
最后结果每个窗口不能卖同样的票解决了线程安全问题
线程之间的通信
(ps:线程之间的通信和线程安全无关联二者不是一回事)
怎样才能让二个线程之间进行通信?
线程之间是相互独立的互不联系 而真正意义上的通信是通过中间件(同步锁 必须是同一把锁)来达到线程之间通信的目的
案例:二个线程来回交替输出一条数据(意思就是必须按照我说一句你说一句的这个规则走)
class BingB extends Thread {
public void run(){
for(int i = 0;i<5;i++){
System.out.println("冰冰美,如花丑");
}
}
} class RuH extends Thread{
public void run(){
for(int i = 0;i<5;i++){
System.out.println("如花美,冰冰丑");
}
}
} public class Test {
public static void main(String[] args) {
BingB bb = new BingB();
RuH rh = new RuH();
bb.start();
rh.start();
}
}
结果图:此结果不是交替出现的
要想达到交替的目的代码如下:
class MyLock{
static Object o = new Object();
} class BingB extends Thread {
public void run(){
for(int i = 0;i<5;i++){
//加锁(意思是必须等到本线程的任务代码执行完以后才去执行别的线程的代码)
synchronized (MyLock.o) {
System.out.println("冰冰美,如花丑");
MyLock.o.notify();//唤醒另一个线程(这里表示如花线程)
try {
MyLock.o.wait();//暂时彻底休眠本线程(不会自动唤醒 需要手动唤醒) 同时解锁 阻塞线程 代码就不会往下继续走 jvm会切换到另外一个线程中去
} catch (InterruptedException e) {
// TODO Auto-generated catch block
}
}
}
}
} class RuH extends Thread{
public void run(){
for(int i = 0;i<5;i++){
synchronized (MyLock.o) {
System.out.println("如花美,冰冰丑");
MyLock.o.notify();//唤醒另一个线程(这里表示如冰冰程)
try {
MyLock.o.wait();//暂时彻底休眠本线程 同时解锁 阻塞线程
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
} public class Test {
public static void main(String[] args) {
BingB bb = new BingB();
RuH rh = new RuH();
bb.start();
rh.start();
}
}
从以上例子可以看出产生线程安全并解决线程安全 到实现线程间的通信 都用到了 同步锁 所以 同步锁既可以解决线程安全问题 又可以解决线程之间的通信
多线程状态流程图:
写到这里有关线程的基本讲解完毕,如果内容有我自己理解错误的地方还请各位大神指教,小弟不吝求教!
java多线程及线程安全详解的更多相关文章
- Java多线程之线程池详解
前言 在认识线程池之前,我们需要使用线程就去创建一个线程,但是我们会发现有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因 ...
- JAVA多线程Thread VS Runnable详解
要求 必备知识 本文要求基本了解JAVA编程知识. 开发环境 windows 7/EditPlus 演示地址 源文件 进程与线程 进程是程序在处理机中的一次运行.一个进程既包括其所要执行的指令,也 ...
- Java 并发编程 | 线程池详解
原文: https://chenmingyu.top/concurrent-threadpool/ 线程池 线程池用来处理异步任务或者并发执行的任务 优点: 重复利用已创建的线程,减少创建和销毁线程造 ...
- Java面试问题——线程全面详解总结
一.多线程是什么?为什么要用多线程? 介绍多线程之前要介绍线程,介绍线程则离不开进程. 首先进程 :是一个正在执行中的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元: ...
- java多线程环境单例模式实现详解
Abstract 在开发中,如果某个实例的创建需要消耗很多系统资源,那么我们通常会使用惰性加载机制,也就是说只有当使用到这个实例的时候才会创建这个实例,这个好处在单例模式中得到了广泛应用.这个机制在s ...
- Java 多线程同步和异步详解
java线程 同步与异步 线程池 1)多线程并发时,多个线程同时请求同一个资源,必然导致此资源的数据不安全,A线程修改了B线 程的处理的数据,而B线程又修改了A线程处理的数理.显然这是由于全局资源造成 ...
- java多线程——同步块synchronized详解
Java 同步块(synchronized block)用来标记方法或者代码块是同步的.Java同步块用来避免竞争.本文介绍以下内容: Java同步关键字(synchronzied) 实例方法同步 静 ...
- Java同步之线程池详解
带着问题阅读 1.什么是池化,池化能带来什么好处 2.如何设计一个资源池 3.Java的线程池如何使用,Java提供了哪些内置线程池 4.线程池使用有哪些注意事项 池化技术 池化思想介绍 池化思想是将 ...
- Java多线程中join方法详解
join()方法用于让当前执行线程等待join线程执行结束.其实现原理是不停的检查join线程是否存活,如果join线程存活则让当前线程永远等待. join()方法部分实现细节 while(isAli ...
随机推荐
- java http缓存
HTTP/1.1中缓存的目的是为了在很多情况下减少发送请求,也即直接返回缓存:同时在许多情况下可以不需要发送完整响应.前者减少了网络回路的数量,挺高响应速度,HTTP利用一个"过期(expi ...
- Struts2(XWork)中的Container 一
本文是<<struts2 技术内幕>>的学习笔记 在进行面向对象编程的时候,我们不可避免地要使用继承实现等等java提供的语法支持.但是复杂的对象关系也为对象生命周期的管理带来 ...
- Linux - 有效群组(effective group)与初始群组(initial group),groups,newgrp
每个使用者在他的 /etc/passwd 里面的第四栏有所谓的 GID ,那个 GID 就是所谓的『初始群组 (initial group) 』!也就是说,当用户一登陆系统,立刻就拥有这个群组的相关权 ...
- OpenCV实现图像物体轮廓,前景背景,标记,并保存。
#include <iostream> // for standard I/O #include <string> // for strings #include <io ...
- java--字符编码,正则表达式
转载请申明出处:http://blog.csdn.net/xmxkf day21 字符编码 06-IO流(转换流的字符编码) 字符编码: 1. 字符流的出现为了方便操作字符,更重要的是加入了编码转 ...
- break和continue的简单介绍
1.break break 用于完全结束一个循环,跳出循环体!不管是哪种循环,如果在程序中遇到Break,系统将完全结束该循环,开始执行循环之后的代码: public class TestBreak ...
- java多继承
众所周知,java面向对象语言中只有单继承的编程语言,也许你会说,通过实现多个接口这种变通的方式达到多继承的目的.没错,你说的对,不过这并不是本片文章要说到的内容,本文要讲到的内容是java中实实在在 ...
- Spring3.x企业应用开发实战-Spring+Hibernat架构分析
1: 持久层设计 采用Spring注解方式省略了大量Hibernate ORM配置文件: BaseDAO减少DAO层代码量,只需要编写非通用型的持久层方法: 持久层提供分页支持: Hibernate ...
- 使用 JMeter 进行压力测试
一.前言 压力测试是每一个Web应用程序上线之前都需要做的一个测试,他可以帮助我们发现系统中的瓶颈问题,减少发布到生产环境后出问题的几率:预估系统的承载能力,使我们能根据其做出一些应对措施.所以压力测 ...
- Redis的安装及学习
最近因为做Chatbot项目需要对于NoSQL数据库进行研究,调研范围包括MongoDB和Redis.本文将介绍Redis在Windows环境的安装及如何利用python来操作Redis数据库. Re ...