关于Java并发中的volatile与synchronized关键字

synchronized关键字

在介绍之前,首先从场景出发,思考一下下面的场景:

 public List<String> list = new ArrayList<>();
    public void dealList(List<String> list){
        /*对list进行增删处理*/
        /*list.add();
        list.remove();*/
    }

如果上述代码是对list的声明与处理过程中,如果在对list进行处理的过程中其他进程也对这个list进行了处理,同时对一个list进行操作,这个过程无法保证list处理的顺序,那么难免会因为处理顺序的不可控性造成处理结果的不可控,对于程序员来讲,最可怕的莫过于不知道结果是什么,这样的问题肯定需要避免,那么如何进行避免呢,这个问题可以转化为如何保证list这个变量操作的顺序,最简单的方法就是一个进程一个进程的执行,那么要实现同一时间只有一个进程访问,那么就需要使用锁,将共享资源在被操作时锁住,这样其他进程就无法进行操作,待处理完成后再释放掉,供其他进程使用。
如下:

public void method() {
        this.intrinsicdock.lock();
        try{
            //处理过程方法
        }finally {
            this.intrinsicdock.unlock();
        }
    }

使用lock和unlock对资源intrinsicdock进行锁定,这样上锁的过程在使用时并不需要这样去实现,使用synchronized关键字即可实现对method方法的锁定,即上述代码等价于:

public synchronized void method() {
        //处理过程方法
    }

使用synchronized关键字,那么对象的锁将会保护整个方法。
下面再提一个问题:下述代码是否是线程安全的?

public class ListHelp<E> {
    public List<E> list = Collections.synchronizedList(new ArrayList<E>());
    public synchronized boolean putIfAbsent(E x) { // 这个方法并不是线程安全的,因为当前只获得了ListHelp的锁,并没有获得list的锁,没有办法保证当前操作的原子性
        boolean absent = !list.contains(x);
        if (absent) {
            list.add(x);
        }
        return absent;
    }
}`

如备注中说明的,此处的synchronized关键字只对putIfAbsent有效,但是list并非是私有的,意味着list可以被其他进程进行修改,从而导致absent失效,所以不是线程安全的。如果要保证线程安全,应该如何更改呢?

public class ListHelp<E> {
    public List<E> list = Collections.synchronizedList(new ArrayList<E>());
    // 方法2
    public synchronized boolean putIfAbsentSafe(E x) { // 这个方法是线程安全的
        synchronized(list ) {
            boolean absent = !list.contains(x);
            if (absent) {
                list.add(x);
            }
            return absent;
        }
    }
}

如上,使用synchronized一并锁住list,这样list就没办法在执行putIfAbsentSafe被其他进程进行访问修改了。

volatile关键字

首先看一下英文中volatile关键字的含义:
在这里插入图片描述
Java中volatile关键字的作用是:使用volatile修饰的变量会强制将修改的值立即写入主存,又因为主存中值的更新会使缓存中的值失效,所以使用volatile修饰的变量的修改可以做到一致性(非volatile变量不具备这样的特性,非volatile变量的值会被缓存,线程A更新了这个值,线程B读取这个变量的值时可能读到的并不是是线程A更新后的值,无法保证该变量的一致性)。volatile会禁止指令重排,这保证了有序性的特点。
volatile的特点:
volatile具有可见性、有序性,不具备原子性的特点(不具备原子性是volatile与synchronized、java.util.concurrent.locks.Lock最大的功能差异)
原子性:这个简而言之就是一组操作,要么全部执行,要么全不执行。
可见性:是指一个进程对变量的修改,在另一个进程当中可以读取到该变量修改后的值。
有序性:即程序执行时会按照代码书写的先后顺序执行。由于在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。volatile关键字禁止了指令重排,所以可以保证执行的顺序性。
在这里插入图片描述
如上图,done作为公共资源,虽然使用synchronized标记了对done进行操作的两个线程,一个设置值,一个取值,但是取值的线程却有可能取出缓存的数据,所以将done声明为volatile是合理的,
在这里插入图片描述
使用volatile声明的done元素可以保证取值线程可以获取实时的变量值。
相较于synchronized,volatile不会让线程阻塞,响应速度比synchronized高,这是它的优点。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值