多线程安全问题分析
在明白了多线程中会引发安全问题的几个重要点后,理解下面的程序你就会非常的轻松。
个人理解总结会引发多线程安全问题的几个重要点:
1.多线程中,每个线程都会从主内存中拷贝变量值到多线程工作内存中。
2.一个线程修改了工作内存的值,如果没有及时同步到主内存中,就会导致其它线程读取主内存的值时,发生脏读,即读取到了过时的值。
3.多个线程不互斥,会在i++等不是原子性操作的语句中,发生很严重的线程抢占现象,同时都想输出,都想修改值,但是这是修改的是线程工作内存中的值,其它线程都不能及时看到,即其它线程不可见,只有等到CPU想同步数据到主内存才不会同步,这种同步是不定时的,但是确实极快的,虽说极快但是却能够很轻松让其它线程看不见最新值,只能说偶尔看见最新值。会出现输出重复的值,负数的值。
4.指令重排序,这个也是严重的问题,由于JVM优化的原因,比如前面调用方法,后面修改值,在指令重排序的情况下,会导致先赋值,后调用方法。如果方法里面依赖该值,就会出现严重的线程安全问题。说实话,对于指令重排序,我到现在还不理解。因为没有亲自体验到!!!
Java 于是祭出两大神器:
volatile【解决值的不可见+干掉指令重排序】
synchronized【通过锁这个概念让线程互斥+干掉线程间值不可见的问题】
让我们来小试牛刀
卖票啦,哈哈
/**
* Created by FireLang on 2017-07-16.
*/
public class Tickets implements Runnable{
//总的票数
private int num = 100;
private boolean isExecute = true;
@Override
public void run() {
//1.注意一个点:线程方法没有结束,线程工作内存中的值,就已经同步到了主内存中了。
while (isExecute){
//2.这里就是消耗每个线程被分配到的时间片。导致的结果就是:每个线程都有执行到 synchronized 代码块里面。
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
/**
* 在第1个注释中,我就提到了,方法没有结束,为什么线程工作内存中的变量会同步到主内存中呢??
* 因为,synchronized 关键字的作用啦,他有两个作用,一个是线程互斥,一个是锁释放后将会对线程工作内存中的变量同步到主内存中去。让其它线程可见。
* 线程工作内存中的变量可见和线程是否互斥,是导致线程安全问题的重要原因。
*/
synchronized (this){
if (num>0){
System.out.println(Thread.currentThread().getName()+"--->"+num--);
}else{
isExecute = false;
}
}
}
}
}
/**
* Created by FireLang on 2017-07-16.
*/
public class SellTickets {
public static void main(String[] args) {
//生产将要售出的票
Tickets tickets = new Tickets();
//创建线程
Thread t1 = new Thread(tickets,"T1");
Thread t2 = new Thread(tickets,"T2");
Thread t3 = new Thread(tickets,"T3");
//开始售票
t1.start();
t2.start();
t3.start();
}
}
各位大佬可以尝试运行代码,将会看到各个线程间的和谐输出!!!哈哈哈!!!