public class VolatileTest {
//定义一个共享变量用来线程间通信,注意用volatile修饰,保证它内存可见
static volatile boolean flag = false;
static double year;
public static void main(String[] args) {
//线程A,练习唱跳rap
Thread threadA = new Thread(() -> {
while (true) {
if (!flag) {
for (year = 0.5; year <= 5; year += 0.5) {
System.out.println("开始练习唱跳rap:已练习" + year + "年");
try {
Thread.sleep(288);
} catch (InterruptedException e) {
e.printStackTrace();
}
//众所周知,练习两年半即可出道
if (year == 2.5) {
System.out.println("===========================>练习时长两年半,出道!!!");
// 通知threadB你可以执行了
flag = true;
//同样留意这个break
break;
}
}
break;
}
}
});
//线程B,练习打篮球
Thread threadB = new Thread(() -> {
while (true) {
// 监听flag
if (flag) {
System.out.println("开始练习打篮球");
break;
}
}
});
threadA.start();
threadB.start();
}
}
结果与上面第一个一样,就不展示了
关于break,我这里先不说,你先猜猜结果,再复制demo去跑跑,一定要动手
synchronized、wait()、notify()三件套
wait() 和 notify()都是Object类的通讯方法,注意一点,wait和 notify需搭配synchronized使用,注意,notify不会释放锁,至于不会释放锁体现在哪儿,这个demo下面有说明
public class SynchronizedTest {
static double year;
public static void main(String[] args) {
SynchronizedTest sync= new SynchronizedTest();
sync.execute();
}
public void execute() {
//线程A,练习唱跳rap
Thread threadA = new Thread(() -> {
synchronized (this) {
for (year = 0.5; year <= 5; year += 0.5) {
try {
System.out.println("开始练习唱跳rap:已练习" + year + "年");
Thread.sleep(288);
if (year == 2.5) {
System.out.println("===========================>练习时长两年半,出道!!!");
//唤醒等待中的threadB,但threadB不会立马执行,而是等待threadA执行完,因为notify不会释放锁
notify();
break;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
//线程B,练习打篮球
Thread threadB = new Thread(() -> {
synchronized (this) {
try {
wait();
System.out.println("开始练习打篮球");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
//注意,一定要先启动B,不然会导致B永远阻塞
threadB.start();
threadA.start();
}
}
这个threadA里面的break一定要多想想,跑一跑你就知道啥叫不会释放锁
如果没有break,threadA在唤醒threadB后,会继续执行自己的逻辑,等自己执行完了才会释放锁,这时候threadB才开始执行
基于ReentrantLock
ReentrantLock是juc包下的并发工具,也能实现,但相对复杂,需结合Condition的await和signal,底层原理有点像上面的wait和notify
这里留个课后作业:思考一下为什么unlock要放在finally里面?
public class ReentrantLockTest {
static double year;
public static void main(String[] args) {
//实例化一个锁和Condition
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
//线程A,练习唱跳rap
Thread threadA = new Thread(() -> {
lock.lock();
try {
for (year = 0.5; year <= 5; year += 0.5) {
System.out.println("开始练习唱跳rap:已练习" + year + "年");
Thread.sleep(288);
//众所周知,练习两年半即可出道
if (year == 2.5) {
System.out.println("===========================>练习时长两年半,出道!!!");
//唤醒等待中的线程
condition.signal();
//这里的break也是个彩蛋,去掉它触发隐藏关卡
break;
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//解锁
lock.unlock();
}
});
//线程B,练习打篮球
Thread threadB = new Thread(() -> {
lock.lock();
try {
//让当前线程等待
condition.await();
System.out.println("开始练习打篮球");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
});
//必须保证B先拿到锁,不然会导致A永远阻塞
threadB.start();
threadA.start();
}
}
基于CountDownLatch
这也是juc包下的并发工具,主要有两个常用方法,countDown和await
简单说下原理:CountDownLatch底层维护了一个计数器count,在实例化的时候设置,当调用countDown方法时,count减一,如果count在减一前已经为0,那么什么都不会发生,如果减一后变成0,则唤醒所有等待的线程;await方法会使当前线程等待,直到count为0
public class CountDownLatchTest {
static double year;
public static void main(String[] args) {
//实例化一个CountDownLatch,count设置为1,也就是说,只要调用一次countDown方法就会唤醒线程
CountDownLatch latch = new CountDownLatch(1);
//线程A,练习唱跳rap
Thread threadA = new Thread(() -> {
for (year = 0.5; year <= 5; year += 0.5) {
System.out.println("开始练习唱跳rap:已练习" + year + "年");
try {
Thread.sleep(288);
} catch (InterruptedException e) {
e.printStackTrace();
}
//众所周知,练习两年半即可出道
if (year == 2.5) {
System.out.println("===========================>练习时长两年半,出道!!!");
//计数器减一
latch.countDown();
//老规矩,去掉break触发隐藏关卡
break;
}
}
});
//线程B,练习打篮球
Thread threadB = new Thread(() -> {
try {
//阻塞当前线程,计数器为0时被唤醒
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("开始练习打篮球");
});
threadA.start();
threadB.start();
}
}
打完收工
罗师傅镇楼
上面五个demo要是都看懂了的话,你对多线程这块也算是比较熟了,恭喜!!!
最后
一次偶然,从朋友那里得到一份“java高分面试指南”,里面涵盖了25个分类的面试题以及详细的解析:JavaOOP、Java集合/泛型、Java中的IO与NIO、Java反射、Java序列化、Java注解、多线程&并发、JVM、Mysql、Redis、Memcached、MongoDB、Spring、Spring Boot、Spring Cloud、RabbitMQ、Dubbo 、MyBatis 、ZooKeeper 、数据结构、算法、Elasticsearch 、Kafka 、微服务、Linux。
这不,马上就要到招聘季了,很多朋友又开始准备“金三银四”的春招啦,那我想这份“java高分面试指南”应该起到不小的作用,所以今天想给大家分享一下。
请注意:关于这份“java高分面试指南”,每一个方向专题(25个)的题目这里几乎都会列举,在不看答案的情况下,大家可以自行测试一下水平 且由于篇幅原因,这边无法展示所有完整的答案解析
lasticsearch 、Kafka 、微服务、Linux。
这不,马上就要到招聘季了,很多朋友又开始准备“金三银四”的春招啦,那我想这份“java高分面试指南”应该起到不小的作用,所以今天想给大家分享一下。
[外链图片转存中…(img-tvPvGJR0-1714524596653)]
请注意:关于这份“java高分面试指南”,每一个方向专题(25个)的题目这里几乎都会列举,在不看答案的情况下,大家可以自行测试一下水平 且由于篇幅原因,这边无法展示所有完整的答案解析