1.ReentrantLock的限制

ReentrantLock保证了只有一个线程可以执行临界区代码。

临界区代码:任何时候只有1个线程可以执行的代码块。

临界区指的是一个访问共用资源(例如:共用设备或是共用存储器)的程序片段,而这些共用资源又无法同时被多个线程访问的特性。当有线程进入临界区段时,其他线程或是进程必须等待,有一些同步的机制必须在临界区段的进入点与离开点实现,以确保这些共用资源是被互斥获得使用,例如:semaphore。只能被单一线程访问的设备,例如:打印机。

public void inc(){
lock.lock();
try{
value += 1;
}finally{
lock.unlock();
}
}
public int get(){
lock.lock();
try(){
return value;
}finally{
lock.unlock();
}
}

但有时候,这种保护有些过头:

  • 因为我们发现任何时刻只允许1个线程修改,对inc()进行加锁是必须的。
  • 但是get()方法只读取数据,不修改数据。它应该允许多个线程同时调用。

我们期望:允许多个线程同时读,但只要有一个线程在写,其他线程就必须等待。

2. ReadWriteLock

使用ReadWriteLock可以解决:

  • 只允许一个线程写入(其他线程既不能写入,也不能读取)
  • 没有写入时,多个线程允许同时读(提高性能)
  • 实现
class Counter{
final ReadWriteLock lock = new ReentrantReadWriteLock();
//分别获得readLock和writeLock
final Lock rlock = lock.readLock();
final Lock wlock = lock.writeLock();
private int value = 0;
public void inc(){
wlock.lock();
try{
value += 1;
}finally {
wlock.unlock();
}
}
public int get(){
rlock.lock();
try{
return this.value;
}finally {
rlock.unlock();
}
}
}

3.示例

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock; class Count{
private ReadWriteLock lock = new ReentrantReadWriteLock();
private Lock rlock = lock.readLock();
private Lock wlock = lock.writeLock();
private int value = 0;
public void add(int m){
wlock.lock();
try{
value += m;
}finally {
wlock.unlock();
}
}
public void dec(int m){
wlock.lock();
try{
value -= m;
}finally {
wlock.unlock();
}
}
public int get(){
rlock.lock();
try{
return value;
}finally {
rlock.unlock();
}
}
}
public class Main{
final static int LOOP = 100;
public static void main(String[] args) throws Exception{
Count count = new Count();
Thread t1 = new Thread(){
public void run(){
for(int i=0;i<LOOP;i++){
count.add(1);
}
}
};
Thread t2 = new Thread(){
public void run(){
for(int i=0;i<LOOP;i++){
count.dec(1);
}
}
};
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(count.get());
}
}

ReadWriteLock适用条件:

  • 同一个实例,有大量线程读取,仅有少数线程修改。
  • 例如论坛帖子,回复视为写入,不频繁操作;浏览器看作读取,非常频繁。这种情况下,就可以适用ReadWriteLock

4. 总结:

使用ReadWriteLock可以提高读取效率:

  • ReadWriteLock只允许一个线程写入。
  • ReadWriteLock允许多个线程同时读取。
  • ReadWriteLock适合读多写少的场景。

廖雪峰Java11多线程编程-3高级concurrent包-2ReadWriteLock的更多相关文章

  1. 廖雪峰Java11多线程编程-3高级concurrent包-5Atomic

    Atomic java.util.concurrent.atomic提供了一组原子类型操作: 如AtomicInteger提供了 int addAndGet(int delta) int increm ...

  2. 廖雪峰Java11多线程编程-3高级concurrent包-4Concurrent集合

    Concurrent 用ReentrantLock+Condition实现Blocking Queue. Blocking Queue:当一个线程调用getTask()时,该方法内部可能让给线程进入等 ...

  3. 廖雪峰Java11多线程编程-3高级concurrent包-1ReentrantLock

    线程同步: 是因为多线程读写竞争资源需要同步 Java语言提供了synchronized/wait/notify来实现同步 编写多线程同步很困难 所以Java提供了java.util.concurre ...

  4. 廖雪峰Java11多线程编程-3高级concurrent包-6ExecutorService

    Java语言内置多线程支持: 创建线程需要操作系统资源(线程资源,栈空间) 频繁创建和销毁线程需要消耗大量时间 如果可以复用一个线程 线程池: 线程池维护若干个线程,处于等待状态 如果有新任务,就分配 ...

  5. 廖雪峰Java11多线程编程-3高级concurrent包-9Fork_Join

    线程池可以高效执行大量小任务: Fork/Join线程池可以执行一种特殊的任务: 把一个大任务拆成多个小任务并行执行 Fork/Join是在JDK 1.7引入的 示例:计算一个大数组的和 Fork/J ...

  6. 廖雪峰Java11多线程编程-3高级concurrent包-8CompletableFuture

    使用Future可以获得异步执行结果 Future<String> future = executor.submit(task); String result = future.get() ...

  7. 廖雪峰Java11多线程编程-3高级concurrent包-7Future

    JDK提供了ExecutorService接口表示线程池,可以通过submit提交一个任务. ExecutorService executor = Executors.newFixedThreadPo ...

  8. 廖雪峰Java11多线程编程-3高级concurrent包-3Condition

    Condition实现等待和唤醒线程 java.util.locks.ReentrantLock用于替代synchronized加锁 synchronized可以使用wait和notify实现在条件不 ...

  9. 廖雪峰Java11多线程编程-2线程同步-3死锁

    1.线程锁可以嵌套 在多线程编程中,要执行synchronized块: 必须首先获得指定对象的锁 Java的线程锁是可重入的锁.对同一个对象,同一个线程,可以多次获取他的锁,即同一把锁可以嵌套.如以下 ...

随机推荐

  1. __attribute__ (( __cleanup__))

    一.简单说明: cleanup作为__attribute__属性中的一个可选属性值 其作用是当其声明的变量离开了其生命周期,那么 会自动调用你所指定的销毁函数 二.例子: #include <s ...

  2. HDU—4046 Panda (线段树)

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=4046   题意:给出一个字符串,统计这个字符串任意区间中"wbw"出现的次数. 规定两 ...

  3. spring在普通类中获取session和request

    在使用spring时,经常需要在普通类中获取session,request等对像.比如一些AOP拦截器类,在有使用struts2时,因为struts2有一个接口使用org.apache.struts2 ...

  4. Vue键盘事件

    键盘事件一 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <tit ...

  5. FIN_WAIT_2

    来自转载:http://blog.sina.com.cn/s/blog_8e5d24890102w9yi.html 上图对排除和定位网络或系统故障时大有帮助,但是怎样牢牢地将这张图刻在脑中呢?那么你就 ...

  6. The linux command之高级键盘技巧

    一.光标移动 二.修改文本 三.剪切和粘贴文本 四.使用历史命令

  7. canvas填充规则,非零环绕

    1.看一块区域是否填充 2.从这个区域拉一条直线 3,看和这条直线相交的轨迹 4.如果顺时针轨迹+1 5.如果逆时针轨迹-1 6.所有轨迹的值计算出来 7.如果是非0,那么填充 8.如果是0那么不填充

  8. CF475F meta-universe

    题意:给你一个无限大矩形中有一些planet,每次可以选择某一没有planet的行或列分割开矩形(分割开以后要求矩形不为空).问最后能分割成几个矩形? 标程: #include<bits/std ...

  9. [JZOJ6340] 【NOIP2019模拟2019.9.4】B

    题目 题目大意 给你个非负整数数列\(a\),每次等概率选择大于零的\(a_i\),使其减\(1\). 问\(a_1\)被减到\(0\)的时候期望经过多少次操作. 思考历程 对于这题的暴力做法,显然可 ...

  10. spring整合Quartz框架过程,大家可以参考下

    这篇文章详细介绍了spring集成quartz框架流程,通过示例代码进行了详细说明,对学习或任务有参考学习价值,并可供需要的朋友参考. 1.quartz框架简介(m.0831jl.com) quart ...