1、volatile关键字有什么作用?
1)保证可见性,不保证原子性。
可见性:当某个线程修改volatile变量时,JMM会强制将这个修改更新到主内存中,并且让 其他线程工作内存中存储的副本失效。
volatile变量并不能保证其操作的原子性,具体来说像i++这种操作并不是原子操作,使用volatile修饰变量后仍然不能保证这一点。
1 public class Test { 2 private static volatile int count = 0; 3 4 public static void main(String[] args){ 5 Thread[] threads = new Thread[5]; 6 for(int i = 0; i<5; i++){ 7 threads[i] = new Thread(()->{ 8 try{ 9 for(int j = 0; j<10; j++){ 10 System.out.println(++count); 11 Thread.sleep(500); 12 } 13 }catch (Exception e){ 14 e.printStackTrace(); 15 } 16 }); 17 threads[i].start(); 18 } 19 } 20 }

原因:count++操作并不是原子操作。
1 Code: 2 0: getstatic //读取原数据 3 3: iconst_1 //定义常量1 4 4: iadd //计算数据 5 5: putstatic //输出结果 // Field count:I 6 8: return
2)禁止指令重排
指令重排是指JVM在编译Java代码的时候,或者CPU在执行JVM字节码的时候,对现有的指令顺序进行重新排序
指令重排的目的是为了在不改变程序执行结果的前提下,优化程序的运行效率。需要注意的是,这里所说的不改变执行结果,指的是不改变单线程下的程序执行结果。
重排序操作不会对存在数据依赖关系的操作进行重排序。比如:a=1;b=a; 这个指令序列,由于第二个操作依赖于第一个操作,所以在编译时和处理器运时这两个操作不会被重排序。
重排序是为了优化性能,但是不管怎么重排序,单线程下程序的执行结果不能被改变。比如:a=1;b=2;c=a+b这三个操作,第一步(a=1)和第二步(b=2)由于不存在数据依赖关系, 所以可能会发生重排序,但是c=a+b这个操作是不会被重排序的,因为需要保证最终的结果一定是c=a+b=3。
然而,指令重排是一把双刃剑,重排序在单线程下一定能保证结果的正确性,但是在多线程环境下,可能发生重排序,影响结果,下例中的1和2由于不存在数据依赖关系,则有可能会被重排序,先执行status=true再执行a=2。而此时线程B会顺利到达4处,而线程A中a=2这个操作还未被执行,所以b=a+1的结果也有可能依然等于2。
public class TestVolatile { int a = 1; boolean status = false; /** * 状态切换为true */ public void changeStatus() { a = 2; status = true; } /** * 若状态为true,则running */ public void run() { if (status) { int b = a + 1; System.out.println(b); } } }
使用volatile关键字修饰共享变量便可以禁止这种重排序。若用volatile修饰共享变量,在编译时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序,volatile禁止指令重排序也有一些规则:
- 当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行;
- 在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。
通俗的说就是执行到volatile变量时,其前面的所有语句都执行完,后面所有语句都未执行。且前面语句的结果对volatile变量及其后面语句可见。
2、编写Java程序模拟烧水泡茶最优工序。
1 package MakeTea; 2 3 public class MakeTea extends Thread{ 4 private int time; 5 private String name; 6 MakeTea(int time,String name){ 7 this.time=time; 8 this.name=name; 9 } 10 11 public String getname(){ 12 return this.name; 13 } 14 public int getTime(){ 15 return time; 16 } 17 public void run(){ 18 this.setName(getname()); 19 int count=0; 20 while (count<=getTime()) { 21 System.out.println(this.getname() + "线程工作中,已进行了" +count+"分钟"); 22 count++; 23 } 24 System.out.println(this.getName() +"工作已经完成!"); 25 } 26 } 27 28 *************************************************** 29 package MakeTea; 30 31 public class TestMakeTea { 32 public static void main(String[] args) { 33 MakeTea WashPot = new MakeTea(1,"WashPot"); 34 MakeTea BoilWater = new MakeTea(15,"BoilWater"); 35 MakeTea WashTeaPot = new MakeTea(1,"WashTeaPot"); 36 MakeTea WashGlass = new MakeTea(2,"WashGlass"); 37 MakeTea GetTea = new MakeTea(1,"GetTea"); 38 39 WashPot.start(); 40 try { 41 WashPot.join(); //让WashPot线程一直进行直至结束; 42 }catch (InterruptedException it){} 43 44 BoilWater.start(); 45 /* 46 try{ 47 BoilWater.sleep(500); 48 }catch (InterruptedException it){} 49 */ 50 //烧水过程中可以同时进行洗茶壶 51 WashTeaPot.start(); 52 try{ 53 WashTeaPot.join(); 54 }catch (InterruptedException it){} 55 56 //洗完茶壶洗茶杯 57 WashGlass.start(); 58 try { 59 WashGlass.join(); 60 }catch (InterruptedException it){} 61 62 //洗完茶杯去拿茶叶 63 GetTea.start(); 64 try{ 65 GetTea.join(); 66 }catch (InterruptedException it){} 67 68 try{ 69 BoilWater.join(); 70 }catch (InterruptedException it){} 71 System.out.println("a good cup of tea is done!"); 72 } 73 }
运行结果:

浙公网安备 33010602011771号