synchronized和ReentrantLock

synchronized

synchronized 是一个 JVM 提供实现的内部锁机制。一个 synchronized 块包括一个锁对象引用和代码块。
synchronized 作用在方法上时,其锁对象是方法所在对象本身。

使用 synchronized 时至多只有一个线程可以获得锁。当线程进入 synchronized 代码块时自动获取锁,离开代码块时自动释放锁。

synchronized 同时也是一个支持可重入的互斥锁,同一个线程可以多次进入同步块。

public class WaitNotify {
    static boolean flag = true;
    static Object lock = new Object();

    public static void main(String args[]) throws InterruptedException {
        Thread waitThread = new Thread(new Wait(), "WaitThread");
        waitThread.start();
        TimeUnit.SECONDS.sleep(1);
        Thread notifyThread = new Thread(new Notify(), "NotifyThread");
        notifyThread.start();
    }

    static class Wait implements Runnable {
        @Override
        public void run() {
            synchronized (lock) { //获取锁
                while (flag) {
                    try {
                        System.out.println(Thread.currentThread() + " flag is true. wait @"
                                + new SimpleDateFormat("HH:mm:ss").format(new Date()));

                        //等待, 并释放锁
                        lock.wait();
                        //重新获得锁之后从 wait() 返回
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread() + " flag is false. running @ " +
                        new SimpleDateFormat("HH:mm:ss").format(new Date()));
            }
        }
    }

    static class Notify implements Runnable {
        @Override
        public void run() {
            synchronized (lock) {
                System.out.println(Thread.currentThread() + " hold lock. notify @ "
                    + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                //通知等待的线程
                lock.notifyAll();
                flag= false;
                try {
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //直到同步块结束才释放锁给 wait() 线程
            }
            synchronized (lock) {
                System.out.println(Thread.currentThread() + " hold lock again. notify @ "
                        + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            }
        }
    }
}

ReentrantLock

ReentrantLock 也是一个可重入的互斥锁。 它是一个J.U.C包中实现了 Lock 接口的类。Lock 接口定义了下面一些方法:

void        lock(); //获取锁
void        lockInterruptibly(); //可被中断获取锁
Condition   newCondition(); //返回一个绑定在此锁上的 condition 实例
boolean     tryLock(); //尝试获取锁,立即返回结果
boolean     tryLock(long time, TimeUnit unit); // 定时获取锁,不可被中断
void        unlock(); //释放锁

ReentrantLock 对比内部锁首先最明显的一个区别就是 ReentrantLock 锁的获取和释放都需要显示地手动调用方法。
获取锁时调用 lock()tryLock() ,释放锁时调用 unlock() .
ReentrantLock 锁的实现是通过和继承自 AQS 的同步器组合使用实现的,而非JVM实现。
ReentrantLock 对锁的控制程度比 synchronized 要高,更灵活,在获取锁时可以中断线程,或者设置最大等待时间等等。

public class LockTest {
    private Lock lock = new ReentrantLock();

    public void do1() throws InterruptedException {
        try {
            lock.lock();
            //do something...
        } finally {
            lock.unlock();
        }
    }

    public void do2() {
        try{        
            lock.lock();
            //do something...
        } finally {
            lock.unlock();
        }
    }
}

小结

通过一个表格来对比一下两者区别

项目 synchronized ReentrantLock
锁实现层次 java关键字,JVM实现 J.U.C下面的一个类,结合 AQS 实现的锁机制
锁获取 进入代码块自动获取 手动调用方法lock()tryLock()获取
锁释放 离开代码块自动释放 手动调用方法 unlock() 释放
锁类型 可重入,不可中断、非公平 可重入、可中断、公平或非公平可控
等待队列 只等有一个等待队列 通过组合 Condition 实现多个等待队列

参考:

https://blog.csdn.net/u012403290/article/details/64910926?locationNum=11&fps=1

你可能感兴趣的:(Java)