Java并发程序设计(15)并发锁之读写锁(续二)写锁降级

1.1.1. 读写锁应用之三写锁降级

ReentrantReadWriteLock还具有写锁降级的特点,而这跟可重入性有一些关系。

(1)持有写锁时可以降级为读锁。

(2)持有读锁时不能升级为写锁。

ReentrantReadWriteLock和ReentrantLock相似的是都有一个特点,就是可重入。可重入指已经获取到锁的线程可以再次获取锁,保证lock和unlock的次数相同即可。

package com.test.concurrence;

import java.util.Random;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.locks.ReadWriteLock;

import java.util.concurrent.locks.ReentrantReadWriteLock;

 class  MySharedResource3 {

 //构造函数:公平锁。

private ReadWriteLock  lock = new ReentrantReadWriteLock(true);

public void  read(int taskid){

lock.readLock().lock();

try{

for(int i=0;i<10;i++){

x--;

System.out.println(taskid + "," + i + ": read called: x:" + x + " , "+ Thread.currentThread().getName());

Thread.sleep(100);

}

} catch (InterruptedException e) {

e.printStackTrace();

}

finally{

lock.readLock().unlock();

}

}

public void  downgrade(int taskid){

//持有写锁

lock.writeLock().lock();

System.out.println(taskid + " writeLock locked.");

try{

    for(int i=0;i<10;i++){

x++;

System.out.println(taskid + "," + i  + ": write called: x:" + x + " , "+ Thread.currentThread().getName());

Thread.sleep(100);

}

    //在已经持有写锁的情况下继续持有读锁。

lock.readLock().lock();

System.out.println(taskid + " writeLock ->  readLock locked.");

//释放写锁后,降级为读锁。

lock.writeLock().unlock();

System.out.println(taskid + " writeLock unlocked.");

 for(int i=0;i<10;i++){

x++;

System.out.println(taskid + "," + i  + ": read in write: x:" + x + " , "+ Thread.currentThread().getName());

Thread.sleep(1000);

}

//释放读锁。

lock.readLock().unlock();

    System.out.println(taskid + " readLock locked.");

} catch (InterruptedException e) {

e.printStackTrace();

}

finally{//释放锁。

lock.readLock().lock();

lock.writeLock().unlock();

}

}

private   int  x = 0;

}

public class ReadWriteLockTest3 {

public static void main(String[] args) {

final MySharedResource3  sr = new MySharedResource3();

ExecutorService  executorService =  Executors.newCachedThreadPool();

int i;

for( i=0;i<10;i++) {

final int taskid = i;

executorService.execute(new Runnable(){

@Override

public void run() {

System.out.println("taskid:" + taskid);

while(true){

if( 0 == (taskid % 2)){

sr.downgrade(taskid);

}

else {

sr.read(taskid);

}

}

}

});

}

executorService.shutdown();

}

}

观察运行结果,可以看到在downgrade()函数中,当线程在执行写锁已经降级为读锁后的代码块时,其它线程时可以获得读锁的。

一个可能的运行结果如下:

taskid:0

taskid:4

taskid:3

taskid:1

taskid:2

0 writeLock locked.

taskid:5

0,0: write called: x:1 , pool-1-thread-1

taskid:6

taskid:9

taskid:8

taskid:7

0,1: write called: x:2 , pool-1-thread-1

0,2: write called: x:3 , pool-1-thread-1

0,3: write called: x:4 , pool-1-thread-1

0,4: write called: x:5 , pool-1-thread-1

0,5: write called: x:6 , pool-1-thread-1

0,6: write called: x:7 , pool-1-thread-1

0,7: write called: x:8 , pool-1-thread-1

0,8: write called: x:9 , pool-1-thread-1

0,9: write called: x:10 , pool-1-thread-1

0 writeLock ->  readLock locked.

0 writeLock unlocked.

0,0: read in write: x:11 , pool-1-thread-1

3,0: read called: x:10 , pool-1-thread-4

3,1: read called: x:9 , pool-1-thread-4

3,2: read called: x:8 , pool-1-thread-4

3,3: read called: x:7 , pool-1-thread-4

3,4: read called: x:6 , pool-1-thread-4

3,5: read called: x:5 , pool-1-thread-4

3,6: read called: x:4 , pool-1-thread-4

3,7: read called: x:3 , pool-1-thread-4

3,8: read called: x:2 , pool-1-thread-4

3,9: read called: x:1 , pool-1-thread-4

0,1: read in write: x:2 , pool-1-thread-1

0,2: read in write: x:3 , pool-1-thread-1

0,3: read in write: x:4 , pool-1-thread-1

0,4: read in write: x:5 , pool-1-thread-1

0,5: read in write: x:6 , pool-1-thread-1

0,6: read in write: x:7 , pool-1-thread-1

0,7: read in write: x:8 , pool-1-thread-1

0,8: read in write: x:9 , pool-1-thread-1

0,9: read in write: x:10 , pool-1-thread-1

0 readLock locked.

4 writeLock locked.

4,0: write called: x:11 , pool-1-thread-5

4,1: write called: x:12 , pool-1-thread-5

4,2: write called: x:13 , pool-1-thread-5

4,3: write called: x:14 , pool-1-thread-5

4,4: write called: x:15 , pool-1-thread-5

4,5: write called: x:16 , pool-1-thread-5

4,6: write called: x:17 , pool-1-thread-5

4,7: write called: x:18 , pool-1-thread-5

4,8: write called: x:19 , pool-1-thread-5

4,9: write called: x:20 , pool-1-thread-5

4 writeLock ->  readLock locked.

4 writeLock unlocked.

1,0: read called: x:19 , pool-1-thread-2

4,0: read in write: x:20 , pool-1-thread-5

1,1: read called: x:19 , pool-1-thread-2

1,2: read called: x:18 , pool-1-thread-2

1,3: read called: x:17 , pool-1-thread-2

1,4: read called: x:16 , pool-1-thread-2

1,5: read called: x:15 , pool-1-thread-2

1,6: read called: x:14 , pool-1-thread-2

1,7: read called: x:13 , pool-1-thread-2

1,8: read called: x:12 , pool-1-thread-2

1,9: read called: x:11 , pool-1-thread-2

4,1: read in write: x:12 , pool-1-thread-5

4,2: read in write: x:13 , pool-1-thread-5

4,3: read in write: x:14 , pool-1-thread-5

4,4: read in write: x:15 , pool-1-thread-5

4,5: read in write: x:16 , pool-1-thread-5

4,6: read in write: x:17 , pool-1-thread-5

4,7: read in write: x:18 , pool-1-thread-5

4,8: read in write: x:19 , pool-1-thread-5

4,9: read in write: x:20 , pool-1-thread-5

4 readLock locked.

2 writeLock locked.

2,0: write called: x:21 , pool-1-thread-3

2,1: write called: x:22 , pool-1-thread-3

2,2: write called: x:23 , pool-1-thread-3

2,3: write called: x:24 , pool-1-thread-3

2,4: write called: x:25 , pool-1-thread-3

2,5: write called: x:26 , pool-1-thread-3

2,6: write called: x:27 , pool-1-thread-3

2,7: write called: x:28 , pool-1-thread-3

2,8: write called: x:29 , pool-1-thread-3

2,9: write called: x:30 , pool-1-thread-3

2 writeLock ->  readLock locked.

2 writeLock unlocked.

5,0: read called: x:29 , pool-1-thread-6

2,0: read in write: x:30 , pool-1-thread-3

5,1: read called: x:29 , pool-1-thread-6

5,2: read called: x:28 , pool-1-thread-6

5,3: read called: x:27 , pool-1-thread-6

5,4: read called: x:26 , pool-1-thread-6

5,5: read called: x:25 , pool-1-thread-6

5,6: read called: x:24 , pool-1-thread-6

5,7: read called: x:23 , pool-1-thread-6

5,8: read called: x:22 , pool-1-thread-6

5,9: read called: x:21 , pool-1-thread-6

2,1: read in write: x:22 , pool-1-thread-3

2,2: read in write: x:23 , pool-1-thread-3

2,3: read in write: x:24 , pool-1-thread-3

2,4: read in write: x:25 , pool-1-thread-3

2,5: read in write: x:26 , pool-1-thread-3

2,6: read in write: x:27 , pool-1-thread-3

2,7: read in write: x:28 , pool-1-thread-3

2,8: read in write: x:29 , pool-1-thread-3

2,9: read in write: x:30 , pool-1-thread-3

2 readLock locked.

6 writeLock locked.

6,0: write called: x:31 , pool-1-thread-7

6,1: write called: x:32 , pool-1-thread-7

6,2: write called: x:33 , pool-1-thread-7

6,3: write called: x:34 , pool-1-thread-7

6,4: write called: x:35 , pool-1-thread-7

6,5: write called: x:36 , pool-1-thread-7

6,6: write called: x:37 , pool-1-thread-7

6,7: write called: x:38 , pool-1-thread-7

6,8: write called: x:39 , pool-1-thread-7

6,9: write called: x:40 , pool-1-thread-7

6 writeLock ->  readLock locked.

6 writeLock unlocked.

6,0: read in write: x:41 , pool-1-thread-7

9,0: read called: x:40 , pool-1-thread-10

9,1: read called: x:39 , pool-1-thread-10

9,2: read called: x:38 , pool-1-thread-10

9,3: read called: x:37 , pool-1-thread-10

9,4: read called: x:36 , pool-1-thread-10

9,5: read called: x:35 , pool-1-thread-10

9,6: read called: x:34 , pool-1-thread-10

9,7: read called: x:33 , pool-1-thread-10

9,8: read called: x:32 , pool-1-thread-10

9,9: read called: x:31 , pool-1-thread-10

6,1: read in write: x:32 , pool-1-thread-7

6,2: read in write: x:33 , pool-1-thread-7

6,3: read in write: x:34 , pool-1-thread-7

6,4: read in write: x:35 , pool-1-thread-7

6,5: read in write: x:36 , pool-1-thread-7

api doc中关于可重入和降级是这么的。

Reentrancy

This lock allows both readers and writers to reacquire read or write locks in the style of a ReentrantLock. Non-reentrant readers are not allowed until all write locks held by the writing thread have been released.

Additionally, a writer can acquire the read lock, but not vice-versa. Among other applications, reentrancy can be useful when write locks are held during calls or callbacks to methods that perform reads under read locks. If a reader tries to acquire the write lock it will never succeed.

【中文】

可重入性

这种锁(ReentrantReadWriteLock)允许读线程和写线程按照ReentrantLock的式样来再次获取到读或写锁。非重入的读是不允许的,直到所有已经被写线程持有的写锁都被 释放了。

另外,写线程能够获取到读锁,但是反过来不行。在其他应用中,调用或回调方法中需要持有读锁时执行读操作时,在已经持有写锁的情况下,可重入性是有用的。如果读线程尝试获取写锁,将不会成功。

Lock downgrading

Reentrancy also allows downgrading from the write lock to a read lock, by acquiring the write lock, then the read lock and then releasing the write lock. However, upgrading from a read lock to the write lock is not possible.

【中文】

锁降级

通过依次获取写锁,获取读锁,然后释放写锁的方式,可重入性也允许从一个写锁降级为一个读锁。然而,从一个读锁升级到写锁是不可能的。

时间: 2024-08-02 02:48:46

Java并发程序设计(15)并发锁之读写锁(续二)写锁降级的相关文章

Java并发程序设计(13)并发锁之读写锁

1.1.1. 读写锁的应用之一读写分离 读写锁ReentrantReadWriteLock相对于ReentrantLock在特定情况下能提高同步性能,这是因为读写锁有以下特点: (1)读和读可以同时进行. 这一点是ReentrantLock所没有的优点. (2)读和写不能同时进行. (3)写和写不能同时进行. 应用读写锁访问资源的代码如下所示. class MySharedResource { private ReadWriteLock lock = new ReentrantReadWrite

Java并发程序设计(12)并发锁之可重入锁ReentrantLock

1.1. 可重入锁ReentrantLock ReentrantLock是java并发库中提供的可重入锁.与synchronized同步块相比,有相似也有不同.相似的地方有: (1)都可以实现多线程之间的同步,避免对共享资源的访问冲突. (2)都是可重入的,即一个已经获取锁的线程可以再次获得同一个锁,synchronized也类似. 不同的地方有: (1)ReentrantLock更灵活,获取锁和释放锁可以在同一个方法中,也可以在不同方法中.synchronized通常用在同一个方法体内. (2

Java并发程序设计(16)并发锁之条件变量

1.1.1. 条件变量应用之等待通知 条件变量Condition提供了一种基于ReentrantLock的事件等待和通知的机制,并且可以监控任意指定的条件,在条件不满足时等待条件满足,其它线程在条件满足时可以通知等待条件的线程,从而唤醒等待中的线程. 下面的代码实现了两件工作分别由两个线程轮流不断执行的效果. package com.test.concurrence; import java.util.concurrent.locks.Condition; import java.util.co

Java并发程序设计(20)并发锁之倒数锁CountDownLatch

1.1. 倒数锁CountDownLatch CountDownLatch是另外一种线程同步工具.参与倒数的每个线程在工作完成后都执行countDown()方法,当所有线程都执行完后,会唤醒一个或多个在等待倒数计数为0的线程. package com.test.concurrence; import java.util.Date; import java.util.Random; import java.util.concurrent.CountDownLatch; import java.ut

Java并发程序设计(19)并发锁之循环障碍CyclicBarrier

1.1. 循环障碍CyclicBarrier CyclicBarrier用于多个线程在某个同步点达到同步.在所有线程都到达该同步点之前,已经到达同步点的线程会等待其他线程.简单理解可以说CyclicBarrier跟一个旅游团出去旅游是类似的.在旅游团所有队员都到达出发集合地点之前,提前达到的队员只能在集合地点等待其他队员.当所有队员都达到后就可以出发了.循环障碍可以类似于旅游团在每个集合地点都这么等待一次,然后统一前往下一个景点. package com.test.concurrence; im

Java并发程序设计(21)并发锁之交换器Exchanger

1.1. 交换器Exchanger Exchanger用于在两个线程之间同步的同时交换数据,并且仅仅可以用于两个线程之间,不支持多个线程之间交换. package com.test.concurrence; import java.util.concurrent.Exchanger; public class ExchangerTest { public static void main(String[] args) { //待交换数据的类型为字符串. final Exchanger<Strin

【实战Java高并发程序设计 4】数组也能无锁:AtomicIntegerArray

除了提供基本数据类型外,JDK还为我们准备了数组等复合结构.当前可用的原子数组有:AtomicIntegerArray.AtomicLongArray和AtomicReferenceArray,分别表示整数数组.long型数组和普通的对象数组. 这里以AtomicIntegerArray为例,展示原子数组的使用方式. AtomicIntegerArray本质上是对int[]类型的封装.使用Unsafe类通过CAS的方式控制int[]在多线程下的安全性.它提供了以下几个核心API: //获得数组第

java并发锁ReentrantReadWriteLock读写锁源码分析

1.ReentrantReadWriterLock基础 所谓读写锁,是对访问资源共享锁和排斥锁,一般的重入性语义为 如果对资源加了写锁,其他线程无法再获得写锁与读锁,但是持有写锁的线程,可以对资源加读锁(锁降级):如果一个线程对资源加了读锁,其他线程可以继续加读锁. java.util.concurrent.locks中关于多写锁的接口:ReadWriteLock public interface ReadWriteLock { /** * Returns the lock used for r

【实战Java高并发程序设计 7】让线程之间互相帮助--SynchronousQueue的实现

[实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference [实战Java高并发程序设计 3]带有时间戳的对象引用:AtomicStampedReference [实战Java高并发程序设计 4]数组也能无锁:AtomicIntegerArray [实战Java高并发程序设计 5]让普通变量也享受原子操作 [实战Java高并发程序设计6]挑战无锁算法:无锁的Vector实现 在对线程池的介绍中,提到了一个非