多线程volatile

volatile看起来简单,但是要想理解它还是比较难的,这里只是对其进行基本的了解。

通俗点讲就是说一个变量如果用volatile修饰了,则Java可以确保所有线程看到这个变量的值是一致的,如果某个线程对volatile修饰的共享变量进行更新,那么其他线程可以立马看到这个更新,这就是所谓的线程可见性。

volatile相对于synchronized稍微轻量些,在某些场合它可以替代synchronized,但是又不能完全取代synchronized,只有在某些场合才能够使用volatile。使用它必须满足如下两个条件:

对变量的写操作不依赖当前值;
该变量没有包含在具有其他变量的不变式中。

volatile经常用于两个两个场景:状态标记量、double check Lock。

multipleThreadjpg.jpg

在并发编程中我们一般都会遇到这三个基本概念:原子性、可见性、有序性。我们稍微看下volatile

原子性
原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

原子性就像数据库里面的事务一样,他们是一个团队,同生共死。其实理解原子性非常简单,我们看下面一个简单的例子即可:

i = 0; ---1
j = i ; ---2
i++; ---3
i = j + 1; ---4
上面四个操作,有哪个几个是原子操作,那几个不是?如果不是很理解,可能会认为都是原子性操作,其实只有1才是原子操作,其余均不是。

1—在Java中,对基本数据类型的变量和赋值操作都是原子性操作;
2—包含了两个操作:读取i,将i值赋值给j
3—包含了三个操作:读取i值、i + 1 、将+1结果赋值给i;
4—同三一样

在单线程环境下我们可以认为整个步骤都是原子性操作,但是在多线程环境下则不同,Java只保证了基本数据类型的变量和赋值操作才是原子性的(注:在32位的JDK环境下,对64位数据的读取不是原子性操作*,如long、double)。要想在多线程环境下保证原子性,则可以通过锁、synchronized来确保。

volatile是无法保证复合操作的原子性

可见性
可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。

在上面已经分析了,在多线程环境下,一个线程对共享变量的操作对其他线程是不可见的。

Java提供了volatile来保证可见性。

当一个变量被volatile修饰后,表示着线程本地内存无效,当一个线程修改共享变量后他会立即被更新到主内存中,当其他线程读取共享变量时,它会直接从主内存中读取。
当然,synchronize和锁都可以保证可见性。

有序性
有序性:即程序执行的顺序按照代码的先后顺序执行。

在Java内存模型中,为了效率是允许编译器和处理器对指令进行重排序,当然重排序它不会影响单线程的运行结果,但是对多线程会有影响。

Java提供volatile来保证一定的有序性。最著名的例子就是单例模式里面的DCL(双重检查锁)。这里可以理解为,当两个处理器针对一个volatile做操作时,一个在Store而另一个在Load时,
因为fence()的lock使得Store和Load不得不错开,而Load自然会推迟到Store完成之后,所以才有了这个Happen-Before原则:
volatile变量的写先于volatile变量的读.

public class ArmyRunnable implements Runnable{

    //ensure the thread can read exactly the value of other thread write  
    volatile boolean keepRunning = true;
    
    @Override
    public void run() {
        while(keepRunning) {
            //Attack 5 times
            for(int i=0; i<5;i++) {
                System.out.println(Thread.currentThread().getName()+" Attack "+i+"st");
                Thread.yield();
            }
        }
        System.out.println(Thread.currentThread().getName()+"Finish Attck !");      
    }

}

public class KeyPersonThread extends Thread {
    public void run() {
        System.out.println(Thread.currentThread().getName()+" start fight!");
        for(int i=0; i<10;i++) {
            System.out.println(Thread.currentThread().getName()+" fight Evil "+ i+ "st");
        }
        System.out.println(Thread.currentThread().getName()+" finish fight!");
    }
}


public class Stage extends Thread {
    public void run() {
        ArmyRunnable armyTaskOfGod = new ArmyRunnable();
        ArmyRunnable armyTaskOfEvil = new ArmyRunnable();
        
        Thread armyOfGod = new Thread(armyTaskOfGod,"God ");
        Thread armyOfEvil = new Thread(armyTaskOfEvil,"Evil");
        
        armyOfGod.start();
        armyOfEvil.start();
        
        //Stage Thread Sleep
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        System.out.println("Suddenly, Heo comes.");
        Thread keyPerson = new KeyPersonThread();
        keyPerson.setName("Hero");
        
        armyTaskOfGod.keepRunning = false;
        armyTaskOfEvil.keepRunning = false;
        
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        keyPerson.start();
        
        try {
            keyPerson.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
//      
//      try {
//          armyOfGod.join();
//      } catch (InterruptedException e) {
//          // TODO Auto-generated catch block
//          e.printStackTrace();
//      }
        
        System.out.println("Finish");
        
    }
    
    public static void main(String[] args) {
        new Stage().start();
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容