java并发编程系列三、Lock和Condition
有了synchronized为什么还要Lock?
因为Lock和synchronized比较有如下优点
1、 尝试非阻塞地获取锁
2、 获取锁的过程可以被中断
3、 超时获取锁
Lock的标准用法
package com.lgs; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * lgs * 显示锁lock的标准写法 */ public class LockTemplete { public static void main(String[] args) { Lock lock = new ReentrantLock(); //获取锁 lock.lock(); try{ // do my work..... }finally{ //释放锁 lock.unlock(); } } }
Lock的常用方法
Lock() 获取锁
tryLock尝试非阻塞地获取锁
lockInterruptibly:获取锁的过程可以被中断
tryLock(long time, TimeUnit unit) 超时获取锁
unlock()释放锁
锁的可重入
一个线程获得了锁进入了同步代码块遇到了锁仍然可以进入同步代码块
递归的时候发生锁的重入,没有锁的可重入,就会死锁
公平和非公平锁
公平锁,先对锁发出获取请求的一定先被满足。公平锁的效率比非公平锁效率要低。
为什么非公平锁的性能要高:因为非公平锁是可以插队的,如线程C被唤醒变为可执行去获取锁的过程中,线程A插队进来直接获取锁执行自己的业务,线程A执行完以后,线程C刚好唤醒完直接就获取锁运行了,这样在线程C唤醒的过程中线程A就执行完了效率更高
读写锁ReentrantReadWriteLock
允许多个读线程同时进行,但是只允许一个写线程(不允许其他读线程防止脏读和写线程),支持读多写少场景,性能会有提升。
package com.lgs; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * lgs * 读写锁的使用 */ public class RwLockTemplete { static final Map<String,String> map = new HashMap<>(); static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(); static Lock r = reentrantReadWriteLock.readLock(); static Lock w = reentrantReadWriteLock.writeLock(); public void put(){ w.lock(); try{ // do my work..... }finally{ w.unlock(); } } public void get(){ r.lock(); try{ // do my work..... }finally{ r.unlock(); } } }
Condition接口有何用处?
Object的 wait,notify/notifyAll 实现等待通知机制
Condition接口和Lock配合来实现等待通知机制
Condition常用方法和使用范式
package com.lgs; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * lgs * Condition的使用方式 */ public class ConditionTemplete { Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); public void waitc() throws InterruptedException { lock.lock(); try{ condition.await(); }finally{ lock.unlock(); } } public void waitnotify() throws InterruptedException { lock.lock(); try{ condition.signal(); //condition.signalAll();尽量少使用 }finally{ lock.unlock(); } } }
结合ReentrantLock和Condition实现线程安全的有界队列
package com.lgs.bq; import java.util.LinkedList; import java.util.List; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * lgs * 结合ReentrantLock和Condition实现线程安全的有界队列 */ public class BlockingQueueLC<T> { private List queue = new LinkedList<>(); private final int limit; Lock lock = new ReentrantLock(); private Condition needNotEmpty = lock.newCondition(); private Condition needNotFull = lock.newCondition(); public BlockingQueueLC(int limit) { this.limit = limit; } public void enqueue(T item) throws InterruptedException { lock.lock(); try{ while(this.queue.size()==this.limit){ needNotFull.await(); } this.queue.add(item); //通知出队线程有数据可以取了 needNotEmpty.signal(); }finally{ lock.unlock(); } } public T dequeue() throws InterruptedException { lock.lock(); try{ while(this.queue.size()==0){ needNotEmpty.await(); } //通知入队线程有位置可以入队了 needNotFull.signal(); return (T) this.queue.remove(0); }finally{ lock.unlock(); } } }
package com.lgs.bq; /** * */ public class BqTest { public static void main(String[] args) { BlockingQueueLC<Integer> bq = new BlockingQueueLC(10); Thread threadA = new ThreadPush(bq); threadA.setName("Push"); Thread threadB = new ThreadPop(bq); threadB.setName("Pop"); threadB.start(); threadA.start(); } private static class ThreadPush extends Thread{ BlockingQueueLC<Integer> bq; public ThreadPush(BlockingQueueLC<Integer> bq) { this.bq = bq; } @Override public void run() { String threadName = Thread.currentThread().getName(); int i = 5; while(i>0){ try { Thread.sleep(500); System.out.println(" i="+i+" will push"); bq.enqueue(i--); } catch (InterruptedException e) { //e.printStackTrace(); } } } } private static class ThreadPop extends Thread{ BlockingQueueLC<Integer> bq; public ThreadPop(BlockingQueueLC<Integer> bq) { this.bq = bq; } @Override public void run() { while(true){ try { Thread.sleep(1000); System.out.println(Thread.currentThread().getName()+" will pop....."); Integer i = bq.dequeue(); System.out.println(" i="+i.intValue()+" alread pop"); } catch (InterruptedException e) { //e.printStackTrace(); } } } } }