架构师养成记--4.volatile关键字
volatile修饰的变量可在多个线程间可见。
如下代码,在子线程运行期间主线程修改属性值并不对子线程产生影响,原因是子线程有自己独立的内存空间,其中有主内存中的变量副本。
1 public class RunThread extends Thread{ 2 3 private volatile boolean isRunning = true; 4 private void setRunning(boolean isRunning){ 5 this.isRunning = isRunning; 6 } 7 8 public void run(){ 9 System.out.println("进入run方法.."); 10 int i = 0; 11 while(isRunning == true){ 12 //.. 13 } 14 System.out.println("线程停止"); 15 } 16 17 public static void main(String[] args) throws InterruptedException { 18 RunThread rt = new RunThread(); 19 rt.start(); 20 Thread.sleep(1000); 21 rt.setRunning(false); 22 System.out.println("isRunning的值已经被设置了false"); 23 } 24 25 26 }
那么如何达到预期效果让子线程停止呢?那就是变量isRunning使用volatile修饰,原理如下图
需要注意的一点是,volatile关键字修饰的变量只有可见性,没有原子性。如下代码,如果使用第九行代码的话最终打印结果不是10000
1 import java.util.concurrent.atomic.AtomicInteger; 2 3 /** 4 * volatile关键字不具备synchronized关键字的原子性(同步) 5 * @author alienware 6 * 7 */ 8 public class VolatileNoAtomic extends Thread{ 9 //private static volatile int count; 10 private static AtomicInteger count = new AtomicInteger(0); 11 private static void addCount(){ 12 for (int i = 0; i < 1000; i++) { 13 //count++ ; 14 count.incrementAndGet(); 15 } 16 System.out.println(count); 17 } 18 19 public void run(){ 20 addCount(); 21 } 22 23 public static void main(String[] args) { 24 25 VolatileNoAtomic[] arr = new VolatileNoAtomic[100]; 26 for (int i = 0; i < 10; i++) { 27 arr[i] = new VolatileNoAtomic(); 28 } 29 30 for (int i = 0; i < 10; i++) { 31 arr[i].start(); 32 } 33 } 34 35 36 37 38 }
那么如何才能有原子性呢?可以对对象进行AtimicInteger 修饰
多个addAndGet在一个方法内是非原子性的,需要加synchronized进行修饰,保证4个addAndGet整体原子性
1 import java.util.ArrayList; 2 import java.util.List; 3 import java.util.concurrent.atomic.AtomicInteger; 4 5 public class AtomicUse { 6 7 private static AtomicInteger count = new AtomicInteger(0); 8 9 //多个addAndGet在一个方法内是非原子性的,需要加synchronized进行修饰,保证4个addAndGet整体原子性 10 /**synchronized*/ 11 public synchronized int multiAdd(){ 12 try { 13 Thread.sleep(100); 14 } catch (InterruptedException e) { 15 e.printStackTrace(); 16 } 17 count.addAndGet(1); 18 count.addAndGet(2); 19 count.addAndGet(3); 20 count.addAndGet(4); //+10 21 return count.get(); 22 } 23 24 25 public static void main(String[] args) { 26 27 final AtomicUse au = new AtomicUse(); 28 29 List<Thread> ts = new ArrayList<Thread>(); 30 for (int i = 0; i < 100; i++) { 31 ts.add(new Thread(new Runnable() { 32 @Override 33 public void run() { 34 System.out.println(au.multiAdd()); 35 } 36 })); 37 } 38 39 for(Thread t : ts){ 40 t.start(); 41 } 42 43 44 45 46 47 48 } 49 }