(1)synchronized是独占锁,加锁和解锁的过程自动进行,易于操作,但不够灵活。ReentrantLock也是独占锁,加锁和解锁的过程需要手动进行,不易操作,但非常灵活。
(2)synchronized可重入,因为加锁和解锁自动进行,不必担心最后是否释放锁;ReentrantLock也可重入,但加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。
(3)synchronized不可响应中断,一个线程获取不到锁就一直等着;ReentrantLock可以相应中断。
ReentrantLock公平锁,等待时间最长的线程先运行
首先做一个简易的售票功能
如下使用了reentrantlock公平锁,结果为
public class T1 { static int num = 23; static ReentrantLock lock = new ReentrantLock(true); public static void main(String[] args) { new Thread(T1::tt, "文化路").start(); new Thread(T1::tt, "瑞达路").start(); new Thread(T1::tt, "红旗路").start(); } static void tt() { String na = Thread.currentThread().getName(); while (num > 1) { lock.lock(); num--; System.out.printf("%s:%d%n", na, num); if (num < 1) { break; } lock.unlock(); } } }
reentrantlock可以打断线程
例如一个线程睡眠时间太长,导致其他线程阻塞,则可以打断前一个进程。
public class T4 { static ReentrantLock lock = new ReentrantLock(); public static void main(String[] args) { Thread a = new Thread(T4::add, "T1"); a.start(); a.interrupt(); new Thread(() -> { try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } try { lock.lockInterruptibly(); TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程二"); lock.unlock(); }).start(); } static void add() { try { lock.lock(); TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("11"); lock.unlock(); } }
lockInterruptibly()获取锁,除非当前线程被中断。如果锁没有被其他线程持有,则获取锁并立即返回,将锁持有计数设置为1。如果当前线程已经持有这个锁,那么持有计数加1,该方法立即返回。如果锁被另一个线程持有,那么当前线程会因为线程调度的目的而被禁用,并处于休眠状态,直到发生以下两种情况之一:锁被当前线程获取;或其他线程中断当前线程。如果当前线程获得了锁,那么锁保持计数被设置为1如果当前线程:在进入此方法时设置其中断状态;或在获取锁时被中断然后,InterruptedException被抛出,当前线程的interruptec状态被清除。在此实现中,由于此方法是显式的中断点,因此优先于响应中断,而不是正常或可重入获取锁。