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禁止指令重排序也有一些规则:

  1. 当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行;
  2. 在进行指令优化时,不能将在对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 }

运行结果:

 

posted on 2020-11-08 20:59  Katharsis_1  阅读(109)  评论(0)    收藏  举报