JAVA进阶 THREAD学习07 volatile关键字

volatile具有内存可见性

来看下面这段代码:

import java.util.Scanner;

public class JAVA_THREAD06 {
    static class Counter {
        public int flag = 0;
    }

    public static void main(String[] args) {
        Counter counter = new Counter();
        Thread t1 = new Thread(() -> {
            while (counter.flag == 0) {
                // do nothing
            }
            System.out.println("循环结束!");
        });
        Thread t2 = new Thread(() -> {
            Scanner scanner = new Scanner(System.in);
            System.out.println("输入一个整数:");
            counter.flag = scanner.nextInt();
        });
        t1.start();
        t2.start();
    }
}

在输入的数值不是0时也不会结束循环,我们来解析一下这个过程。

这里存在两个线程,但由于线程t1和线程t2不在同步状态,所以当t2还没获取输入的数时,t1就已经判断结束了。

接下来我们验证一下这个猜想。将线程开始和结束进行打印来监视线程的运行顺序,将counter.flag进行打印监视它的值。

import java.util.Scanner;

public class JAVA_THREAD06 {
    static class Counter {
        public  int flag = 0;
    }

    public static void main(String[] args) {
        Counter counter = new Counter();
        Thread t1 = new Thread(() -> {            System.out.println("t1 start");
            while (counter.flag == 0) {
                // do nothing
            }
            System.out.println("循环结束!");
            System.out.println("t1 finish");
        });
        Thread t2 = new Thread(() -> {
            System.out.println("t2 start");
            Scanner scanner = new Scanner(System.in);
            System.out.println("输入一个整数:");
            counter.flag = scanner.nextInt();
            System.out.println("t2 finish");
        });
        t1.start();
        t2.start();
        System.out.println(counter.flag);
    }
}

在这里插入图片描述
这里主线程就直接运行,将counter.flag的值给打印出来了,然后两个线程才会运行。在输入数值2后线程t2停止运行,线程t1没有反应,判断要么不能读取到counter.flag的值、要么就是已经读取了并且判断结束(判断结果是true所以会不停循环)。
这里再验证新的猜想,把flag的初始值设为非0.

 static class Counter {
        public  int flag = 2;
    }

在这里插入图片描述
结果显然符合线程t1提前进行判断并结束的猜想。
那么这里线程t1会等不到flag的变化的情况就叫做不可见。即线程t2对于flag的改变t1看不到了。

那么想要让flag数值的修改可见,线程t1能够读取到输入的数值我们应该怎么办呢?这就需要使用我们今天的主角——volatile关键字。

接下来我们对之前的代码进行如下改变:
在两个线程共享的变量前加上volatile关键字

static class Counter {
        public volatile int flag = 0;
    }

运行结果如下:
在这里插入图片描述

volatile不具有原子性

这里就可以谈谈synchronized和volatile的本质区别:synchronized绝对保证原子性,volatile绝对保证可见性。

static class Counter {
        public volatile int flag = 0;
        void increase(){
            this.flag++;
        }
    }

    public static void main(String[] args) throws InterruptedException{
        Counter counter = new Counter();
        Thread t1 = new Thread(() -> {
            for(int i=0;i<10000;i++){
                counter.increase();
            }
        });

        Thread t2 = new Thread(() -> {
            for(int i=0;i<10000;i++){
                counter.increase();
            }
        });
        t1.start();
        t2.start();

        t1.join();
        t2.join();
        System.out.println(counter.flag);
    }

从这里的运行结果可以看出volatile并不保证原子性。

synchronized也具有可见性

用同一个例子来证明

static class Counter {
        public int flag = 0;
    }
    public static void main(String[] args) {
        Counter counter = new Counter();
        Thread t1 = new Thread(() -> {
//            System.out.println("t1 start");
            while (true) {
                synchronized (counter){
                    if(counter.flag!=0)break;
                }
                // do nothing
            }
            System.out.println("循环结束!");
//            System.out.println("t1 finish");
        });
        Thread t2 = new Thread(() -> {
//            System.out.println("t2 start");
            Scanner scanner = new Scanner(System.in);
            System.out.println("输入一个整数:");
            counter.flag = scanner.nextInt();
//            System.out.println("t2 finish");
        });
        t1.start();
        t2.start();
//        System.out.println(counter.flag);
    }

在这里插入图片描述
注意:这里的synchronized是对代码块使用,加锁的对象是已经实例化好了的counter对象。

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值