//线程2
Thread t2 = new Thread(() -> {
hello();
});
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(“主函数执行完毕。。。。。。。。。”);
}
结果如下:
为什么join(),可以实现线程同步呢?join()源码如下:
很明显,这里使用while做了循环等待,让线程不往下执行,达到线程同步(等待)的效果。
然而我们平时的开发过程中基本不会这么创建线程,一般都是使用线程池,那在使用线程池的情况下如何让线程实现同步呢?
我们先试试自己写一个方法让它实现同步,代码如下:
public static void hello(){
String name = Thread.currentThread().getName();
try {
System.out.println(“线程:”+name+" 休眠开始。。。。。。。。。。。。");
Thread.sleep(1000);
System.out.println(“线程:”+name+" 休眠结束。。。。。。。。。。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
// 计数器初始化为2
AtomicInteger count = new AtomicInteger(2);
executor.execute(() ->{
hello();
count.decrementAndGet();
});
executor.execute(() ->{
hello();
count.decrementAndGet();
});
//等待两个线程执行完毕
while(count.get() != 0){
}
System.out.println(“我是在两个线程执行之后才执行的内容”);
}
count:用于统计线程执行的数量,线程执行-1;AtomicInteger:原子类,可以在多线程中保证共享变量的安全。
decrementAndGet:自减并返回自减以后的结果(原子操作)。
while:线程同步的重点:这里主要是让主线程处于循环状态,直到count被减为0,也就意味着两个子线程都已执行完毕。
但是我不推荐这么做,为什么呢?因为java SDK给我们提供了现成的方法,我们为啥还要自己去手动实现呢?下面我们就来看看 CountDownLatch是如何实现线程同步:
// 创建2个线程的线程池
private static Executor executor = Executors.newFixedThreadPool(2);
public static void hello(){
String name = Thread.currentThread().getName();
try {
System.out.println(“线程:”+name+" 休眠开始。。。。。。。。。。。。");
Thread.sleep(1000);
System.out.println(“线程:”+name+" 休眠结束。。。。。。。。。。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
//这里需要注意一点,那就是实例化CountDownLatch的初始大小,一定要和你需要等待线程的数量相同,
//小了会导致等待的线程提前执行。
//大了会导致线程一直处于无限循环当中
CountDownLatch countDownLatch = new CountDownLatch(2);
executor.execute(() ->{
hello();
countDownLatch.countDown();
});
executor.execute(() ->{
hello();
countDownLatch.countDown();
});
//等待两个线程执行完毕
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(“我是在两个线程执行之后才执行的内容”);
}
为了效果明显,我特意在hello方法让线程休眠1秒。
countDownLatch:实现线程同步的关键,实例化一个需要等待的线程数量
countDownLatch.countDown():等待线程数-1。
countDownLatch.await();让主线程处于等待状态,直到等待的线程被减为0(注意:这里必须要做异常捕获线程中断的异常:(InterruptedException);
上面代码结果如下:
这里需要注意一点:CountDownLatch的初始大小是不会被重置的,所以使用这个解决方案的时候需要手动重置CountDownLatch线程等待的初始大小。
实现原理:
其实查看源码,他的实现方式和我之前使用的while类似,他这里用了for的无限循环,直到等待的线程被减为0;
那有没有不需要重新设置线程等待的工具类呢?肯定是有的,那就是接下来要说的:CyclicBarrier
CyclicBarrier
主要通过线程回调来实现线程等待,这里的实现方式稍微做了一下修改:
// 创建3个线程的线程池,其中一个线程用于回调处理主线程的事情
private static Executor executor = Executors.newFixedThreadPool(3);
public static void hello(){
String name = Thread.currentThread().getName();
try {
System.out.println(“线程:”+name+" 休眠开始。。。。。。。。。。。。");
Thread.sleep(1000);
System.out.println(“线程:”+name+" 休眠结束。。。。。。。。。。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
//这里需要注意一点,那就是实例化CountDownLatch的初始大小,一定要和你需要等待线程的数量相同,
//小了会导致等待的线程提前执行。
//大了会导致线程一直处于无限循环当中
final CyclicBarrier barrier = new CyclicBarrier(2, ()->{ executor.execute(()->printAfter()); });
executor.execute(() ->{
hello();
try {
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
});
executor.execute(() ->{
hello();
try {
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
});
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
最后
光给面试题不给答案不是我的风格。这里面的面试题也只是凤毛麟角,还有答案的话会极大的增加文章的篇幅,减少文章的可读性
Java面试宝典2021版
最常见Java面试题解析(2021最新版)
2021企业Java面试题精选
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
链图片转存中…(img-gGuFm40x-1713412369227)]
最常见Java面试题解析(2021最新版)
[外链图片转存中…(img-oOKbq4Ib-1713412369227)]
[外链图片转存中…(img-oZHhxmJL-1713412369227)]
2021企业Java面试题精选
[外链图片转存中…(img-8D78YawP-1713412369227)]
[外链图片转存中…(img-YIKkpeh8-1713412369228)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!