一:lock接口的基本使用
1.1:使用synchronized关键字实现wait/notify机制
1、业务类
public class MyService {
private Object lock = new Object();
public void awaitMethod(){
try {
synchronized (lock){
System.out.println("awaitMethod() start...");
lock.wait();
System.out.println("awaitMethod() end...");
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
public void notifyMethod(){
synchronized (lock){
System.out.println("notifyMethod() start...");
lock.notify();
System.out.println("notifyMethod() end...");
}
}
}
2、测试类
public class Demo {
public static void main(String[] args) throws InterruptedException {
MyService myService = new MyService();
//注意: 多个线程之间一定要使用的是同一把锁,所以这里不能写成如下:
// Thread t = new Thread(() -> new MyService().awaitMethod()).start();
Thread t1 = new Thread(() -> myService.awaitMethod());
Thread t2 = new Thread(() -> myService.notifyMethod());
t1.start();
TimeUnit.SECONDS.sleep(2);
t2.start();
}
}
3、控制台
awaitMethod() start...
notifyMethod() start...
notifyMethod() end...
awaitMethod() end...
4、注意点
a. 可以使用任意对象作为锁,但必须保证锁是同一把!
b. 一般都是使用锁去调用wait/notify/notifyAll方法!
c. wait/notify/notifyAll方法,必须在同步代码块中使用,否则会出现异常!
1.2:使用lock锁来实现wait/notify机制
1、业务类
public class MyService {
//依赖倒转原则
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void awaitMethod(){
try {
lock.lock();
System.out.println("awaitMethod() start...");
condition.await(); //会释放锁,await方法必须在lock和unlock中使用
System.out.println("awaitMethod() end...");
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void signalMethod(){
try {
lock.lock();
System.out.println("signalMethod() start...");
condition.signal(); //signal方法必须在lock和unlock中使用
System.out.println("signalMethod() end...");
}finally {
lock.unlock();
}
}
}
2、测试类
public class Demo {
public static void main(String[] args) throws InterruptedException {
//1.创建共享资源
MyService myService = new MyService();
//2.创建两个线程,分别执行业务类的不同方法
Thread t1 = new Thread(() -> myService.awaitMethod());
Thread t2 = new Thread(() -> myService.signalMethod());
t1.start();
//3.让main线程休眠2s,防止t2线程先执行.如果t2线程先执行,那么t1线程到时没人唤醒了
TimeUnit.SECONDS.sleep(2);
t2.start();
}
}
3、控制台
awaitMethod() start...
signalMethod() start...
signalMethod() end...
awaitMethod() end...
4、注意点
概述:使用lock锁来实现wait/notify机制,其实和我们使用synchronized关键字实现wait/notify机制很相似
区别如下:
一:synchronized中
1.等待和唤醒的方法为:wait/notify/notifyAll方法,一般是由锁来调用
2.等待和唤醒的方法必须在同步中使用,否则会出现异常
二:lock中:
1.等待和唤醒的方法为:await/signal/signalAll方法,并且只能由Condition类实例调用
2.等待和唤醒的方法必须在同步中使用(也就是lock.lock(),和lock.unlock()方法中使用),否则会出现异常
3.lock一般都和try..finally结合使用,finally中放lock.unlock();释放锁
1.3:Condition中的等待方法
1.3.1:wait():
一直等待,直到被唤醒或者被中断
1.3.2:await(long time, TimeUnit unit),long awaitNanos(long nanosTimeout)
使当前线程等待直到被唤醒或中断,或者指定的时间过去。
注:指定的时间过去会自动唤醒!但是否往下执行,则还需抢夺cpu的执行权
1、业务类
public class MyService {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void awaitMethod(){
try {
lock.lock();
System.out.println("awaitMethod() start time: "+System.currentTimeMillis());
condition.await(3, TimeUnit.SECONDS); //3秒后会自动被唤醒!
System.out.println("awaitMethod() end time: "+System.currentTimeMillis());
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
2、测试类
public class Demo {
public static void main(String[] args) throws InterruptedException {
//注:单个线程执行的时候,直接new MyService()没有关系!,如果多个线程直接new的话,就不是同一把锁了
new Thread(() -> new MyService().awaitMethod()).start();
}
}
3、控制台
awaitMethod() start time: 1596360184487 ①
awaitMethod() end time: 1596360187488 ②
注: ①中的最后四位: 4487, 和②中的最后四位: 7488, 刚好相差大约3000毫秒,也就是3秒!
注:long awaitNanos(long nanosTimeout) 类似,只不过是纳秒!
1.3.3:boolean awaitUntil(Date deadline)
1、业务类
public class MyService {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void waitMethod(){
try {
lock.lock();
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND,4); //当前时间后4秒
System.out.println("await start time: "+System.currentTimeMillis());
condition.awaitUntil(calendar.getTime());
System.out.println("await end time: "+System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
2、测试类
public class Demo {
public static void main(String[] args) {
new Thread(() -> new MyService().waitMethod()).start();
}
}
3、控制台
await start time: 1596373204297
await end time: 1596373208287
注:刚好相差大约4s左右!
1.3.4:awaitUninterruptibly()
一直等待,直到被唤醒,不会被中断
注:在演示在方法前,我们知道await()的重载方法都是可以被中断的,现在以await()演示一下被中断的效果
1、业务类
public class MyService {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void waitMethod(){
try {
lock.lock();//①
System.out.println("await start time: "+System.currentTimeMillis());
condition.await();
System.out.println("await end time: "+System.currentTimeMillis());
} catch (InterruptedException e) {
System.out.println("我捕获到了异常...");
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
2、测试类
public class Demo {
public static void main(String[] args) throws InterruptedException {
MyService myService = new MyService();
Thread t1 = new Thread(() -> myService.waitMethod());
t1.start();
//让t1线程先启动
Thread.sleep(2000);
t1.interrupt();
System.out.println("执行了中断t1线程的方法");
}
}
3、控制台
await start time: 1596373545296
执行了中断t1线程的方法
我捕获到了异常...
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.reportInterruptAfterWait(AbstractQueuedSynchronizer.java:2014)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2048)
at test.lock.practice01.MyService.waitMethod(MyService.java:17)
at test.lock.practice01.Demo.lambda$main$0(Demo.java:19)
at java.lang.Thread.run(Thread.java:748)
注:程序停止了
将上面业务类中的①处,将它改成
condition.awaitUninterruptibly()
因为该方法不会被interrupt
方法给中断,所以不会抛出InterruptedException异常,所以不需要catch去捕获!
4、使用awaitUninterruptibly()之后的控制台
await start time: 1596374052683
执行了中断t1线程的方法
注:程序没有停止,即使调用了中断方法也不会去停止,一直等待,直到被唤醒!!
来自:虽然帅,但是菜的cxy