Loading...
墨滴

。残颜

2021/04/21  阅读:46  主题:默认主题

既生瑜何生亮

今天简单聊聊Java中两个同步锁:synchronized 和ReentrantLock.

java同学肯定对synchronized这个关键字非常熟悉,通常会用在处理同步问题中。

首先,我们看一下synchronized常见的使用方式:

  • 修饰实例方法
  • 修饰类的静态方法
  • 修饰代码块

使用方式

下面我们分别看一下三种方法的简单实现代码

  1. 修饰实例方法
public class test {
    private int sum = 0;
    public synchronized void addNum() {
        sum = sum + 1;
    }
}

修饰实例方法时,锁对象为当前类的实例,不同的实例对象不会产生互斥效果。

  1. 修改类的静态方法
public class test {
    private static int num1 = 0;
    
    public static synchronized void addSum(){
        num1 = num1 + 1;
    }
}

修改静态方法时,锁对象为当前类Class对象,即使不用线程中的不同的实例也会产生互斥效果。

  1. 修饰代码块
public class test {
    private Object lock = new Object();
    public void addSum(){
        synchronized (lock){
            for (int i = 0; i < 10; i++) {
                System.out.println("测试:"+i);
            }
        }
    }
}

修饰代码块时,锁对象为synchronized后面括号中的对象,任何对象都可以作为锁对象。

接下来,我们看一下ReentrantLock的使用方式

public class Test {
    ReentrantLock lock = new ReentrantLock();
    
    public static void main(String[] args) {
        Test tt = new Test();

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                tt.add();
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                tt.add();
            }
        });
        
        t1.start();
        t2.start();
    }
    
    public void add(){
        try{
            lock.lock();
            for (int i = 0; i <10; i++) {
                System.out.println("测试"+i);
            }
        }catch (Exception e){}
        finally {
            lock.unlock();
        }
    }
}

通过以上代码我们可以发现,不同于synchronized,ReentrantLock需要开发手动在代码中加锁和解锁。

在上述代码中,在finally方法中调用解锁unlock方法,是因为当异常发生时synchronized会自动的释放锁,但是ReentrantLokc并不会自动释放锁,所以放在finall代码块中,保证任何情况下锁都能被正常释放掉。

公平锁与非公平锁

公平锁:通过同步队列来实现多个线程按照申请锁的顺序获取锁。

默认情况下,synchronized和ReentrantLock都是非公平锁。但是ReentrantLock可以通过构造函数传参true,来创建公平锁,可以通过以下的部分源码。

两者的不同

1.锁的实现

synchronized是JVM虚拟机的实现, 而ReentrantLock是JDK实现的,是java.util.concurrent包中的锁。

2.性能

新版本java对synchronized进行了很多优化,引入许多概念,比如偏向锁,轻量锁,自旋锁等, 两者性能大致相同。

3.公平锁

上面文章已经提到了,此处省略。

4. 选择

ReentrantLock并不是所有的JDK版本都支持,除非需要使用ReentrantLock的高级功能,否则优先选择使用synchronized。因为这是JVM内置实现的一种锁机制,原生支持,不用担心没有释放锁导致的死锁问题,因为JVM会确保锁的释放。

单例的双重校验锁实现(面试必备)

public class Singleton {
    private Singleton() {}
    // 使用volatile修饰变量可以禁止JVM的指令重排,保证多线程下获取实例的唯一。
    private volatile static Singleton instance;
    
    public static Singleton getInstance(){
        if(instance == null){
            synchronized (Singleton.class){
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

synchronized底层实现原理

synchronized是采用对象内部监视器(ObjectMonitor)的同步机制,是基于JVM对操作系统底层的Mutex Lock(互斥锁)实现的,期间会由用户态转入操作系统内核态。synchronized实现锁,是一个重量锁,当多个线程切换上下文时,是非常重量级的操作,成本非常高。

。残颜

2021/04/21  阅读:46  主题:默认主题

作者介绍

。残颜