第一节使用ReentrantLock类


1.1使用ReentrantLock实现同步:测试1

  使用下面代码获取ReenTrantLock对象lock

private Lock lock = new ReentrantLock();

  调用lock.lock()方法可以对代码进行加锁,调用lock.unlock()方法对代码进行解锁。

  注:需要进行加锁的代码放在lock()与unlock()方法中。


1.2使用ReentrantLock实现同步:测试2

  调用了lock()方法代码的线程会持有“对象监视器”,其他线程只有等待锁被释放时再次争抢,效果跟使用synchronized关键字一样,线程间还是顺序执行的。


1.3使用Condition实现等待/同步错误用法与解决

  类ReentrantLock实现等待/同步功能,需要借助于condition对象。

  Condition类具有很好的灵活性,可以实现多路通知功能:在一个Lock对象中创建多个Condition(对象监视器)实例,线程对象可以注册在指定的Condition中,从而有选择性地进行线程通知,在调度线程上更灵活。

  在使用notify()/notifyAll()方法进行通知时,被通知的线程是由JVM随机选择的,但使用ReentrantLock结合Condition类是可以实现前面介绍过的“选择性通知”,这个功能是常用,且在Condition中是默认的。

  通过下面代码获取Condition实例

private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();

  然后使用condition.await()来进行等待操作。

  注:在进行await()前,必须调用lock.lock()来获取监视器对象,否则的话就会爆出异常:java.lang.IllegalMonitorStateException(无监视器异常)。


1.4正确使用Condition实现等待/通知

  Object类中的wait()方法相当于Condition类中的await()方法。

  Object类中的wait(long timeout)方法相当于Condition类中的await(long time, TimeUnit unit)方法。

  Object类中的notify()方法相当于Condition类中的single()方法。

  Object类中的notifyAll()方法相当于Condition类中的singleAll()方法。


1.5使用多个Condition实现通知部分线程:错误用法

  一个Condition对象可以通过singleAll()方法唤醒所有调用lock.newCondition().await()的线程。

  但是若想实现只唤醒部分线程,只能使用多个Condition对象了,这也就是Condition对象可以唤醒部分指定线程,提高程序运行效率。


1.6使用多个Condition实现通知部分线程:正确用法

  若想实现Condition对象唤醒部分指定线程。一定要是调用等待的Condition对象与调用唤醒的Condition保持是一个Condition对象。


1.7实现生产者/消费者模式:一对一交替打印

public class Service {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition(); private boolean flag = false; public void set() {
try {
lock.lock();
while (flag == true) {
condition.await();
}
System.out.println("我是set");
flag = true;
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
} public void get() {
try {
lock.lock();
while (flag == false) {
condition.await();
}
System.out.println("我是get");
flag = false;
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}

  一个线程调用set(),一个线程调用get()就可以实现一对一交替打印。


1.8实现生产者/消费者模式:多对多打印

  service代码还是同1.7,执行时开启多条线程即可,但是会遇到“假死”问题,可以通过将signal()改为signalAll()来解决。


1.9公平锁与非公平锁

  锁Lock分为“公平锁”与“非公平锁”。

  公平锁:标识线程获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得的FIFO先进先出顺序。

  非公平锁:一种线程抢占机制,是随机获取锁的,和公平锁不一样的就是先来的不一定先得到锁,这个方法可能会导致某些线程一直拿不到锁,自然不公平。

  创建的方式就是:

private Lock lock = new ReentrantLock(Boolean flag);

  flag为true就是公平锁,flag为false就是非公平锁。


1.10方法getHoldCount()、getQueueLength()和getWaitQueueLength()的测试

  1)int getHoldCount()                       查询当前线程保持此锁定的个数,也就是调用lock()方法的次数。

  2)int getQueueLength()                  返回正等待获取此锁定线程估计数。

      比如有5个线程,一个线程先执行await()方法,那么在调用getQueueLength()方法后返回值是4,说明有4个线程同时在等待lock的释放。

  3)int getWaitQueueLength(Condition condition)        返回等待与此锁定相关的给定条件Condition的线程估计数。

      比如有5个线程,每个线程都执行了同一个condition对象的await()方法,则调用getWaitQueueLength()返回的值就是5。

   注:以上都是ReentrantLock类的API


1.11方法hasQueuedThread()、hasQueuedThreads()、hasWaiters()的测试

   1)boolean hasQueuedThread(Thread thread)          查询指定的线程是否正在等待获取此锁定。  

  2)boolean hasQueuedThreads()                  查询是否有线程正在等待获取此锁定。

  3)boolean hasWaiters(Condition condition)             查询是否有线程正在等待与此锁定有关的condition条件。

  

  注:以上都是ReentrantLock类的API


1.12方法isFair()、isHeldByCurrentThread()和isLocked()的测试

  1)boolean isFair()            判断是不是公平锁

  2)boolean isHeldByCurrentThread()    查询当前线程是否保持此锁定

  3)boolean isLocked()            查询此锁定是否由任意线程保持

  注:以上都是ReentrantLock类的API


1.13方法lockInterruptibly()、tryLock()和tryLock(Long timeout, TimeUnit unit)的测试

  1)void lockInterruptibly()              如果当前线程未被中断(不处于中断状态),则获得锁定,如果已经被检测中断(处于中断状态)则出现异常。

            爆出java.lang.InterruptedException异常。

  2)boolean tryLcok()                仅在调用时锁定未被另一个线程保持的情况下,才获得该锁定。

  3)boolean tryLock(long timeout, TimeUnit unit)    如果锁定在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获得该锁定。

  注:以上都是ReentrantLock类的API


1.14方法awaitUninterruptibly()的使用

  此方法为condition的方法await()的替代。可以避免在挂起的时候遇到打断状态(interrupt)而爆出异常。


1.15方法awaitUntil()的使用

  在指定时间后唤醒自己。参数为long

  在线程等待期间也可以被其他线程唤醒。

  此方法是condition的API


1.16使用Condition实现顺序执行

  就是利用多个Condition实现顺序执行。

public class Main {
volatile private static int flag = 1; private static ReentrantLock lock = new ReentrantLock();
final private static Condition c1 = lock.newCondition();
final private static Condition c2 = lock.newCondition();
final private static Condition c3 = lock.newCondition(); public static void main(String[] args) throws Exception{
Thread threadA = new Thread() {
@Override
public void run() {
try {
lock.lock();
while (flag != 1) {
c1.await();
}
for (int i = 0; i < 3; i++) {
System.out.println("ThreadA" + (i + 1));
}
flag = 2;
c2.signalAll();
}catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
};
Thread threadB = new Thread() {
@Override
public void run() {
try {
lock.lock();
while (flag != 2) {
c2.await();
}
for (int i = 0; i < 3; i++) {
System.out.println("ThreadB" + (i + 1));
}
flag = 3;
c3.signalAll();
}catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
};
Thread threadC = new Thread() {
@Override
public void run() {
try {
lock.lock();
while (flag != 3) {
c3.await();
}
for (int i = 0; i < 3; i++) {
System.out.println("ThreadC" + (i + 1));
}
flag = 1;
c1.signalAll();
}catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}; Thread[] a = new Thread[5];
Thread[] b = new Thread[5];
Thread[] c = new Thread[5];
for (int i = 0; i < 5; i++) {
a[i] = new Thread(threadA);
b[i] = new Thread(threadB);
c[i] = new Thread(threadC);
a[i].start();
b[i].start();
c[i].start();
}
}
}

第二节使用ReentrantReadWriteLock类

  实际上ReentrantLock虽然能够保证实例变量的线程安全性,但是效率比较低。

  为了在一些不需要操作实例变量的方法中,使用一些效率较高的锁来实现提升运行效率,即使用ReentrantReadWriteLock读写锁。

  读写锁表示有两个锁,一个是读操作相关的锁,也成为共享锁;

            另一个是写操作相关的锁,也叫排他锁。

            也就是说读锁之间不互斥,读锁与写锁互斥,写锁与写锁互斥。在没有线程进行写操作时,多个进行读操作的线程都可以获取读锁,而进行写入操作的Thread只有在获取写锁后才能进

            行写入操作。

  注:多个线程可以同时进行读操作,但同一时刻只允许一个Thread进行写入操作。  


2.1读读共享

  可以通过如下代码获取读写锁

private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

  lock.readLock().lock()

  就可以获得读锁,并进行加锁。

  lock.readLock().unlock()

  释放读锁。

  注:读锁是共享锁。


2.2写写互斥  

  lock.writeLock().lock()

  就可以获得写锁,并进行加锁。

  lock.writeLock().unlock()

  释放写锁。

  注:写锁是互斥锁。


2.3读写互斥

2.4写读互斥

  读锁与写锁是互斥的,也就是说不管是读锁先加锁还是写锁先加锁,只要两者相遇都是互斥的,必须等待其中一个释放线程后才能够再获得锁。

  “读写”、“写读”、“写写”都是互斥的(因为写锁时互斥锁,所以只要出现写锁,就一定是互斥的)

  “读读”是共享的,非互斥的。


本章总结:

  本章用Lock来替换掉了synchronized关键字。

  Lock很重要,重要到什么程度?很多并发包的源码都是通过Lock实现的。学好了Lock,有助于你理解你进一步进阶去看一些并发的源码。

本文内容是书中内容兼具自己的个人看法所成。可能在个人看法上会有诸多问题(毕竟知识量有限,导致认知也有限),如果读者觉得有问题请大胆提出,我们可以相互交流、相互学习,欢迎你们的到来,心成意足,等待您的评价。

java多线程编程核心技术——第四章总结的更多相关文章

  1. Java多线程编程核心技术-第1章-Java多线程技能-读书笔记

    第 1 章 Java 多线程技能 本章主要内容 线程的启动 如何使线程暂停 如何使线程停止 线程的优先级 线程安全相关的问题 1.1 进程和多线程的概念及线程的优点 进程是操作系统结构的基础:是一次程 ...

  2. java多线程编程核心技术——第七章补漏拾遗

    本章主要知识点: 1)线程组的使用 2)如何切换线程状态 3)SimpleDateFormat类与多线程的解决方法 4)如何处理线程异常. 这本书基本来到了终点,其实在第四章来说,核心(基础)的线程知 ...

  3. Java多线程编程核心技术-第4章-Lock的使用-读书笔记

    第 4 章 Lock 的使用 本章主要内容 ReentrantLocal 类的使用. ReentrantReadWriteLock 类的使用. 4.1 使用 ReentrantLock 类 在 Jav ...

  4. java多线程编程核心技术——第三章

    第一节等待/通知机制 1.1不使用等待/通知机制实现线程间的通讯 1.2什么是等待/通知机制 1.3等待/通知机制的实现 1.4方法wait()锁释放与notify()锁不释放 1.5当interru ...

  5. java多线程编程核心技术——第三章总结

    第一节等待/通知机制 1.1不使用等待/通知机制实现线程间的通讯 1.2什么是等待/通知机制 1.3等待/通知机制的实现 1.4方法wait()锁释放与notify()锁不释放 1.5当interru ...

  6. Java多线程编程核心技术-第7章-拾遗增补-读书笔记

    第 7 章 拾遗增补 本章主要内容 线程组的使用. 如何切换线程状态. SimpleDataFormat 类与多线程的解决办法. 如何处理线程的异常. 7.1 线程的状态 线程对象在不同的运行时期有不 ...

  7. Java多线程编程核心技术-第5章-定时器 Timer-读书笔记

    第 5 章 定时器 Timer 定时 / 计划功能在移动开发领域使用较多,比如 Android 技术.定时计划任务功能在 Java 中主要使用的就是 Timer 对象,他在内部使用多线程的方式进行处理 ...

  8. Java多线程编程核心技术-第2章-对象及变量的并发访问-读书笔记

    第 2 章 对象及变量的并发访问 本章主要内容 synchronized 对象监视器为 Object 时的使用. synchronized 对象监视器为 Class 时的使用. 非线程安全是如何出现的 ...

  9. Java多线程编程核心技术-第6章-单例模式与多线程-读书笔记

    第 6 章 单例模式与多线程 本章主要内容 如何使单例模式遇到多线程是安全的.正确的. 6.1 立即加载 / “饿汉模式” 什么是立即加载?立即加载就是使用类的时候已经将对象创建完毕,常见的实现办法就 ...

随机推荐

  1. Eight(经典题,八数码)

    Eight Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Subm ...

  2. ArcGIS API for js InfoWindow

    说明:有关该示例中怎么引用部署在iis上的离线arcgis api请参考我前面的博文 1.运行效果 2.HTML代码 <!DOCTYPE html> <html> <he ...

  3. Django 之 ORM 字段和字段参数

    ORM介绍 ORM概念 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术. 简单的说,ORM是通过使用描述 ...

  4. Js自定义异常

    function MyError(msg){ this.name="MyError"; this.message = msg || "自定义异常的默认消息";} ...

  5. Java的接口和抽象类(转发:http://www.importnew.com/18780.html)

    深入理解Java的接口和抽象类 对于面向对象编程来说,抽象是它的一大特征之一.在Java中,可以通过两种形式来体现OOP的抽象:接口和抽象类.这两者有太多相似的地方,又有太多不同的地方.很多人在初学的 ...

  6. 关于mosquitto_internal.h:40:25:#include <uuid/uuid.h> 致命错误的解决

    一.安装mosquitto1.4的时候使用make的时候报以下错误: mosquitto_internal.h:40:25: 致命错误:openssl/ssl.h:没有那个文件或目录 #include ...

  7. HDU - 2701 Lampyridae Teleportae 【模拟】

    题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=2701 题意 有一个萤火虫会闪现 一个人 也会闪现 给出 这个人的起始位置 和他能够闪现的距离 然后依次 ...

  8. Effective java -- 1

    写博客我也不知道是不是一个好习惯,但是目前还不知道有什么其他更有效率的学习方法.现在的学习方法:看书,写博客.如果看明白一个东西,去写博客的话,这通常是一个浪费时间的行为,但是这个过程同样帮助自己二次 ...

  9. POJ2253 frogger 最短路 floyd

    #include<iostream>#include<algorithm>#include<stdio.h>#include<string.h>#inc ...

  10. Linux学习之CentOS(一)--CentOS6.6下Mysql数据库的安装与配置

    在这里我是通过yum来进行mysql数据库的安装的,通过这种方式进行安装,可以将跟mysql相关的一些服务.jar包都给我们安装好,所以省去了很多不必要的麻烦!!! [root@larry ~]# c ...