相关知识点参考:ConcurrentHashMap 可以使用 ReentrantLock 作为锁吗?
线程可以通过加 synchronized 锁或 ReentrantLock 锁两种方式对代码加锁,实现线程安全。他们的区别如下:
共同点:
(1)都保证了可见性和互斥性
(2)都是可重入锁,同一线程可以多次获得同一个锁
(3)都是用来协调多线程对共享对象、变量的访问
不同点:
(1)synchronized 是 Java 关键字,ReentrantLock 是 lock 的实现类。
(2)synchronized 适合锁少量的代码同步问题,Lock 适合锁大量的同步代码
(3)synchronized 在程序发生异常或执行完时会自动释放锁,ReentrantLock 必须要手动释放锁(用 unlock 方法),如果不释放就成了死锁,因此使用 lock 时需要在 finally 块中释放锁。
(4)底层实现不一样,synchronized 是同步阻塞,使用悲观并发策略;ReentrantLock 是同步非阻塞,采用乐观并发策略,所以可以提高多个线程进行读操作的效率。
(5)synchronized 可以把任意一个非 NULL 的对象当做锁,它属于独占式的悲观锁、非公平锁,同时也属于可重入锁。ReentrantLock 继承接口 Lock 并实现了接口中定义的方法,它也是一种可重入锁,同时可以将其设置为公平锁。
(6)synchronized 无法判断获取锁的状态,ReentrantLock 可以判断线程是否获取到了锁,并且可以对获取锁的等待时间进行设置,避免死锁,同时可以获取各种锁的信息以及可以灵活地实现多路通知。
ReentrantLock 公平性的设置:
ReentrantLock fairLock = new ReentrantLock(true),参数为 true 时,表明设置为公平锁,会倾向于将锁赋予等待时间最久的线程。
公平锁:获取锁的顺序按先后调用 lock()方法的顺序(慎用),也就是排队一样。
非公平锁:抢占的顺序不一定,随机的。
注意:一般没有强需求按顺序执行的情况下,采用非公平锁好,因为 Java 的 JVM 设置很少情况下才会出现一个线程等待很久没有被分配到资源的情况。然后采用了公平锁的话,会造成一定的性能消耗,导致吞吐量下降。
synchronized 加锁实现方式:
关于同步方法的总结:
- 同步方法仍然涉及到同步监视器,只是不需要我们显式的声明
- 非静态的同步方法,其同步监视器是:this(当前类的对象)
静态同步方法:同步监视器是:当前类本身(类.class ( ))
class Ticket{ private int num=100;//总共100张票 public synchronized void sell() throws InterruptedException { //此时用synchronized修饰方法sell(),其同步监视器是:this(当前类的对象),由于使用的是 while(num>0){ System.out.println(Thread.currentThread().getName()+"卖票,票号为:"+(num--)); //为了创造一些异常,让线程到此处sleep阻塞一下 TimeUnit.MILLISECONDS.sleep(10); } } }
class Ticket{ private int num=100;//总共100张票 public void sell() throws InterruptedException { synchronized (this){ //此时使用synchronized修饰代码块 while (num > 0) { System.out.println(Thread.currentThread().getName() + "卖票,票号为:" + (num--)); //为了创造一些异常,让线程到此处sleep阻塞一下1 TimeUnit.MILLISECONDS.sleep(10); } } } }
ReentrantLock 加锁实现方式:
public void buy() { lock.lock(); try { while (num==0){ //此时没有商品,需要等待 notNull.await(); } //不为0则可以进行操作 num--; System.out.println(Thread.currentThread().getName()+"消费商品,当前剩余商品:"+num); notNull.signalAll();//通知其他线程可以生产商品 } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } }