首页 > 代码库 > 线程(-)volatile

线程(-)volatile

volatile概念:volatile关键字的主要作用是使变量在多个线程间可见

代码1:isRunning变量并没有设置为volatile

 1 package com.java.day01_volatile;
 2 
 3 public class RunThread extends Thread {
 4     private boolean isRunning = true;
 5 
 6     private void setRunning(boolean isRunning) {
 7         this.isRunning = isRunning;
 8     }
 9 
10     public void run() {
11         System.out.println("进入run方法。。。");
12         while (isRunning == true) {
13             //
14         }
15 
16         System.out.println("线程停止");
17     }
18 
19     public static void main(String[] args) throws InterruptedException {
20         RunThread r = new RunThread();
21         r.start();
22 
23         Thread.sleep(4000);
24 
25         r.setRunning(false);
26         System.out.println("isRunning的值已经设置为false");
27         Thread.sleep(1000);
28         System.out.println(r.isRunning);
29     }
30 
31 }

运行结果:

技术分享

运行结果:此时,虽然改变了isRunning的值,但是线程中的值并没有改变

java JDK1.5以后,会为线程专门分配一块内存,将主内存中的数据copy到内存线程的工作区中,目的:线程执行的时候效率可以更高一些。

volatile关键字,线程执行流程图

技术分享

 

 

 

代码2:将isRunning设置为volatile

 1 package com.java.day01_volatile;
 2 
 3 public class RunThread extends Thread {
 4     private volatile boolean isRunning = true;
 5 
 6     private void setRunning(boolean isRunning) {
 7         this.isRunning = isRunning;
 8     }
 9 
10     public void run() {
11         System.out.println("进入run方法。。。");
12         while (isRunning == true) {
13             //
14         }
15 
16         System.out.println("线程停止");
17     }
18 
19     public static void main(String[] args) throws InterruptedException {
20         RunThread r = new RunThread();
21         r.start();
22 
23         Thread.sleep(4000);
24 
25         r.setRunning(false);
26         System.out.println("isRunning的值已经设置为false");
27         Thread.sleep(1000);
28         System.out.println(r.isRunning);
29     }
30 
31 }

运行结果:

1 进入run方法。。。
2 isRunning的值已经设置为false
3 线程停止
4 false

运行结果:改变isRunning的值后,线程也读取到了isRunning的值,线程停止

  在java中,每一个线程都会有一块工作内存区,其中存放着所有线程共享的主内存中变量值的拷贝。当线程执行时,他在自己的工作内存区中操作这些变量。为了存取一个共享的变量,一个线程通常先获取锁定并清除他的工作内存区,把这些共享变量从所有线程的共享内存区中正确的装入到自己所在的工作内存区中,当线程解锁时保证该工作内存区变量的值写回到共享内存中。

  一个线程可以执行的操作有使用(use)、赋值(assign)、装载(load)、存储(store)、锁定(lock)、解锁(unlock)

  而主内存可以执行的操作有读(read)、写(write)、锁定(lock)、解锁(unlock),每个操作都是原子的。

  volatile的作用是强制线程到主内存(共享内存)里去读取变量,而不去线程工作内存区里去读取,从而实现了多个线程间的变量可见。也就是满足线程安全的可见性也就是只具备可见性,不具备原子性。

 

对于原子性的验证

代码:

 

 1 package com.java.day01_volatile;
 2 
 3 public class VolatileNoAutomic extends Thread{
 4     private volatile static int count;
 5     
 6     private static void addCount(){
 7         for (int i = 0; i < 1000; i++) {
 8             count++;
 9         }
10         System.out.println(Thread.currentThread().getName()+"   count="+count);
11     }
12     
13     
14     public void run(){
15         addCount();
16     }
17     
18     
19     public static void main(String[] args) {
20         VolatileNoAutomic[] av = new VolatileNoAutomic[10]; 
21         for (int i = 0; i < av.length; i++) {
22             av[i]=new VolatileNoAutomic();
23         }
24         
25         for (int i = 0; i < av.length; i++) {
26             av[i].start();
27         }
28         
29         
30     }
31     
32 }

运行结果

 

 1 Thread-0   count=1000
 2 Thread-1   count=2000
 3 Thread-3   count=3222
 4 Thread-2   count=3222
 5 Thread-5   count=4915
 6 Thread-4   count=4915
 7 Thread-6   count=7915
 8 Thread-7   count=6915
 9 Thread-8   count=5915
10 Thread-9   count=8915

 

运行结果:预期的运行结果应该加到一万,然而现在只加到8915,原因为count++为三步:首先取得count的值,然后对该值加1,最后将加1后的值赋值给count

代码2:使用具有原子操作的atomicInteger

 1 package com.java.day01_volatile;
 2 
 3 import java.util.concurrent.atomic.AtomicInteger;
 4 
 5 public class VolatileNoAutomic extends Thread{
 6 //    private volatile static int count;
 7     //可以用来保证原子性,本身就支持多线程并发的原子操作
 8     private static AtomicInteger count = new AtomicInteger(0);
 9     private static void addCount(){
10         for (int i = 0; i < 1000; i++) {
11 //            count++;
12             count.incrementAndGet();
13         }
14         System.out.println(Thread.currentThread().getName()+"   count="+count);
15     }
16     
17     
18     public void run(){
19         addCount();
20     }
21     
22     
23     public static void main(String[] args) {
24         VolatileNoAutomic[] av = new VolatileNoAutomic[10]; 
25         for (int i = 0; i < av.length; i++) {
26             av[i]=new VolatileNoAutomic();
27         }
28         
29         for (int i = 0; i < av.length; i++) {
30             av[i].start();
31         }
32         
33         
34     }
35     
36 }

运行结果:正确

 1 Thread-1   count=2590
 2 Thread-3   count=3076
 3 Thread-0   count=2590
 4 Thread-2   count=4322
 5 Thread-4   count=5238
 6 Thread-5   count=6365
 7 Thread-6   count=7222
 8 Thread-7   count=8000
 9 Thread-8   count=10000
10 Thread-9   count=10000

扩展:CAS,非阻塞算法

若有多个atomicInteger,则要保证其原子性需要使用synchronized关键字

代码1:不加synchronized关键字

 1 package com.java.day01_volatile;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 import java.util.concurrent.atomic.AtomicInteger;
 6 
 7 public class AtomicUse {
 8 
 9     private static AtomicInteger count  = new AtomicInteger(0);
10     
11     public int method(){
12         
13         try {
14             Thread.sleep(100);
15         } catch (InterruptedException e) {
16             e.printStackTrace();
17         }
18         
19         count.addAndGet(1);
20         count.addAndGet(2);
21         count.addAndGet(3);
22         count.addAndGet(4);
23         
24         return count.get();
25         
26     }
27     
28     
29     public static void main(String[] args) {
30         final AtomicUse a = new AtomicUse();
31         
32         List<Thread>ts = new ArrayList<Thread>();
33         
34         for (int i = 0; i < 100; i++) {
35             ts.add(new Thread(new Runnable() {
36                 public void run() {
37                     System.out.println(a.method());
38                 }
39             }));
40         }
41         
42         
43         for (Thread thread : ts) {
44             thread.start();
45         }
46         
47     }
48     
49 }

运行结果:原本结果的运行位每次加10,原子性操作,现在不能保证每次加10

技术分享

代码2:加synchronized关键字

 1 package com.java.day01_volatile;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 import java.util.concurrent.atomic.AtomicInteger;
 6 
 7 public class AtomicUse {
 8 
 9     private static AtomicInteger count  = new AtomicInteger(0);
10     
11     public synchronized int method(){
12         
13         try {
14             Thread.sleep(100);
15         } catch (InterruptedException e) {
16             e.printStackTrace();
17         }
18         
19         count.addAndGet(1);
20         count.addAndGet(2);
21         count.addAndGet(3);
22         count.addAndGet(4);
23         
24         return count.get();
25         
26     }
27     
28     
29     public static void main(String[] args) {
30         final AtomicUse a = new AtomicUse();
31         
32         List<Thread>ts = new ArrayList<Thread>();
33         
34         for (int i = 0; i < 100; i++) {
35             ts.add(new Thread(new Runnable() {
36                 public void run() {
37                     System.out.println(a.method());
38                 }
39             }));
40         }
41         
42         
43         for (Thread thread : ts) {
44             thread.start();
45         }
46         
47     }
48     
49 }

运行结果:为正序

技术分享

volatile关键字的非原子性

volatile关键字虽然拥有多个线程之间的可见性,但是却不具备同步性(也就是原子性),可以算上是一个轻量级的synchronized,性能要比synchronized强很多,不会造成阻塞(在很多开源的架构里,比如netty的底层代码就大量使用volatile,可见netty性能一定是非常不错的。)这里需要注意:一般volatile用于只针对多个线程可见的变量操作,并不能代替synchronized的同步功能。

volatile关键字只具有可见性,没有原子性。要实现原子性一般建议使用atomic类的系类对象,支持原子性操作(注意atomic类只保证本方法原子性,并不能保证多次操作的原子性)

扩展:目前最主流的两个框架为netty和mina

 

 

 



 

线程(-)volatile