Lock的使用
- 4.1 使用ReentrantLock类
- 4.1.1 使用ReentrantLock实现同步:测试1
- 4.1.2 使用ReentrantLock实现同步:测试2
- 4.1.3 使用Condition实现等待/通知:错误用法与解决
- 4.1.4 正确使用Condition实现等待/通知
- 4.1.5 使用多个Condition实现通知部分线程:错误用法
- 4.1.6 使用多个Condition实现通知部分线程:正确用法
- 4.1.7 实现生产者/消费者模式:一对一交替打印
- 4.1.8 实现生产者/消费者模式:多对多交替打印
- 4.1.9 公平锁与非公平锁
- 4.1.10 方法getHoldCount()、getQueueLength()、getWaitQueueLength()的测试
- 4.1.11 方法hasQueuedThread()、hasQueuedThreads()、hasWaiters()的测试
- 4.1.12 方法isFair()、isHeldByCurrentThread()、isLocked()的测试
- 4.1.13 方法lockInterruptibly()、tryLock()、tryLock(long timeout,TimeUnit unit)的测试
- 4.1.14 方法awaitUninterrptibly()的使用
- 4.1.15 方法awaitUntil()的使用
- 4.1.16 使用Condition实现顺序执行
- 4.2 使用ReentrantReadWriteLock类
4.1 使用ReentrantLock类
4.1.1 使用ReentrantLock实现同步:测试1
4.1.2 使用ReentrantLock实现同步:测试2
package org.test.t8.t_1;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyService {
private Lock lock = new ReentrantLock();
public void methodA() {
try {
lock.lock();
System.out.println("methodA begin ThreadName="
+ Thread.currentThread().getName() + " time=" +
System.currentTimeMillis());
Thread.sleep(5000);
System.out.println("methodA end ThreadName="
+ Thread.currentThread().getName() + " time=" +
System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
lock.unlock();
}
}
public void methodB() {
try {
lock.lock();
System.out.println("methodB begin ThreadName="
+ Thread.currentThread().getName() + " time=" +
System.currentTimeMillis());
Thread.sleep(5000);
System.out.println("methodB end ThreadName="
+ Thread.currentThread().getName() + " time=" +
System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException{
MyService service = new MyService();
ThreadA a = new ThreadA(service);
a.setName("A");
a.start();
ThreadAA aa = new ThreadAA(service);
aa.setName("AA");
aa.start();
ThreadB b = new ThreadB(service);
b.setName("B");
b.start();
ThreadBB bb = new ThreadBB(service);
bb.setName("BB");
bb.start();
}
}
调用lock.lock()的线程就持有了对象监视器,其他线程只有等待锁被释放时再次争抢。
效果和使用synchronized一样,线程之间还是按照顺序执行的。
4.1.3 使用Condition实现等待/通知:错误用法与解决
使用ReentrantLock结合Condition类是可以实现前面介绍过的“选择性通知”。
package other.thread15;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class DemoService {
private Lock lock = new ReentrantLock();
private 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 other.thread15;
public class ThreadA extends Thread {
private DemoService service;
public ThreadA(DemoService service) {
this.service = service;
}
@Override
public void run() {
service.await();
}
}
package other.thread15;
public class Test {
public static void main(String[] args) throws InterruptedException {
DemoService service = new DemoService();
ThreadA threadA = new ThreadA(service);
threadA.start();
Thread.sleep(1000);
service.signal();
}
}
在condition.await()方法调用之前需要调用lock.lock()获得同步监视器。
4.1.4 正确使用Condition实现等待/通知
Object类中的wait()相当于Condition类中的await()方法。
Object类中的wait(long timeout)相当于Condtion类中的await(long time,TimeUnit unit)方法。
Object类中的notify()方法相当于Condition类中的signal()方法。
Object类中的notifyAll()方法相当于Condition类中的signalAll()方法。
4.1.5 使用多个Condition实现通知部分线程:错误用法
signalAll()会唤醒所有线程
4.1.6 使用多个Condition实现通知部分线程:正确用法
package other.thread15;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class DemoService {
private Lock lock = new ReentrantLock();
private Condition conditionA = lock.newCondition();
private Condition conditionB = lock.newCondition();
public void awaitA() {
try {
lock.lock();
System.out.println("await A 时间为:" + System.currentTimeMillis());
conditionA.await();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void awaitB() {
try {
lock.lock();
System.out.println("await B 时间为:" + System.currentTimeMillis());
conditionB.await();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void signalA() {
try {
lock.lock();
System.out.println("signal A 时间为:" + System.currentTimeMillis());
conditionA.signalAll();
}finally {
lock.unlock();
}
}
public void signalB() {
try {
lock.lock();
System.out.println("signal B 时间为:" + System.currentTimeMillis());
conditionB.signalAll();
}finally {
lock.unlock();
}
}
}
package other.thread15;
public class ThreadA extends Thread {
private DemoService service;
public ThreadA(DemoService service) {
this.service = service;
}
@Override
public void run() {
service.awaitA();
}
}
package other.thread15;
public class ThreadB extends Thread {
private DemoService service;
public ThreadB(DemoService service) {
this.service = service;
}
@Override
public void run() {
service.awaitB();
}
}
package other.thread15;
public class Test {
public static void main(String[] args) throws InterruptedException {
DemoService service = new DemoService();
ThreadA threadA = new ThreadA(service);
threadA.start();
ThreadB threadB = new ThreadB(service);
threadB.start();
Thread.sleep(1000);
service.signalA();
}
}
4.1.7 实现生产者/消费者模式:一对一交替打印
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) {
condition.await();
}
System.out.println("----");
hasValue = true;
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void get() {
try {
lock.lock();
while (hasValue == false) {
condition.await();
}
System.out.println("****");
hasValue = false;
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
//两个线程
public class MyThread1 extends Thread {
private MyService service;
public MyThread1(MyService service) {
this.service = service;
}
@Override
public void run() {
for (int i=0; i<100; i++) {
service.set();
}
}
}
public class MyThread2 extends Thread{
private MyService service;
public MyThread2(MyService service) {
this.service = service;
}
@Override
public void run() {
for (int i=0; i<100; i++) {
service.get();
}
}
}
public class Run {
public static void main(String[] args) throws InterruptedException {
MyService service = new MyService();
MyThread1 a = new MyThread1(service);
a.start();
MyThread2 b = new MyThread2(service);
b.start();
}
}
4.1.8 实现生产者/消费者模式:多对多交替打印
出现假死,用signalAll()解决。
4.1.9 公平锁与非公平锁
- 公平锁
package demo;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class MyService {
private ReentrantLock lock;
public MyService(boolean isFair) {
super();
lock = new ReentrantLock(isFair); // FIXME:判断公平锁非公平锁的传入参数
}
public void serviceMethod(){
try{
lock.lock();
System.out.println("获得线程锁的名称:" + Thread.currentThread().getName());
}finally {
lock.unlock();
}
}
}
package demo;
import com.sun.tools.internal.ws.wsdl.document.soap.SOAPUse;
public class Run1 {
public static void main(String[] args) {
final MyService myService = new MyService(true); // FIXME: 公平锁
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("开始运行的线程名称:" + Thread.currentThread().getName());
myService.serviceMethod();
}
};
Thread[] threadArray = new Thread[10];
for (int i = 0; i < 10; i++) {
threadArray[i] = new Thread(runnable);
}
for (int i = 0; i < 10; i++) {
threadArray[i].start();
}
}
}
- 非公平锁
package demo;
import com.sun.tools.internal.ws.wsdl.document.soap.SOAPUse;
public class Run1 {
public static void main(String[] args) {
final MyService myService = new MyService(false); // FIXME: 公平锁
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("开始运行的线程名称:" + Thread.currentThread().getName());
myService.serviceMethod();
}
};
Thread[] threadArray = new Thread[10];
for (int i = 0; i < 10; i++) {
threadArray[i] = new Thread(runnable);
}
for (int i = 0; i < 10; i++) {
threadArray[i].start();
}
}
}
4.1.10 方法getHoldCount()、getQueueLength()、getWaitQueueLength()的测试
- 方法int getHoldCount()的作用是查询当前线程保持此锁定的个数,也就是调用lock()方法的次数。
- 方法int getQueueLength()的作用是返回正等待获取此锁锁定的线程估计数,比如有5个线程,1个线程首先执行await()方法,那么在调用getQueueLength()方法后返回值是4,说明有4个线程同时在等待lock的释放。
- 方法int getWaitQueueLength(Condition condition) 的作用是返回等待与此锁定相关的给定条件Condition的线程估计数,比如有5个线程,每个线程都执行了同一个condition对象的await()方法,则调用getWaitQueueLength(Conditioncondition)方法时返回的int值是5。
4.1.11 方法hasQueuedThread()、hasQueuedThreads()、hasWaiters()的测试
- lock.hasQueuedThread(Thread A):指定的线程是否正在等待获取此锁定;
- lock.hasQueuedThreads():查询是否有线程正在等待获取锁;
- lock.hasWaiters(Condition cond):查询是否有线程在cond.await状态中;
4.1.12 方法isFair()、isHeldByCurrentThread()、isLocked()的测试
- isFair():作用是判断ReentrantLock是否是公平锁。返回true为公平锁,false为非公平锁。
- isHeldByCurrentThread():查询当前线程是否保持此锁定。
- isLocked():查询此锁是否由任意线程保持。
4.1.13 方法lockInterruptibly()、tryLock()、tryLock(long timeout,TimeUnit unit)的测试
- lockInterruptibly():如果当前线程未被中断,则获取锁定(需要等待别的线程释放锁才行),如果已被中断则出现异常。但是使用lock.lock()时,当前线程被中断,不会报错。
- tryLock():仅在调用时锁定未被另一个线程保持的情况下,才获取该锁定,否则就不获取。就是说只会去获取未被锁定的线程。
- tryLock(long timeout,TimeUnit unit):如果给定线程在等待时间内未被另一个线程保持,且当前线程未被中断,则获取该锁定,否则就不获取,相当于tryLock()加了等待时间。
4.1.14 方法awaitUninterrptibly()的使用
线程在调用condition.await()后处于await状态,此时调用thread.interrupt()会报错
但是使用condition.awaitUninterruptibly()后,调用thread.interrupt()则不会报错
4.1.15 方法awaitUntil()的使用
此方法可以证明线程在等待时间达到前,可以被其他线程提前唤醒。
public class Service {
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void waitMethod() {
try {
Calendar calendarRef = Calendar.getInstance();
calendarRef.add(Calendar.SECOND, 3);
lock.lock();
System.out.println("wait begin timer=" + System.currentTimeMillis());
condition.awaitUntil(calendarRef.getTime());
System.out.println("wait end timer=" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
lock.unlock();
}
}
public void notifyMethod() {
try {
Calendar calendarRef = Calendar.getInstance();
calendarRef.add(Calendar.SECOND, 3);
lock.lock();
System.out.println("notify begin timer=" + System.currentTimeMillis());
condition.signalAll();
System.out.println("notify end timer=" + System.currentTimeMillis());
}finally{
lock.unlock();
}
}
public static void main(String[] args) {
Service service = new Service();
MyThreadA myThreadA = new MyThreadA(service);
myThreadA.start();
}
}
4.1.16 使用Condition实现顺序执行
略
4.2 使用ReentrantReadWriteLock类
读写锁共有两个锁,一个是读相关的锁,也称作共享锁,另一个是与写操作相关的锁,也称作排他锁。同时规定如下:
- 读锁与读锁之间不互斥
- 写锁与写锁之间互斥
- 读锁与写锁之间互斥
4.2.1 类ReentrantReadWriteLock的使用:读读共享
class MyService {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void read() {
try {
lock.readLock().lock();
System.out.println(Thread.currentThread().getName() + " 获得读锁 "
+ System.currentTimeMillis());
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.readLock().unlock();
}
}
}
class ThreadA extends Thread {
private MyService myService;
public ThreadA(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
myService.read();
}
}
class ThreadB extends Thread {
private MyService myService;
public ThreadB(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
myService.read();
}
}
public class ReadAndRead {
public static void main(String[] args) throws InterruptedException {
MyService service = new MyService();
ThreadA threadA = new ThreadA(service);
threadA.setName("AAAA");
threadA.start();
Thread.sleep(2000);
ThreadB threadB = new ThreadB(service);
threadB.setName("BBBB");
threadB.start();
}
}
BBBB 获得读锁 1541404600765
AAAA 获得读锁 1541404600765
4.2.2 类ReentrantReadWriteLock的使用:写写互斥
class MyService2 {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void write() {
try {
lock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + " 获得写锁 "
+ System.currentTimeMillis());
Thread.sleep(10000);
System.out.println(Thread.currentThread().getName() + " 结束写锁 "
+ System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.writeLock().unlock();
}
}
}
class ThreadA2 extends Thread {
private MyService2 service2;
public ThreadA2(MyService2 service2) {
this.service2 = service2;
}
@Override
public void run() {
service2.write();
}
}
class ThreadB2 extends Thread {
private MyService2 service2;
public ThreadB2(MyService2 service2) {
this.service2 = service2;
}
@Override
public void run() {
service2.write();
}
}
public class WriteAndWrite {
public static void main(String[] args) {
MyService2 service2 = new MyService2();
ThreadA2 threadA2 = new ThreadA2(service2);
threadA2.setName("AAA");
ThreadB2 threadB2 = new ThreadB2(service2);
threadB2.setName("BBB");
threadA2.start();
threadB2.start();
}
}
AAA 获得写锁 1541405542280
AAA 结束写锁 1541405552281
BBB 获得写锁 1541405552282
BBB 结束写锁 1541405562282
4.2.3 类ReentrantReadWriteLock的使用:读写互斥
class MyService3 {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void read() {
try {
lock.readLock().lock();
System.out.println(Thread.currentThread().getName() + " 获得读锁 "
+ System.currentTimeMillis());
Thread.sleep(10000);
System.out.println(Thread.currentThread().getName() + " 结束读锁 "
+ System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.readLock().unlock();
}
}
public void write() {
try {
lock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + " 获得写锁 "
+ System.currentTimeMillis());
Thread.sleep(10000);
System.out.println(Thread.currentThread().getName() + " 结束写锁 "
+ System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.writeLock().unlock();
}
}
}
class ThreadA3 extends Thread {
private MyService3 myService3;
public ThreadA3(MyService3 myService3) {
this.myService3 = myService3;
}
@Override
public void run() {
myService3.read();
}
}
class ThreadB3 extends Thread {
private MyService3 myService3;
public ThreadB3(MyService3 myService3) {
this.myService3 = myService3;
}
@Override
public void run() {
myService3.write();
}
}
public class ReadAndWrite {
public static void main(String[] args) {
MyService3 service3 = new MyService3();
ThreadA3 threadA3 = new ThreadA3(service3);
threadA3.setName("AAA");
ThreadB3 threadB3 = new ThreadB3(service3);
threadB3.setName("BBB");
threadA3.start();
threadB3.start();
}
}
AAA 获得读锁 1541407541955
AAA 结束读锁 1541407551955
BBB 获得写锁 1541407551955
BBB 结束写锁 1541407561955
4.2.4 类ReentrantReadWriteLock的使用:写读互斥
参考4.2.3