【Java多线程】线程同步机制

线程同步是为了确保线程安全,所谓线程安全指的是多个线程对同一资源进行访问时,有可能产生数据不一致问题,导致线程访问的资源并不是安全的。如果多线程程序运行结果和单线程运行的结果是一样的,且相关变量的值与预期值一样,则是线程安全的。

Java中与线程同步有关的关键字/类包括:

volatile、synchronized、Lock、AtomicInteger等concurrent包下的原子类。。。等

接下来讨论这几种同步方法。

volatile

volatile一般用在多个线程访问同一个变量时,对该变量进行唯一性约束,volatile保证了变量的可见性,不能保证原子性。

用法(例):private volatile booleanflag = false

保证变量的可见性:volatile本质是告诉JVM当前变量在线程寄存器(工作内存)中的值是不确定的,需要从主存中读取,每个线程对该变量的修改是可见的,当有线程修改该变量时,会立即同步到主存中,其他线程读取的是修改后的最新值。

不能保证原子性:原子性指的是不会被线程调度机制打断的操作,在java中,对基本数据类型的变量的读取和赋值操作是原子性操作。自增/自减操作不是原子性操作。例如:i++,其实是分成三步来操作的:1)从主存中读取i的值;2)执行+1操作;3)回写i的值。volatile关键字并不能保证原子性操作。非原子操作都会产生线程安全的问题,那么如何实现自增/自减的原子性呢?后续将有讲解。

synchronized

synchronized提供了一种独占的加锁方式,是比较常用的线程同步的关键字,一般在“线程安全的单例”中普遍使用。该关键字能够保证代码块的同步性和方法层面的同步。

用法(例):1)代码块同步

//使用synchronized关键字实现线程安全的单例模式
    private static Singleton instance;
    public static Singleton getInstance(){
        if(instance == null){
            synchronized (Singleton.class)
            {
                if(instance == null){
                    instance = new Singleton();
                }              
            }
        }
        return instance;
}
privateSingleton(){ }

2)方法同步

public static synchronized Singleton getInstance2(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
private Singleton(){ }

很多人说synchronized在性能上存在较大问题,但并没有真实环境产生的数据比较说明,因此在这里不好讨论性能问题。

volatile和synchronized的区别

1)      volatile通过变量的可见性,指定线程必须从主存中读取变量的最新值;synchronized通过阻塞线程的方式,只有当前线程能访问该变量,锁定了当前变量。

2)      volatile使用在变量级别;synchronized可以使用在变量、方法、类级别

3)      volatile不会造成线程阻塞;synchronized可能会造成线程阻塞

4)      volatile不能保证原子性;synchronized能保证原子性

5)      volatile标记的变量不会被编译器优化;synchronized标记的变量有可能会被编译器优化(指令重排)。

如何保证自增/自减的原子性

1)      使用java.util.concurrent包下提供的原子类,如AtomicInteger、AtomicLong、AtomicReference等。

用法:   

AtomicInteger atomicInteger = new AtomicInteger();
atomicInteger.getAndIncrement();//实现原子自增
atomicInteger.getAndDecrement();//实现原子自减

2)使用synchronized同步代码块

Synchronized(this){
      value++;
}

3)使用Lock显示锁同步代码块

 private Lock lock = new ReentrantLock();
    private final int incrementAndGet(){
        lock.lock();
        try
        {
            return value++;
        }
        finally
        {
            // TODO: handle finally clause
            lock.unlock();
        }
    }



评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值