多线程 四
- 多线程 四
- 读写锁的使用
- 代码演示
- 乐观锁的使用
- 代码演示
- 信号量
- 代码演示
- 倒计时门禁
- 代码演示
- 循环栅栏
- Condition详解
- 代码案例
多线程 四
读写锁的使用
上一篇我们介绍到了可重入锁,现在我们来介绍读写锁。实际上,使用可重入锁的时候我们就可以明显感知到,其实是有优化的地方的。因为读的时候加的锁不应该跟写入的时候的锁一样。这样会降低并发效率的(因为多个线程同时读的话并不会造成问题)
代码演示
class Count {private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();private final Lock readLock = readWriteLock.readLock();private final Lock writeLock = readWriteLock.writeLock();private int a;public int add() {writeLock.lock();try {a++;}finally {writeLock.unlock();}return a;}public int get() {readLock.lock();try {return a;}finally {readLock.unlock();}}}
所以当我们面对的是改动不是很频繁,但是读取很频繁的场景的时候,实际上我们就可以考虑使用读写锁了
乐观锁的使用
前面的读写锁,已经在原有的临界资源的上,放开了多个线程同时读。但是如果写入的时候,有线程正在读的话,写的线程就必须要等待读线程完成,不然就会导致不一致。乐观锁的优化点是,在我们实际开发过程中,实际上写入是比较少的。我们写线程可以乐观不用等读线程结束。但这样肯定会有一些读写不一致的现象。 这时候就需要我们在代码里进行处理。注意乐观锁是不可重入锁。
代码演示
class Count1 {private final StampedLock stampedLock = new StampedLock();private int a;public int add() {long version = stampedLock.writeLock();try {a++;}finally {stampedLock.unlock(version);}return a;}public int get() {long tryOptimisticRead = stampedLock.tryOptimisticRead();if (!stampedLock.validate(tryOptimisticRead)) { // 看乐观锁是不是通过// 乐观锁不通过,获取悲观锁stampedLock.readLock();try {return a;}finally {stampedLock.unlockRead(tryOptimisticRead);}}return a;}}
信号量
信号量就好比,我们现实生活中,排队上厕所。一共就4个厕所。有人出来就释放厕所资源,后面的人可以获取资源,使用资源。比较简单好理解。
代码演示
class Count2 {final Semaphore semaphore = new Semaphore(5);public String get() throws InterruptedException {semaphore.acquire();try {return ""; // do something here}finally {semaphore.release();}}}
倒计时门禁
CountDownLatch类,只有倒计时的数字变成0才会放开。主要用于线程协同,要么控制同时开始,要么控制主从协作。
代码演示
public class Test19 {public static void main(String[] args) throws InterruptedException {CountDownLatch count = new CountDownLatch(2);List<Thread> threads = new ArrayList<>();for (int i = 0; i < 2; i++) {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(2000);System.out.println(Thread.currentThread().getName());} catch (InterruptedException e) {e.printStackTrace();}finally {count.countDown();}}});thread.start();threads.add(thread);}count.await();System.out.println("main over");}
}
这段代码实现的就是主线程,会等待所有子线程执行完毕。
循环栅栏
CyclicBarrier类,相当于是一个栅栏,所有线程在到达该栅栏后都需要等待其他线程,等所有线程都到达后再一起通过,它是循环的,可以用作重复的同步。循环栅栏的功能跟倒计时门禁的功能类似,只不过它是一直循环重复的,就不给代码演示了。
Condition详解
上一篇文章中,我说到了之前我们使用synchronized 是Java语言原生支持的,所以Object有协同用的方法。 现在我们线程协同,我们应使用Condition进行协同
使用方法:
- Condition对象必须从Lock实例的newCondition()返回。
- Condition的await()、signal()、signalAll()方法和synchronized使用的wait()、notify()、notifyAll()可以进行类比,就不展开了
public interface Condition {void await() throws InterruptedException;void awaitUninterruptibly();long awaitNanos(long var1) throws InterruptedException;boolean await(long var1, TimeUnit var3) throws InterruptedException;boolean awaitUntil(Date var1) throws InterruptedException;void signal();void signalAll();
}
代码案例
public class Test18 {public static void main(String[] args) throws InterruptedException {FlagThread flagThread = new FlagThread();flagThread.start();Thread.sleep(100);System.out.println("change flag");flagThread.change();}
}class FlagThread extends Thread {private volatile boolean flag = false;private Lock lock = new ReentrantLock();private Condition condition = lock.newCondition();@Overridepublic void run() {try {lock.lock();try {while (!flag) {condition.await();}} finally {lock.unlock();}System.out.println("run over flag");} catch (InterruptedException e) {Thread.interrupted();}}public void change() {lock.lock();try {this.flag = true;condition.signalAll();} finally {lock.unlock();}}
}