Thread相关方法
run() 与 start()
package com.yanxb.method.threadmethod;
import com.yanxb.util.MyTool;
/**
* 测试 run()方法 和 start()方法
*
* */
public class TestRunAndStart {
public static void main(String[] args) {
test01();
}
/**
* 直接调用 run 是在主线程中执行了 run,没有启动新的线程
* 使用 start 是启动新的线程,通过新的线程间接执行 run 中的代码
*
* 输出
* 2022-04-08T05:39:28.958Z | 1649396368970 | 1 | main | 执行了...
* 2022-04-08T05:39:28.971Z | 1649396368971 | 13 | t2 | 执行了...
* */
private static void test01() {
Thread t1 = new Thread(() -> {
MyTool.printTimeAndThread("执行了...");
},"t1");
Thread t2 = new Thread(() -> {
MyTool.printTimeAndThread("执行了...");
},"t2");
t1.run();
t2.start();
}
}
yield() 与 setPriority()
package com.yanxb.method.threadmethod;
/**
* 测试 yield 方法 与设置优先级
* */
public class TestYieldAndPriority {
public static void main(String[] args) {
// test01();
test02();
}
/**
* 测试yield方法
* 1.调用 yield 会让当前线程从 Running 进入 Runnable 就绪状态,然后调度执行其它线程
* 2.具体的实现依赖于操作系统的任务调度器
*
* 输出:
* ---->2 150426
* ---->1 347692
* */
private static void test01() {
Runnable task1 = () -> {
int count = 0;
for (;;) {
System.out.println("---->1 " + count++);
}
};
Runnable task2 = () -> {
int count = 0;
for (;;) {
Thread.yield();
System.out.println(" ---->2 " + count++);
}
};
Thread t1 = new Thread(task1, "t1");
Thread t2 = new Thread(task2, "t2");
t1.start();
t2.start();
}
/**
* 测试设置线程优先级
* 1.线程优先级会提示(hint)调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它
* 2.如果 cpu 比较忙,那么优先级高的线程会获得更多的时间片,但 cpu 闲时,优先级几乎没作用
*
* 输出
* ---->1 57615
* ---->2 118569
* */
private static void test02() {
Runnable task1 = () -> {
int count = 0;
for (;;) {
System.out.println("---->1 " + count++);
}
};
Runnable task2 = () -> {
int count = 0;
for (;;) {
System.out.println(" ---->2 " + count++);
}
};
Thread t1 = new Thread(task1, "t1");
Thread t2 = new Thread(task2, "t2");
t1.setPriority(Thread.MIN_PRIORITY);
t2.setPriority(Thread.MAX_PRIORITY);
t1.start();
t2.start();
}
}
sleep()
package com.yanxb.method.threadmethod;
import com.yanxb.util.MyTool;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* 特点:
* 调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞)
* 其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException
* 睡眠结束后的线程未必会立刻得到执行
* 建议用 TimeUnit 的 sleep 代替 Thread 的 sleep 来获得更好的可读性
* sleep方法不会释放锁
*
* */
public class TestSleep {
public static void main(String[] args) {
// test01();
// test02();
// test03();
test04();
}
/**
* sleep方法不会释放锁测试
* 2022-04-07T00:05:22.456Z | 1649289922471 | 12 | t1 | sleep == 前str
* 2022-04-07T00:05:23.481Z | 1649289923481 | 12 | t1 | sleep == 后str
* 2022-04-07T00:05:23.481Z | 1649289923481 | 13 | t2 | t2str
* */
private static void test04() {
String str = "str";
new Thread(() -> {
synchronized (str){
MyTool.printTimeAndThread("sleep == 前" + str);
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
}
MyTool.printTimeAndThread("sleep == 后" + str);
}
},"t1").start();
new Thread(() -> {
synchronized (str){
MyTool.printTimeAndThread("t2 == " + str);
}
},"t2").start();
}
private static void test03() {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
lock.lock();
try {
// while(条件不满足) {
while (lock.toString() != null) {
try {
//让当前线程等待
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}finally {
lock.unlock();
}
new Thread(() -> {
//唤醒工作线程
condition.signalAll();
},"Other Thread").start();
}
/**
* obj.wait() 与 其他线程结合使用 obj.notifyAll()唤醒工作线程方式
* */
private static void test02() {
TestSleep testSleep = new TestSleep();
synchronized(testSleep) {
// while(条件不满足) {
while(testSleep.toString() != null) {
try {
testSleep.wait();
} catch(InterruptedException e) {
e.printStackTrace();
}
}
//业务逻辑
}
// 其他线程唤醒工作线程
Thread thread = new Thread(() -> {
testSleep.notifyAll();
}, "其他线程");
}
/**
* 限制cpu的使用率防止空转
* 在没有利用 cpu 来计算时,不要让 while(true) 空转浪费 cpu,这时可以使用 yield 或 sleep 来让出 cpu 的使用权给其他程序
* 其他方式实现:
* 可以用 wait 或 条件变量达到类似的效果
* 不同的是,后两种都需要加锁,并且需要相应的唤醒操作,一般适用于要进行同步的场景
* sleep 适用于无需锁同步的场景
* */
private static void test01() {
while(true) {
try {
Thread.sleep(50);
//业务逻辑
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
interrupt() 与 isInterrupted()
package com.yanxb.method.threadmethod;
import com.yanxb.util.MyTool;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
import static com.yanxb.util.MyTool.sleep;
/**
* 打断方法 interrupt()
*
* interrupt() 打断线程
* 如果被打断线程正在 sleep,wait,join会导致被打断的线程抛出InterruptedException,并清除打断标记;
* 如果打断的正在运行的线程,则会设置打断标记 ;park 的线程被打断,也会设置打断标记
* 打断正在运行的线程,是不会让对应线程立即停止的
* isInterrupted() 判断是否被打断 不会清除 打断标记
* interrupted() 判断当前线程是否被打断 会清除打断标记
* */
public class TestInterrupt {
public static void main(String[] args) throws InterruptedException {
// test01();
// test02();
// test03();
// test04();
}
/**
* 如果打断标记已经是 true, 则park会失效
* 执行 park 会先阻塞,当主线程睡眠时间到打断t1线程后,会将park置为失效
* */
private static void test04() {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 3; i++) {
MyTool.printTimeAndThread("park...");
LockSupport.park();
MyTool.printTimeAndThread("打断状态:" + Thread.currentThread().isInterrupted());
}
});
t1.start();
sleep(1);
t1.interrupt();
}
/**
*打断 park 线程, 不会清空打断状态
* */
private static void test03() {
Thread t1 = new Thread(() -> {
MyTool.printTimeAndThread("park...");
LockSupport.park();
MyTool.printTimeAndThread("unpark...");
MyTool.printTimeAndThread("打断状态:" + Thread.currentThread().isInterrupted());
}, "t1");
t1.start();
sleep(0.5);
t1.interrupt();
MyTool.printTimeAndThread("打断状态:" + t1.isInterrupted());
}
/**
* 测试打断正在运行的线程:
* 打断正在运行的线程,是不会让对应线程立即停止的(将死循环里的break注释掉即可证明)
*
* 输出:
* 2022-04-06T06:10:09.678Z | 1649225409688 | 1 | main | t1线程被打断前,打断标记为==false
* 2022-04-06T06:10:09.688Z | 1649225409688 | 1 | main | t1线程被打断后,打断标记为==true
* 2022-04-06T06:10:09.688Z | 1649225409688 | 12 | t1 | 当前线程被打断,退出循环,打断标记为==true
* */
private static void test02() throws InterruptedException {
Thread t1 = new Thread(() -> {
while (true) {
Thread thread = Thread.currentThread();
boolean interrupted = thread.isInterrupted();
MyTool.printTimeAndThread("当前线程被打断,退出循环,打断标记为==" + interrupted);
if (interrupted){
MyTool.printTimeAndThread("当前线程被打断,退出循环,打断标记为==" + interrupted);
break;
}
}
}, "t1");
t1.start();
TimeUnit.SECONDS.sleep(1);
MyTool.printTimeAndThread("t1线程被打断前,打断标记为==" + t1.isInterrupted());
t1.interrupt();
MyTool.printTimeAndThread("t1线程被打断后,打断标记为==" + t1.isInterrupted());
}
/**
* sleep,wait,join 这几个方法都会让线程进入阻塞状态
* 测试打断 sleep,wait,join 的线程 -> 报异常,且sleep()会清空打断标记,在调用interrupt()可以重置打断标记,在获取时即可获得true
*
* 输出:
* 打断前 -- main 获取thread 打断状态为false
* 打断后 -- main 获取thread 打断状态为false
* t1 e 线程 打断状态false
* */
private static void test01() throws InterruptedException {
Thread thread = new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
MyTool.printTimeAndThread("t1 e 线程 打断状态" + Thread.currentThread().isInterrupted());
e.printStackTrace();
//在这里调用interrupt()方法可以重置打断标记,但其他线程获取不到此打断标记
Thread.currentThread().interrupt();
boolean interrupted = Thread.currentThread().isInterrupted();
MyTool.printTimeAndThread("t1 e 线程 调用interrupt() 打断状态" + interrupted);
}
}, "t1");
thread.start();
MyTool.printTimeAndThread("打断前 -- main 获取thread 打断状态为" + thread.isInterrupted());
TimeUnit.SECONDS.sleep(1);
thread.interrupt();
// 结果为false -- interrupt打断sleep线程,打断标记会被清除
TimeUnit.SECONDS.sleep(1);
boolean interrupted = thread.isInterrupted();
MyTool.printTimeAndThread("打断后 -- main 获取thread 打断状态为" + interrupted);
}
}
join()
package com.yanxb.method.threadmethod;
import com.yanxb.util.MyTool;
import java.util.concurrent.TimeUnit;
/**
* 测试join() --> 等待结果返回
* */
public class TestJoin {
static int i = 0;
public static void main(String[] args) {
// test01();
test02();
}
/**
* 测试使用join()获取另外一个线程的执行结果
*
* 输出
* 2022-04-08T06:01:47.018Z | 1649397707030 | 12 | t1 | 执行业务逻辑...
* 2022-04-08T06:01:48.042Z | 1649397708042 | 1 | main | i 的值为 10
* */
private static void test01() {
Thread t1 = new Thread(() -> {
MyTool.printTimeAndThread("执行业务逻辑...");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
i = 10;
}, "t1");
t1.start();
try {
t1.join();
} catch (InterruptedException e) {}
MyTool.printTimeAndThread("i 的值为 " + i);
}
/**
* 测试使用join(long time)获取超时时另外一个线程的执行结果
*
* 输出
* 2022-04-08T06:07:36.419Z | 1649398056434 | 12 | t1 | 执行业务逻辑...
* 2022-04-08T06:07:37.434Z | 1649398057434 | 1 | main | i 的值为 0
* 2022-04-08T06:07:38.436Z | 1649398058436 | 12 | t1 | 返回结果...
* 2022-04-08T06:07:39.441Z | 1649398059441 | 1 | main | i 的值为 10
* */
private static void test02() {
Thread t1 = new Thread(() -> {
MyTool.printTimeAndThread("执行业务逻辑...");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) { }
MyTool.printTimeAndThread("返回结果...");
i = 10;
}, "t1");
t1.start();
try {
t1.join(1000);
} catch (InterruptedException e) {}
MyTool.printTimeAndThread("i 的值为 " + i);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {}
MyTool.printTimeAndThread("i 的值为 " + i);
}
}
LockSupport相关方法
park() 和 unpark(Thread t)
package com.yanxb.method.othermethod;
import com.yanxb.util.MyTool;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
/**
* 测试 park 让线程等待、unPark 唤醒线程 类
*
* 与 obj 中的 wait、 notify/notifyAll 的区别
* 1.wait,notify 和 notifyAll 必须配合 Object Monitor 一起使用,而 park,unpark 不必
* 2.park & unpark 是以线程为单位来【阻塞】和【唤醒】线程,而 notify 只能随机唤醒一个等待线程,notifyAll 是唤醒所有等待线程,就不那么【精确】
* 3.park & unpark 可以先 unpark,而 wait & notify 不能先 notify
* 4.控制的粒度会更细。JUC包下的很多类都是使用Unsafe(提供CAS操作)和LockSupport(提供park/unpark操作)实现
*
* 参考链接:
* https://www.cnblogs.com/zhizhizhiyuan/p/4966827.html
* https://blog.youkuaiyun.com/weixin_39687783/article/details/85058686
* */
public class TestPark {
public static void main(String[] args) throws InterruptedException {
// test01();
// test02();
// test03();
// test04();
}
/**
* 打断 park 线程:
* 打断 park 线程, 不会清空打断状态(如果被打断后,打断状态就为true; 如果清空打断状态,则打断状态还为false)
*
* 如果打断标记已经是 true, 则 park 会失效
* */
private static void test04() throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 3; i++) {
MyTool.printTimeAndThread("t1 park..." + i + Thread.currentThread().isInterrupted());
LockSupport.park();
MyTool.printTimeAndThread("打断状态:" + Thread.currentThread().isInterrupted());
}
},"t1");
t1.start();
TimeUnit.SECONDS.sleep(1);
t1.interrupt();
}
/**
* 打断park()中的线程 -- 不会清空打断标记
* */
private static void test03() throws InterruptedException {
Thread t1 = new Thread(() -> {
MyTool.printTimeAndThread("t1 打断状态唤醒...前");
MyTool.printTimeAndThread("t1 打断状态:"+ Thread.currentThread().isInterrupted());
LockSupport.park();
MyTool.printTimeAndThread("t1 打断状态唤醒...后");
MyTool.printTimeAndThread("t1 打断状态:"+ Thread.currentThread().isInterrupted());
}, "t1");
t1.start();
MyTool.printTimeAndThread("main 打断前 获取 t1打断状态:" + t1.isInterrupted());
TimeUnit.SECONDS.sleep(1);
t1.interrupt();
MyTool.printTimeAndThread("main 打断后 获取 t1打断状态:" + t1.isInterrupted());
}
/**
* 在t1线程执行代码前,在其他线程内先给与t1一个许可(也就是执行LockSupport.unpark(t1))
* 则t1线程在执行park()方法时不会被阻塞等待,而是直接执行后面的代码
* */
private static void test02() {
Thread t1 = new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
LockSupport.park();
MyTool.printTimeAndThread("park...后的代码");
}, "t1");
t1.start();
MyTool.printTimeAndThread("main 打断t1 park...前");
LockSupport.unpark(t1);
MyTool.printTimeAndThread("main 打断t1 park...后");
}
/**
* 先让t1线程等待,在主线程中睡眠两秒唤醒t1线程,让t1线程继续执行
* */
private static void test01() throws InterruptedException {
Thread t1 = new Thread(() -> {
MyTool.printTimeAndThread("park...前");
LockSupport.park();
MyTool.printTimeAndThread("park...后");
}, "t1");
t1.start();
MyTool.printTimeAndThread("打断 t1 park...前");
TimeUnit.SECONDS.sleep(1);
LockSupport.unpark(t1);
MyTool.printTimeAndThread("打断 t1 park...后");
}
}
原理
- 每个线程都有自己的一个 Parker 对象,由三部分组成 _counter , _cond 和 _mutex
情况一:先park(),在unPark(Thread t)
- 图一步骤说明
1.当前线程调用 Unsafe.park() 方法
2.检查 _counter ,本情况为 0,这时,获得 _mutex 互斥锁
3.线程进入 _cond 条件变量阻塞
4.设置 _counter = 0 - 图二步骤说明
1.调用 Unsafe.unpark(Thread_0) 方法,设置 _counter 为 1
2.唤醒 _cond 条件变量中的 Thread_0
3.Thread_0 恢复运行
4.设置 _counter 为 0
情况二:先unPark(Thread t),在park()
- 上图步骤说明
1.调用 Unsafe.unpark(Thread_0) 方法,设置 _counter 为 1
2.当前线程调用 Unsafe.park() 方法
3.检查 _counter ,本情况为 1,这时线程无需阻塞,继续运行
4.设置 _counter 为 0
Object相关方法
wait() 和 notify()/notifyAll()
package com.yanxb.method.othermethod;
import com.yanxb.util.MyTool;
import lombok.extern.slf4j.Slf4j;
/**
* 测试 obj 中的wait() 和 notify / notifyAll方法类
*
* 与 park 和 unpark 的区别:
* 1.wait,notify 和 notifyAll 放在synchronized使用,必须配合 Object Monitor 一起使用,而 park,unpark 不必
* 2.park & unpark 是以线程为单位来【阻塞】和【唤醒】线程,而 notify 只能随机唤醒一个等待线程,notifyAll 是唤醒所有等待线程,就不那么【精确】
* 3.park & unpark 可以先 unpark,而 wait & notify 不能先 notify
* */
@Slf4j(topic = "c.TestWaitNotify")
public class TestWaitNotify {
final static Object obj = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (obj) {
log.debug("执行....");
try {
obj.wait(); //让线程在obj上一直等待下去
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("其它代码....");
}
},"t1").start();
new Thread(() -> {
synchronized (obj) {
log.debug("执行....");
try {
obj.wait(); // 让线程在obj上一直等待下去
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("其它代码....");
}
},"t2").start();
// 主线程两秒后执行
MyTool.sleep(1);
log.debug("唤醒 obj 上其它线程");
synchronized (obj) {
// obj.notify(); // 唤醒obj上一个线程
obj.notifyAll(); // 唤醒obj上所有等待线程
}
}
}
原理
原理图
- Owner 线程发现条件不满足,调用 wait 方法,即可进入 WaitSet 变为 WAITING 状态
- BLOCKED 和 WAITING 的线程都处于阻塞状态,不占用 CPU 时间片
- BLOCKED 线程会在 Owner 线程释放锁时唤醒
- WAITING 线程会在 Owner 线程调用 notify 或 notifyAll 时唤醒,但唤醒后并不意味者立刻获得锁,仍需进入EntryList 重新竞争
API说明
- obj.wait() 让进入 object 监视器的线程到 waitSet 等待
- obj.notify() 在 object 上正在 waitSet 等待的线程中挑一个唤醒
- obj.notifyAll() 让 object 上正在 waitSet 等待的线程全部唤醒
- wait() 方法会释放对象的锁,进入WaitSet等待区,从而让其他线程就机会获取对象的锁。无限制等待,直到notify 为止
- wait(long n) 有时限的等待, 到 n 毫秒后结束等待,或是被notify
sleep(long n) 和 wait(long n) 的区别
- sleep 是 Thread 方法,而 wait 是 Object 的方法
- sleep 不需要强制和 synchronized 配合使用,但 wait 需要和 synchronized 一起用
- sleep 在睡眠的同时,不会释放对象锁的,但 wait 在等待的时候会释放对象锁
与park() 与unpark(Thread t)区别
- wait,notify 和 notifyAll 必须配合 Object Monitor 一起使用,而 park,unpark 不必
- park & unpark 是以线程为单位来【阻塞】和【唤醒】线程,而 notify 只能随机唤醒一个等待线程,notifyAll 是唤醒所有等待线程,就不那么【精确】
- park & unpark 可以先 unpark,而 wait & notify 不能先 notify
ReentrantLock相关方法
基本特性测试方法
package com.yanxb.method.othermethod;
import com.yanxb.util.MyTool;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/**
* ReentrantLock测试类
* 与 obj 使用 synchronized 关键字加锁的对比
* 1.ReentrantLock可中断
* 2.ReentrantLock可设置超时时间
* 3.ReentrantLock可设置公平锁(也就是先到先得)
* 4.ReentrantLock可以支持多个条件变量(类似于synchronized关联对象中minitor中的waitSet)
*
* 公平性与条件变量的使用没有总结在该类中
* */
public class TestReentrantLock {
private static ReentrantLock rl = new ReentrantLock();
public static void main(String[] args) {
// test01();
// test03();
// test04();
// test05();
// test06();
test07();
}
/**
* 获取锁超时测试 -- 超时时间 tryLock(long timeout, TimeUnit unit)
*
* 被打断场景时 输出
* 2022-04-10T02:25:32.298Z | 1649557532312 | 1 | main | main 获取到了锁
* 2022-04-10T02:25:32.312Z | 1649557532312 | 12 | t1 | t1 线程执行方法启动
* 2022-04-10T02:25:32.312Z | 1649557532312 | 12 | t1 | 等待获取锁时被打断
* java.lang.InterruptedException
* at java.util.concurrent.locks.AbstractQueuedSynchronizer.tryAcquireNanos(AbstractQueuedSynchronizer.java:1245)
* at java.util.concurrent.locks.ReentrantLock.tryLock(ReentrantLock.java:442)
* at com.yanxb.method.othermethod.TestReentrantLock.lambda$test07$0(TestReentrantLock.java:46)
* at java.lang.Thread.run(Thread.java:745)
*
* 获取到锁是输出
* 2022-04-10T02:26:17.139Z | 1649557577148 | 1 | main | main 获取到了锁
* 2022-04-10T02:26:17.148Z | 1649557577148 | 12 | t1 | t1 线程执行方法启动
* 2022-04-10T02:26:18.157Z | 1649557578157 | 12 | t1 | t1 获取了锁
* */
private static void test07() {
Thread thread = new Thread(() -> {
MyTool.printTimeAndThread("t1 线程执行方法启动 ");
try {
if (! rl.tryLock(2, TimeUnit.SECONDS)) {
MyTool.printTimeAndThread("t1 未获取到锁");
return;
}
} catch (InterruptedException e) {
MyTool.printTimeAndThread("等待获取锁时被打断");
e.printStackTrace();
return;
}
try {
MyTool.printTimeAndThread("t1 获取了锁");
}finally {
rl.unlock();
}
}, "t1");
rl.lock();
MyTool.printTimeAndThread("main 获取到了锁");
thread.start();
try {
// thread.interrupt();
MyTool.sleep(1);
}finally {
rl.unlock();
}
}
/**
* 获取锁超时测试 -- 立即超时 tryLock
*
* 输出
* 2022-04-10T02:20:45.585Z | 1649557245600 | 1 | main | main 获取到了锁
* 2022-04-10T02:20:45.601Z | 1649557245601 | 12 | t1 | t1 线程执行方法启动
* 2022-04-10T02:20:45.601Z | 1649557245601 | 12 | t1 | t1 未获取到锁
* */
private static void test06() {
Thread thread = new Thread(() -> {
MyTool.printTimeAndThread("t1 线程执行方法启动 ");
if (! rl.tryLock()) {
MyTool.printTimeAndThread("t1 未获取到锁");
return;
}
try {
MyTool.printTimeAndThread("t1 获取了锁");
}finally {
rl.unlock();
}
}, "t1");
rl.lock();
MyTool.printTimeAndThread("main 获取到了锁");
thread.start();
try {
MyTool.sleep(1);
}finally {
rl.unlock();
}
}
/**
* 获取锁时可打断性测试
* lockInterruptibly()方法获取锁是不可打断的
*
* 输出
* 2022-04-10T02:08:51.621Z | 1649556531636 | 1 | main | main 获取到了锁
* 2022-04-10T02:08:52.645Z | 1649556532645 | 12 | t1 | t1 被打断
* java.lang.InterruptedException
* at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
* at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
* at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
* at com.yanxb.method.othermethod.TestReentrantLock.lambda$test05$0(TestReentrantLock.java:33)
* at java.lang.Thread.run(Thread.java:745)
* */
private static void test05() {
Thread thread = new Thread(() -> {
try {
rl.lockInterruptibly();
//这里的代码不会被执行,因为在获取锁的过程中被其他线程打断了
MyTool.printTimeAndThread("t1 线程执行方法 ");
} catch (InterruptedException e) {
MyTool.printTimeAndThread("t1 被打断 ");
e.printStackTrace();
return;
}
try {
//这里的代码不会被执行,因为在获取锁的过程中被其他线程打断了
MyTool.printTimeAndThread("获取到了锁");
}finally {
rl.unlock();
}
}, "t1");
rl.lock();
MyTool.printTimeAndThread("main 获取到了锁");
thread.start();
MyTool.sleep(1);
try {
//这里打断t1获取锁的操作,从而控制线程的执行过程
thread.interrupt();
}finally {
rl.unlock();
}
}
/**
* 获取锁时可打断性测试
* 使用lock()方法获取锁是不可打断的
*
* 输出
* 2022-04-10T02:06:49.391Z | 1649556409411 | 1 | main | main 获取到了锁
* 2022-04-10T02:06:50.415Z | 1649556410415 | 12 | t1 | t1 线程执行方法
* 2022-04-10T02:06:50.415Z | 1649556410415 | 12 | t1 | t1 获取到了锁
* */
private static void test04() {
Thread thread = new Thread(() -> {
rl.lock();
MyTool.printTimeAndThread("t1 线程执行方法 ");
try {
MyTool.printTimeAndThread("t1 获取到了锁");
}finally {
rl.unlock();
}
}, "t1");
rl.lock();
MyTool.printTimeAndThread("main 获取到了锁");
thread.start();
MyTool.sleep(1);
try {
//执行打断操作
thread.interrupt();
}finally {
rl.unlock();
}
}
/**
* 可重入性测试:
* 可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁如果是不可重入锁,那么第二次获得锁时,自己也会被锁挡住
*
* 输出
* 2022-04-10T01:30:18.496Z | 1649554218561 | 1 | main | 执行了test01 方法...
* 2022-04-10T01:30:18.561Z | 1649554218561 | 1 | main | 执行了test02 方法...
* */
private static void test01() {
rl.lock();
try {
MyTool.printTimeAndThread("执行了test01 方法...");
test02();
} finally {
rl.unlock();
}
}
private static void test02() {
rl.lock();
try {
MyTool.printTimeAndThread("执行了test02 方法...");
} finally {
rl.unlock();
}
}
public static void test03() {
Thread thread = new Thread(() -> {
try {
rl.lockInterruptibly();
MyTool.printTimeAndThread("t1 线程执行方法 ");
} catch (InterruptedException e) {
MyTool.printTimeAndThread("t1 被打断 ");
e.printStackTrace();
return;
}
try {
MyTool.printTimeAndThread("获取到了锁");
} finally {
rl.unlock();
}
}, "t1");
rl.lock();
MyTool.printTimeAndThread("main 获取到了锁");
MyTool.sleep(1);
thread.start();
try {
//执行打断操作
thread.interrupt();
} finally {
rl.unlock();
}
}
}
条件变量await() 与 signalAll()/signal()
package com.yanxb.method.othermethod;
import com.yanxb.util.MyTool;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* 测试Reentrant类条件变量类
* 条件变量:
* synchronized 中也有条件变量,就是 waitSet 阻塞队列,当条件不满足时进入 waitSet 等待
* ReentrantLock 的条件变量比 synchronized 强大之处在于,它是支持多个条件变量的,
* 这就好比synchronized 是那些不满足条件的线程都在一个阻塞队列里等待唤醒
* 而 ReentrantLock 支持多个阻塞队列,如可知设置为:有业务A的阻塞队列、有业务B的阻塞队列..
* 这些不同的条件变量都是通过ReentrantLock对象获取
*
* 使用要点
* 1.await 前需要获得锁
* 2.await 执行后,会释放锁,进入 conditionObject 等待
* 3.await 的线程被唤醒(或打断、或超时)取重新竞争 lock 锁
* 4.等待await和唤醒signalAll、signal操作都是通过条件变量控制的
* 5.竞争 lock 锁成功后,从 await 后继续执行
*
* */
public class TestReentrantCondition {
static ReentrantLock lock = new ReentrantLock();
static Condition waitCigaretteQueue = lock.newCondition();
static Condition waitbreakfastQueue = lock.newCondition();
static volatile boolean hasCigrette = false;
static volatile boolean hasBreakfast = false;
public static void main(String[] args) {
new Thread(() -> {
lock.lock();
try {
while (!hasCigrette){
MyTool.printTimeAndThread("条件不满足,进入条件变量中等待,并释放锁...");
try {
waitCigaretteQueue.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
MyTool.printTimeAndThread("条件满足,执行业务逻辑...");
}finally {
lock.unlock();
}
},"t1").start();
new Thread(() -> {
lock.lock();
try {
while (!hasBreakfast){
MyTool.printTimeAndThread("条件不满足,进入条件变量中等待,并释放锁...");
try {
waitbreakfastQueue.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
MyTool.printTimeAndThread("条件满足,执行业务逻辑...");
}finally {
lock.unlock();
}
},"t2").start();
MyTool.sleep(1);
send01();
MyTool.sleep(1);
send02();
}
static void send01(){
lock.lock();
try {
MyTool.printTimeAndThread("改变t1线程中的判断条件");
hasCigrette = Boolean.TRUE;
//唤醒t1对应条件变量中的所有线程
waitCigaretteQueue.signalAll();
}finally {
lock.unlock();
}
}
static void send02(){
lock.lock();
try {
MyTool.printTimeAndThread("改变t2线程中的判断条件");
hasBreakfast = Boolean.TRUE;
//唤醒t1对应条件变量中的单个线程(这里只是记录下唤醒条件变量里线程的API,一个或多个时的不同API,没有实际意义)
waitbreakfastQueue.signal();
}finally {
lock.unlock();
}
}
}