(四)Lock,ReentrantLock,ReentrantReadWriteLock类的使用以及相关api---synchronized进阶
这篇博客记录了Lock,ReentrantLock,ReentrantReadWriteLock类的使用以及其一些api:
码字不易~~另外《java多线程编程核心技术》这本书读着很爽
前言说明:之前为了解决多线程时的非线程安全问题,使用的是synchronized。接下来记录的是他的升级版本ReentrantLock,更加灵活,可控性更高,而ReentrantReadWriteLock类是对ReentrantLock类的补充,能够在某些条件之间之下提交效率
下面先来看下都有哪些api,以及和synchronized之间是怎样对应的吧。
以前使用锁完成同步是将同步代码块写在synchronized之内,现在我们使用
Lock lock = new ReentrantLock();
来声明一个锁,他有这两个方法
lock.lock(); 和 lock.unlock(); 这两个是配套的,在其之间的代码就是同步代码块。
和之前一样,lock()方法会让当前线程持有对象监听器,具体规则之类的和synchronized也一样,
比如下面的例子,MyService有一段代码上锁,自定义线程类调用它
MyService.java
package 第四章;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class MyService {
private Lock lock = new ReentrantLock();
public void testMethod(){
lock.lock();
for(int i=0;i<5;i++){
System.out.println(i+"线程:"+Thread.currentThread().getName());
}
lock.unlock();
}
}
MyThread.java
package 第四章; public class MyThread extends Thread {
private MyService myService; public MyThread(MyService myService) {
super();
this.myService = myService;
} public void run(){
myService.testMethod();
}
}
test.java
package 第四章; public class test {
public static void main(String[] args){
MyThread[] threads = new MyThread[5];
MyService myService = new MyService();
for(int i=0;i<5;i++){
threads[i] = new MyThread(myService);
threads[i].start();
}
}
}
运行结果:
可以看到线程之间是同步执行的,当然前提是同一个MyService对象。
之前的wait/notify,用Condition对象来替换:
效率提高的地方以及原因:
Condition对象可以对同一个锁声明多个,相当于每当让线程等待时,他都有自己的唤醒condition,换句话说,每一个线程都可以注册一个Condition,这样当我们唤醒线程的时候,就可以唤醒指定的线程,比如之前的生产者消费者模型之中的假死现象,我们使用过notifyAll()来解决的,但是这种方法唤醒了所有的线程,让所有线程都去争抢cpu,但是我们事实上指向唤醒异类线程,并不想唤醒同类,全部唤醒的话,效率是一个问题。那么现在,给每一个线程都注册
一个Condition,这样子唤醒时候,我们就可以唤醒指定的线程,提高了效率,也更加灵活。
下面的是一个简单的await/signal例子,展示了基本的使用:await类似之前的wait,signal类似于notify:signalAll()唤醒全部
更改之前的MyService.java
condition.await()让线程阻塞,condition.signal()随机唤醒一个由当前condition注册的线程
package 第四章;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;
public class MyService {
private Lock lock = new ReentrantLock();
public Condition condition = lock.newCondition();
public void testMethod(){
try{
lock.lock();
System.out.println("即将开始循环");
condition.await();
for(int i=0;i<2;i++){
System.out.println(i+"线程:"+Thread.currentThread().getName());
}
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void signal(){
try{
lock.lock();
this.condition.signal();
System.out.println("唤醒了一个线程");
}finally {
lock.unlock();
}
}
}
MyThread.java不变
test.java:先让线程全部阻塞,然后调用自定义的signal方法唤醒线程,
package 第四章; public class test {
public static void main(String[] args){
MyThread[] threads = new MyThread[5];
MyService myService = new MyService();
for(int i=0;i<5;i++){
threads[i] = new MyThread(myService);
threads[i].start();
}
try{
Thread.sleep(1000);
myService.signal();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
运行结果如下:
可以看到,我们成功唤醒了一个线程。
下面的例子唤醒了一个指定的线程
MyService.java:根据当前线程的名字让指定的Condition对象等待,并书写两个唤醒不同的Condition对象注册的线程
package 第四章;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;
public class MyService {
private Lock lock = new ReentrantLock();
public Condition conditionA = lock.newCondition();
public Condition conditionB = lock.newCondition();
public void testMethod(){
try{
lock.lock();
System.out.println("线程"+Thread.currentThread().getName()+"等待中...");
if(Thread.currentThread().getName().equals("A"))
conditionA.await();
else
conditionB.await();
for(int i=0;i<2;i++){
System.out.println(i+"线程:"+Thread.currentThread().getName());
}
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void signalA(){
try{
lock.lock();
this.conditionA.signal();
System.out.println("唤醒了A线程");
}finally {
lock.unlock();
}
}
public void signalB(){
try{
lock.lock();
this.conditionB.signal();
System.out.println("唤醒了B线程");
}finally {
lock.unlock();
}
}
}
test.java,启动A,B两个线程,只唤醒A线程
package 第四章; public class test {
public static void main(String[] args){
MyService myService = new MyService();
MyThread myThreadA = new MyThread(myService);
myThreadA.setName("A");
MyThread myThreadB = new MyThread(myService);
myThreadB.setName("B");
myThreadA.start();
myThreadB.start();
try{
Thread.sleep(1000);
myService.signalA();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
运行结果:
根据代码,我们可以看到可以通过不同Condition对象来唤醒指定的线程。
用处:
1.可以想到,如果用Lock来解决之前的多消费多生产者时的假死问题,我们可以将生产者统一注册一个Condition,消费者统一注册一个Condition,每一次唤醒对方的Condition,这样子就不会出现连续唤醒同类导致假死的情况了,并且可以避免唤醒所有线程,导致效率低下。
2.我们也可以按照我们想要的顺序进行唤醒,只要你注册了正确的Condition对象
公平锁和非公平锁:
比较好理解,公平锁相当于一个队列,先进先出,先运行的线程先拿到锁,后运行的后拿到锁,按照顺序来,非公平锁就是锁的抢占是随机的,没有顺序。
默认是非公平锁,创建Lock时加上true参数即为公平锁:
Lock lock =new ReentrantLock(true);
下面介绍一些ReentrantLock的api,
一般在一些定制化的情况可能会用到,emmm,这块先了解一下,知道有这些就行,emmm,说实话目前我感觉这个没啥用,有个印象,不过注意使用这些API使,必须以下面这种方式new对象
ReentrantLock lock = new ReentrantLock();
(lock.)GetHoldCount():查询当前线程有保持着几个lock锁,简单来讲就是当前线程调用了几次lock()方法
GetQueueLength():有多少个线程在等待获取当前锁,可以理解为有多少个没有拿到当前锁,
getWaitQueueLength(Condition condition):有多少个线程处于阻塞状态,并且是执行了参数Condition对象所对应的await()方法导致阻塞的。
hasQueuedThread(Thread thread):查询指定的线程是否正在等待获取当前锁
hasQueuedThreads():查询是否有线程正在等待获取当前锁
hasWaiters(Condition):查询是否有线程是由于调用了参数Condition.await()导致阻塞的。
isHeldByCurrentThread():查询当前线程是否持有当前锁
isLocked():当前锁是否被某个线程持有
awaitUninterruptibly():这也是一种让当前线程阻塞的方法,不过await调用之后如果再使用Interrupt等代码阻塞当前进程会报异常,但是这个不会,相当于让当前线程变成可以阻塞的线程,,,,不懂有撒用
awaitUntil(Date):阻塞当前线程,如果在指定时间之前还没有被唤醒,则唤醒他。参数也可以传Calendar.getTime(),Calendar类用于处理时间
ReentrantReadWriteLock类
之前的ReentrantLock相当于同一时间只有一个线程在执行代码。但是在不涉及更改实例变量的代码之中,我们可以允许异步运行来加快效率, 而一些涉及到更改实例变量的代码,这时候同步执行(这时候异步可能出现非线程安全),这样可以在一定程度上加快效率,这就是这个类的作用。
简单来说,我们一般有读写两个操作,如果多个线程执行读操作,ok,异步执行,如果多个线程有的执行读,有的写,ok,同步执行,这个类就是自动完成这个事情,你只需要在锁时使用不同类型的锁就行。
下面是一个例子,读读异步(其他全部同步):
ReadAndWrite.java 代表具体的操作,读,写,输出当前操作以及时间,sleep()模拟操作耗费的时间
package 第四章; import java.util.concurrent.locks.ReentrantReadWriteLock; public class ReadAndWrite {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void read(){
try{
lock.readLock().lock();
System.out.println("读操作"+System.currentTimeMillis());
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.readLock().unlock();
}
}
public void write(){
try{
lock.writeLock().lock();
System.out.println("写操作"+System.currentTimeMillis());
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.writeLock().unlock();
}
}
}
MyThread2.java:里面有两个java类,一个执行读操作,一个写操作
package 第四章; class MyThreadRead extends Thread{
private ReadAndWrite readAndWrite; public MyThreadRead(ReadAndWrite readAndWrite) {
this.readAndWrite = readAndWrite;
} public void run(){
this.readAndWrite.read();
}
}
class MyThreadWrite extends Thread{
private ReadAndWrite readAndWrite; public MyThreadWrite(ReadAndWrite readAndWrite) {
this.readAndWrite = readAndWrite;
} public void run(){
this.readAndWrite.write();
}
}
test.java: 创建三个读线程
package 第四章; public class test {
public static void main(String[] args){
ReadAndWrite readAndWrite = new ReadAndWrite();
MyThreadRead reads[] = new MyThreadRead[3];
for(int i=0;i<3;i++) {
reads[i] = new MyThreadRead(readAndWrite);
reads[i].start();
}
}
}
运行结果:
可以看到,三个读操作时同时执行的。
下面更改test.java,创建三个读线程,三个写线程:
test.java
package 第四章; public class test {
public static void main(String[] args){ ReadAndWrite readAndWrite = new ReadAndWrite();
MyThreadWrite writes[] = new MyThreadWrite[3];
for(int i=0;i<3;i++) {
writes[i] = new MyThreadWrite(readAndWrite);
writes[i].start();
}
MyThreadRead reads[] = new MyThreadRead[3];
for(int i=0;i<3;i++) {
reads[i] = new MyThreadRead(readAndWrite);
reads[i].start();
} }
}
运行:
可以看到,写操作之间是互斥的,相当于同步,一个一个执行的,读的时候就是异步的,
,,好嘞,就演示这几个,其他的都同理,只有读读是异步的,读写同步,你可以交替着start看一下,如下:
好滴,第四章就这些暂时。。
(四)Lock,ReentrantLock,ReentrantReadWriteLock类的使用以及相关api---synchronized进阶的更多相关文章
- 高并发第十一弹:J.U.C -AQS(AbstractQueuedSynchronizer) 组件:Lock,ReentrantLock,ReentrantReadWriteLock,StampedLock
既然说到J.U.C 的AQS(AbstractQueuedSynchronizer) 不说 Lock 是不可能的.不过实话来说,一般 JKD8 以后我一般都不用Lock了.毕竟sychronize ...
- Java Lock ReentrantLock ReentrantReadWriteLock
Lock与Synchronized的区别: 1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现: 2)synchronized在发生异 ...
- ReentrantLock修饰类文件,实现按类获取锁的逻辑
1.ReentrantLock 给类文件加锁,实现类似synchronized(class)的功能 核心是类文件中,使用static修饰的reentrantLock对象 public class So ...
- ReentrantReadWriteLock类和ReentrantLock类的区别
Java.util.concurrent.locks包定义了两个锁类,ReentrantLock和ReentrantReadWriteLock类. 当有很多线程都从某个数据结构中读取数据而很少有线程对 ...
- 多线程编程学习四(Lock 的使用).
一.前言 本文要介绍使用Java5中 Lock 对象,同样也能实现同步的效果,而且在使用上更加方便.灵活,主要包括 ReentrantLock 类的使用和ReentrantReadWriteLock ...
- concrrent类下ReentrantReadWriteLock类的原理以及使用
1.ReentrantreadWriteLock 类的介绍 Lock接口下的子类存在 ReentrantLock子类,该子类是一个线程同步处理类:ReentrantLock类的介绍详见XXX: Loc ...
- Java多线程核心技术(四)Lock的使用
本文主要介绍使用Java5中Lock对象也能实现同步的效果,而且在使用上更加方便. 本文着重掌握如下2个知识点: ReentrantLock 类的使用. ReentrantReadWriteLock ...
- .net之工作流工程展示及代码分享(四)主控制类
现在应该讲主控制类了,为了不把系统弄得太复杂,所以就用一个类作为主要控制类(服务类),作为前端.后端.业务逻辑的控制类. WorkflowService类的类图如下: 该类的构造函数: public ...
- delphi 线程教学第四节:多线程类的改进
第四节:多线程类的改进 1.需要改进的地方 a) 让线程类结束时不自动释放,以便符合 delphi 的用法.即 FreeOnTerminate:=false; b) 改造 Create 的参数 ...
随机推荐
- HDFS的HA(高可用)
HDFS的HA(高可用) 概述 (1)实现高可用最关键的策略是[消除单点故障].HA 严格来说应该分成各个组件的 HA 机制:HDFS 的 HA 和 YARN 的 HA. (2)Hadoop2.0 之 ...
- C语言入门6-选择结构--f语句-switch
一. 什么是选择结构? 选择结构,也称为分支结构!! 选择结构就是根据 给定的判定条件,判断结果, 并根据 判断的结果 来控制程序的流程 (流程图中, 菱形框 是有来判断的 , ...
- [剑指offer] 16. 合并两个排序的链表
题目描述 输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则. 解法一: 非递归解 class Solution { public: ListNode *Mer ...
- Linux C 网络编程——多线程的聊天室实现(服务器端)
服务器端的主要功能: 实现多用户群体聊天功能(此程序最多设定为10人,可进行更改),每个人所发送的消息其他用户均可以收到.用户可以随意的加入或退出(推出以字符串"bye"实现),服 ...
- 重复造轮子系列——基于Ocelot实现类似支付宝接口模式的网关
重复造轮子系列——基于Ocelot实现类似支付宝接口模式的网关 引言 重复造轮子系列是自己平时的一些总结.有的轮子依赖社区提供的轮子为基础,这里把使用过程的一些觉得有意思的做个分享.有些思路或者方法在 ...
- thinkphp 数据库操作
//所有的数据中,查出某个字段$result = $music->field('music')->select();$hotlist = M('News')->where('stat ...
- Android的简述4
NoteEditor深入分析 首先来弄清楚“日志编辑“的状态转换,通过上篇文章的方法来做下面这样一个实验,首先进入“日志编辑“时会触发onCreate和onResume,然后用户通过Option Me ...
- rabbitMQ_topic(五)
主题转发器 发送到主题转发器的消息不能有任意的 routing_key - 它必须是由点分隔的单词列表.这些单词可以是任何东西,但通常它们指定与消息相关联的一些功能.几个有效的routeKey示例:“ ...
- js的位运算(其它语言也通用)
左移运算符(<<) 该运算符有2个运算数,a<<b,将a左移相当于a乘以2的b次方,2个运算符要求是整数,或可以转换成整数的. 如:1<<2 =4 "1& ...
- nginx基本运维及常用配置
nginx基本运维及常用配置 ========================================================== 基本运维 nginx 的启动 nginx -c /p ...