1.相比synchronized,ReentrantLock(重入锁)增加了一些高级功能
等待可中断——对于synchronized,如果一个线程正在等待锁,那么结果只有两种情况,要么获得这把锁继续执行 ,要么就保持等待。而使用ReentrantLock,如果一个线程正在等待锁,那么它依然可以收到通知,被告知无需再等待,可以停止工作了。
1)lockInterruptiby()的使用:
线程t1一直占用者锁 不释放,此时t2一直在等待着锁,通过lockInterruptibly()方法可以通知线程t2停止等待,而使用synchronized方式,只能继续等待。注意:这里说的是一个线程正在等待锁即没有获得锁的情况,而不是说获得锁后执行出行异常,这两者不一样,因为synchronized同步的代码出现异常,也会释放锁,例如在synchronized同步代码中进入sleep(),此时通过中断,也会释放锁。
import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; public class ReentrantLock4 { public static void main(String[] args) { ReentrantLock lock = new ReentrantLock(); Thread t1 = new Thread(()->{ try { lock.lock(); System.out.println("t1 start"); TimeUnit.SECONDS.sleep(Integer.MAX_VALUE); System.out.println("t1 end"); } catch (InterruptedException e) { System.out.println("interrupted!"); } finally { lock.unlock(); } }); t1.start(); Thread t2 = new Thread(()->{ try { lock.lockInterruptibly(); //可以对interrupt()方法做出响应 System.out.println("t2 start"); TimeUnit.SECONDS.sleep(5); System.out.println("t2 end"); } catch (InterruptedException e) { System.out.println("interrupted!"); //在这里处理退出中断后的后续动作 } finally { if(lock.isHeldByCurrentThread()) { lock.unlock(); } } }); t2.start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } t2.interrupt(); //打断线程2的等待 } }
t1 start interrupted!
2)使用tryLock进行尝试锁定,不管锁定与否,方法都将继续执行,可以根据tryLock的返回值来判定是否锁定
也可以指定tryLock的时间,犹如tryLock(time)抛出异常,所以要注意unlock的处理,必须方法finally中。
代码示例:
class MyService { public ReentrantLock lock = new ReentrantLock(); public void waitMethod() { if (lock.tryLock()) { //如果获取到了锁,打印"获得锁",否则打印"没有获得锁" System.out.println(Thread.currentThread().getName() + "获得锁"); } else { System.out.println(Thread.currentThread().getName() + "没有获得锁"); } } } public class HighConcurrency { public static void main(String[] args) throws InterruptedException { final MyService service = new MyService(); Runnable runnableRef = new Runnable() { @Override public void run() { service.waitMethod(); } }; Thread threadA = new Thread(runnableRef); threadA.setName("A"); threadA.start(); Thread threadB = new Thread(runnableRef); threadB.setName("B"); threadB.start(); } }
B没有获得锁 A获得锁
tryLock(time)示例:
class MyService { public ReentrantLock lock = new ReentrantLock(); public void waitMethod() { try { if (lock.tryLock(3, TimeUnit.SECONDS)) { //等待3s后,如果没有获取锁,则执行else里语句 System.out.println(" " + Thread.currentThread().getName() + "获得锁的时间:" + System.currentTimeMillis()); Thread.sleep(10000); } else { System.out.println(" " + Thread.currentThread().getName() + "没有获得锁"); } } catch (InterruptedException e) { e.printStackTrace(); } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); } } } } public class HighConcurrency { public static void main(String[] args) throws InterruptedException { final MyService service = new MyService(); Runnable runnableRef = new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "调用waitMethod时间:" + System.currentTimeMillis()); service.waitMethod(); } }; Thread threadA = new Thread(runnableRef); threadA.setName("A"); threadA.start(); Thread threadB = new Thread(runnableRef); threadB.setName("B"); threadB.start(); } }
A调用waitMethod时间:1526127191925 B调用waitMethod时间:1526127191925 A获得锁的时间:1526127191926 B没有获得锁说明A先获取锁,B等待3S后没有获取锁,就进入else语句执行,不再等待。
2.可实现公平锁:公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁,而非公平锁不保证这一点,在锁被释放时,任何一个等待锁的线程都有机会获得锁,synchronized中的锁是非公平的,ReenrantLock默认情况下也是非公平的,但是可以在构造函数中设置为公平锁。ReentrantLock(boolean fair)
。
公平锁示例:
public class ReentrantLock5 extends Thread { private static ReentrantLock lock=new ReentrantLock(true); //参数为true表示为公平锁 public void run() { for(int i=0; i<100; i++) { lock.lock(); try{ System.out.println(Thread.currentThread().getName()+"获得锁"); }finally{ lock.unlock(); } } } public static void main(String[] args) { ReentrantLock5 rl=new ReentrantLock5(); Thread th1=new Thread(rl); Thread th2=new Thread(rl); th1.start(); th2.start(); } }输出:
Thread-1获得锁 Thread-2获得锁 Thread-1获得锁 Thread-2获得锁 Thread-1获得锁如果将构造函数中的true去掉,此时成为非公平锁后的打印为:
Thread-1获得锁 Thread-1获得锁 Thread-1获得锁 Thread-1获得锁
3.锁可以绑定多个条件
一个ReentrantLock对象可以同时绑定多个Condition(即对象监视器)实例,线程对象可以注册在指定的Condition中,从而可以有选择性地进行线程通知。
自己对condtion的理解:condition使得调用该方法的线程进入等待队列,但是唤醒的时候唤醒的是对方的等待队列。用一个例子来说明,有一个容器,有多个生产者和多个消费者,希望当容器满的时候,能够只让生产的线程等待,只唤醒消费的线程,同样当容器空的时候,让消费的线程等待,只唤醒生产的线程。
代码实现:
public class MyContainer2<T> { final private LinkedList<T> lists = new LinkedList<>(); final private int MAX = 10; //最多10个元素 private int count = 0; private Lock lock = new ReentrantLock(); private Condition producer = lock.newCondition(); private Condition consumer = lock.newCondition(); public void put(T t) { try { lock.lock(); while(lists.size() == MAX) { producer.await(); //当消费者线程们执行到这里的时候等待 } lists.add(t); ++count; System.out.println("里面有个数:"+count); consumer.signalAll(); //通知消费者线程进行消费 } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public T get() { T t = null; try { lock.lock(); while(lists.size() == 0) { consumer.await(); //当生产者线程执行到这里的等待 } t = lists.removeFirst(); count --; System.out.println("里面有个数:"+count); producer.signalAll(); //通知生产者的线程们进行生产 } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } return t; } public static void main(String[] args) { MyContainer2<String> c = new MyContainer2<>(); //启动消费者线程 for(int i=0; i<10; i++) { new Thread(()->{ for(int j=0; j<5; j++) c.get(); //System.out.println(c.get()); }, "c" + i).start(); } try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } //启动生产者线程 for(int i=0; i<2; i++) { new Thread(()->{ for(int j=0; j<25; j++) c.put(Thread.currentThread().getName() + " " + j); }, "p" + i).start(); } } }
说明:该代码有一个注意点,在put方法中判断容器是否满的时候使用的是while(lists.size()==MAX),这里能否改成if(lists.size==MAX)呢?这里假设这样一种情形,此时容器已经满了,两个消费者线程都进入await()等待,此时一个消费者线程消费了一个商品,同时唤醒了这两个生产者线程c1和c2,此时被唤醒c1先抢到锁,直接执行wait()后面的语句,生产了一个商品,然后c2抢到锁,此时c2直接从wait()后面开始执行,并不会判断容器是否已经满了,所以导致容器装不下,导致错误。
https://blog.csdn.net/chenchaofuck1/article/details/51045134
https://blog.csdn.net/zheng548/article/details/54426947