相关链接:
【Java多线程编程核心技术】1.Java多线程技能-笔记总结
【Java多线程编程核心技术】2.对象及变量的并发访问(上)-笔记总结
【Java多线程编程核心技术】2.对象及变量的并发访问(下)-笔记总结
【Java多线程编程核心技术】3.线程间通信 -笔记总结
【Java多线程编程核心技术】4.Lock的使用-笔记总结
【Java多线程编程核心技术】5.定时器Timer-笔记总结
【Java多线程编程核心技术】6.单例模式与多线程-笔记总结
【Java多线程编程核心技术】7.拾遗增补-笔记总结
一个可重入的互斥锁 Lock,它具有与使用 synchronized方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,并且在扩充功能上也更强大,比如具有嗅探锁定、多路分支通知等功能。
package service;
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("ThreadName=" + Thread.currentThread().getName()
+ (" " + (i + 1)));
}
lock.unlock();
}
}
......省略代码
与synchronized用法相似,只是多出来一个解锁(lock.unlock())的过程
与上一章通过wait()和notify()/notifyAll()类似,ReentrantLock类也可以实现同样的功能,但需要借助于Condition对象(await()与signal/signalAll())
ReentrantLock类结合Condition类可以实现“选择性通知”(notify()进行通知时,被通知的线程是由JVM随机选择的)
package extthread;
import service.MyService;
public class ThreadA extends Thread {
private MyService service;
public ThreadA(MyService service) {
super();
this.service = service;
}
@Override
public void run() {
service.await();
}
}
package service;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyService {
private Lock lock = new ReentrantLock();
public Condition condition = lock.newCondition();
public void await() {
try {
lock.lock();
System.out.println(" await时间为" + System.currentTimeMillis());
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void signal() {
try {
lock.lock();
System.out.println("signal时间为" + System.currentTimeMillis());
condition.signal();
} finally {
lock.unlock();
}
}
}
package test;
import service.MyService;
import extthread.ThreadA;
public class Run {
public static void main(String[] args) throws InterruptedException {
MyService service = new MyService();
ThreadA a = new ThreadA(service);
a.start();
Thread.sleep(3000);
service.signal();
}
}
输出结果:
await时间为1511258842692
signal时间为1511258845691
package extthread;
import service.MyService;
public class ThreadA extends Thread {
private MyService service;
public ThreadA(MyService service) {
super();
this.service = service;
}
@Override
public void run() {
service.awaitA();
}
}
package extthread;
import service.MyService;
public class ThreadB extends Thread {
private MyService service;
public ThreadB(MyService service) {
super();
this.service = service;
}
@Override
public void run() {
service.awaitB();
}
}
package service;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyService {
private Lock lock = new ReentrantLock();
public Condition conditionA = lock.newCondition();
public Condition conditionB = lock.newCondition();
public void awaitA() {
try {
lock.lock();
System.out.println("begin awaitA时间为" + System.currentTimeMillis()
+ " ThreadName=" + Thread.currentThread().getName());
conditionA.await();
System.out.println(" end awaitA时间为" + System.currentTimeMillis()
+ " ThreadName=" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void awaitB() {
try {
lock.lock();
System.out.println("begin awaitB时间为" + System.currentTimeMillis()
+ " ThreadName=" + Thread.currentThread().getName());
conditionB.await();
System.out.println(" end awaitB时间为" + System.currentTimeMillis()
+ " ThreadName=" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void signalAll_A() {
try {
lock.lock();
System.out.println(" signalAll_A时间为" + System.currentTimeMillis()
+ " ThreadName=" + Thread.currentThread().getName());
conditionA.signalAll();
} finally {
lock.unlock();
}
}
public void signalAll_B() {
try {
lock.lock();
System.out.println(" signalAll_B时间为" + System.currentTimeMillis()
+ " ThreadName=" + Thread.currentThread().getName());
conditionB.signalAll();
} finally {
lock.unlock();
}
}
}
package test;
import service.MyService;
import extthread.ThreadA;
import extthread.ThreadB;
public class Run {
public static void main(String[] args) throws InterruptedException {
MyService service = new MyService();
ThreadA a = new ThreadA(service);
a.setName("A");
a.start();
ThreadB b = new ThreadB(service);
b.setName("B");
b.start();
Thread.sleep(3000);
service.signalAll_A();
}
}
输出结果:(只通知了A)
begin awaitA时间为1511258966784 ThreadName=A
begin awaitB时间为1511258966785 ThreadName=B
signalAll_A时间为1511258969785 ThreadName=main
end awaitA时间为1511258969785 ThreadName=A
使用ReentrantLock对象可以唤醒指定种类的线程,这是控制部分线程行为的方便方式。
package extthread;
import service.MyService;
public class MyThreadA extends Thread {
private MyService myService;
public MyThreadA(MyService myService) {
super();
this.myService = myService;
}
@Override
public void run() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
myService.set();
}
}
}
package extthread;
import service.MyService;
public class MyThreadB extends Thread {
private MyService myService;
public MyThreadB(MyService myService) {
super();
this.myService = myService;
}
@Override
public void run() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
myService.get();
}
}
}
package service;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class MyService {
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private boolean hasValue = false;
public void set() {
try {
lock.lock();
while (hasValue == true) {
System.out.println("有可能★★连续");
condition.await();
}
System.out.println("打印★");
hasValue = true;
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void get() {
try {
lock.lock();
while (hasValue == false) {
System.out.println("有可能☆☆连续");
condition.await();
}
System.out.println("打印☆");
hasValue = false;
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
package test;
import service.MyService;
import extthread.MyThreadA;
import extthread.MyThreadB;
public class Run {
public static void main(String[] args) throws InterruptedException {
MyService service = new MyService();
MyThreadA[] threadA = new MyThreadA[10];
MyThreadB[] threadB = new MyThreadB[10];
for (int i = 0; i < 10; i++) {
threadA[i] = new MyThreadA(service);
threadB[i] = new MyThreadB(service);
threadA[i].start();
threadB[i].start();
}
}
}
输出结果:
有可能★★连续
有可能★★连续
打印☆
有可能☆☆连续
有可能☆☆连续
有可能☆☆连续
打印★
有可能★★连续
打印☆
.......
出现连续★★或者☆☆是因为signalAll()唤醒的同类争抢到了锁,这也是**只使用一个**condition的缺点吧。
锁Lock分为“公平锁”和“非公平锁”。
公平锁:表示线程获取锁的顺序是按照线程加锁的顺序来分配,即先来先得的FIFO顺序(也可以说成优先选择等待时间最长的线程)
非公平锁:是一种获取锁的抢占机制,是随机获取锁。
设置方法:lock=new ReentrantLock(true)/lock=new ReentrantLock(false) 默认是false,不公平
getHoldCount() 查询当前线程保持此锁定的个数,也就是调用 lock() 的方法
//lock.getHoldCount(); int
getQueueLength() 返回正等待获取此锁定的线程估计数
//lock.getQueueLength(); int
getWaitQueueLength() 返回等待与此锁定相关的给定条件 Condition 的线程估计数
//lock.getWaitQueueLength(newCondition) int
个人感觉有点类似于上一章提到的“阻塞队列”
hasQueuedThread() 查询指定的线程是否正在等待获取此锁定
//lock.hasQueuedThread(threadA); boolean
hasQueuedThreads() 查询是否有线程正在等待获取此锁定
//lock.hasQueuedThreads; boolean
hasWaiters() 查询是否有线程正在等待与此锁定有关的 condition 条件
//lock.hasWaiters(newCondition); boolean
isFair() 判断是否是公平锁
//lock.isFair(); boolean
isHeldByCurrentThread() 查询当前线程是否保持此锁定
//lock.isHeldByCurrentThread(); boolean
isLocked() 查询此锁定是否由任意线程保持
//lock.isLocked(); boolean
lockInterruptibly() 如果当前线程未被中断,则获取锁定,如果已经被中断则出现异常
//lock.lockInterruptibly(); void
tryLock() 仅在调用时锁定未被另一个线程保持的情况下,才获取该锁定(拿不到,立即返回)
//lock.tryLock(); boolean
tryLock(long time, TimeUtil util) 如果锁定在给定的等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁定。
//lock.tryLock(3,TimeUtil.SECONDS); boolean
awaitUninterruptibly():等待释放锁而且在线程被打断时interrupt()不会抛出异常
//condition.awaitUninterruptibly()
awaitUntil():如果在对应的时间内未被唤醒,则在对应时间结束后自动唤醒自己
用法:
Calendar calendarRef = Calendar.getInstance();
calendarRef.add(Calendar.SECOND, 10);
lock.lock();
condition.awaitUntil(calendarRef.getTime());
述API常用配套使用:
public ReentrantLock lock = new ReentrantLock();
public void waitMethod() {
try {
if (lock.tryLock(3, TimeUnit.SECONDS)) { //用法
System.out.println(" " + Thread.currentThread().getName()
+ "获得锁的时间:" + System.currentTimeMillis());
Thread.sleep(10000);
} else {
System.out.println(" " + Thread.currentThread().getName()
+ "没有获得锁");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock.isHeldByCurrentThread()) { //用法:
lock.unlock();
}
}
}
}
package finaltools;
public class F {
volatile public static int nextPrintWho = 1;
}
package test.run;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Run {
volatile private static int nextPrintWho = 1;
private static ReentrantLock lock = new ReentrantLock();
final private static Condition conditionA = lock.newCondition();
final private static Condition conditionB = lock.newCondition();
final private static Condition conditionC = lock.newCondition();
public static void main(String[] args) {
Thread threadA = new Thread() {
public void run() {
try {
lock.lock();
while (nextPrintWho != 1) {
conditionA.await();
}
for (int i = 0; i < 3; i++) {
System.out.println("ThreadA " + (i + 1));
}
nextPrintWho = 2;
conditionB.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
};
Thread threadB = new Thread() {
public void run() {
try {
lock.lock();
while (nextPrintWho != 2) {
conditionB.await();
}
for (int i = 0; i < 3; i++) {
System.out.println("ThreadB " + (i + 1));
}
nextPrintWho = 3;
conditionC.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
};
Thread threadC = new Thread() {
public void run() {
try {
lock.lock();
while (nextPrintWho != 3) {
conditionC.await();
}
for (int i = 0; i < 3; i++) {
System.out.println("ThreadC " + (i + 1));
}
nextPrintWho = 1;
conditionA.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
};
Thread[] aArray = new Thread[5];
Thread[] bArray = new Thread[5];
Thread[] cArray = new Thread[5];
for (int i = 0; i < 5; i++) {
aArray[i] = new Thread(threadA);
bArray[i] = new Thread(threadB);
cArray[i] = new Thread(threadC);
aArray[i].start();
bArray[i].start();
cArray[i].start();
}
}
}
输出结果:
ThreadA 1
ThreadA 2
ThreadA 3
ThreadB 1
ThreadB 2
ThreadB 3
ThreadC 1
ThreadC 2
ThreadC 3
.......
读写锁ReentrantReadWriteLock类,表示有2个锁,一个是读操作相关的锁,也称为共享锁。另一个是写操作相关的锁,也叫排他锁。多个读锁之间不互斥,写锁与写锁/写锁与读锁 互斥。
读写互斥demo代码:
package extthread;
import service.Service;
public class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.read();
}
}
package extthread;
import service.Service;
public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.write();
}
}
package service;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Service {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void read() {
try {
try {
lock.readLock().lock();
System.out.println("获得读锁A" + Thread.currentThread().getName()
+ " " + System.currentTimeMillis());
Thread.sleep(10000);
} finally {
lock.readLock().unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void write() {
try {
try {
lock.writeLock().lock();
System.out.println("获得写锁B" + Thread.currentThread().getName()
+ " " + System.currentTimeMillis());
Thread.sleep(10000);
} finally {
lock.writeLock().unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package test;
import service.Service;
import extthread.ThreadA;
import extthread.ThreadB;
public class Run {
public static void main(String[] args) throws InterruptedException {
Service service = new Service();
ThreadA a = new ThreadA(service);
a.setName("A");
a.start();
Thread.sleep(1000);
ThreadB b = new ThreadB(service);
b.setName("B");
b.start();
}
}
输出结果:
获得读锁AA 1511263792194
获得写锁BB 1511263802194
读写”、“写读”和“写写”都是互斥的;而“读读”是异步的,非互斥的。