![](https://img.haomeiwen.com/i3816895/62fd8fd1ab07cbde.png)
本文主要整理了关于多线程的五个demo,以demo来说明Java中多线程的演进之路,写法上是如何从繁到简的,这样更易于理解复杂场景下的多线程。
第一个demo
说到Java中的多线程,小伙伴们肯定想到了Thread类和Runnable接口,要实现多线程,可以继承Thread类,也可以实现Runnable接口;打开Thread类的源码,可以发现Thread类本身就是实现Runnable接口的。
现在写两个类,分别演示一下多线程。第一个类实现了Runnable接口:
/**
* 通过实现Runnable接口实现多线程
* @author 程就人生
* @date 2019年11月11日
*/
public class DemoByRunable implements Runnable{
private int i;
DemoByRunable(int i){
this.i = i;
}
@Override
public void run() {
System.out.println("当前第" + i + "个线程;");
}
/**
* 测试
* @param argo
*
*/
public static void main(String[] argo){
DemoByRunable demo;
for(int i=0;i<10;i++){
demo = new DemoByRunable(i+1);
demo.run();
}
}
}
运行结果如下:
当前第1个线程;
当前第2个线程;
当前第3个线程;
当前第4个线程;
当前第5个线程;
当前第6个线程;
当前第7个线程;
当前第8个线程;
当前第9个线程;
当前第10个线程;
第二个类继承了Thread类:
/**
* 通过继承Thread类实现多线程
* @author 程就人生
* @date 2019年11月11日
*/
public class DemoByThread extends Thread{
private int i;
DemoByThread(int i){
this.i = i;
}
public void run() {
System.out.println("当前第" + i + "个线程;");
}
public static void main(String[] argo){
DemoByThread demo;
for(int i=0;i<10;i++){
demo = new DemoByThread(i+1);
demo.start();
}
}
}
运行结果如下:
当前第2个线程;
当前第6个线程;
当前第5个线程;
当前第4个线程;
当前第1个线程;
当前第8个线程;
当前第9个线程;
当前第3个线程;
当前第10个线程;
当前第7个线程;
从执行结果来看,实现接口的是按照顺序来的,继承类的是无序的。
很多时候,我们需要做的事情,是有分工的,一个线程就相当于一个分工,当几个线程都完成后,任务才能走完最后一步。其中的一个线程没有走完,其他的线程则需要等待。就拿最经典、最易懂的泡茶,来看看多线程的实现吧。
第二个demo
/**
*
* @author 程就人生
* @date 2019年11月11日
*/
public class JoinDemo {
//假设用时
public static final int SLEEP_GAP = 500;
//获取线程名称
public static String getCurThreadName() {
return Thread.currentThread().getName();
}
//烧热水的线程
static class HeatUpWarterThread extends Thread {
public HeatUpWarterThread() {
super("烧水-Thread");
System.out.println("烧水线程开始运行.....");
}
public void run() {
try {
System.out.println("洗好水壶");
System.out.println("灌上凉水");
System.out.println("放在火上");
//线程睡眠一段时间,代表烧水中
Thread.sleep(SLEEP_GAP);
System.out.println("水开了.....");
} catch (InterruptedException e) {
System.out.println(" 发生异常被中断.");
}
System.out.println("烧水线程,运行结束.");
}
}
//清洗的线程
static class WashThread extends Thread {
public WashThread() {
super("清洗-Thread");
System.out.println("清洗线程开始运行.....");
}
public void run() {
try {
System.out.println("洗茶壶");
System.out.println("洗茶杯");
System.out.println("拿茶叶");
//线程睡眠一段时间,代表清洗中
Thread.sleep(SLEEP_GAP);
System.out.println("洗完了......");
} catch (InterruptedException e) {
System.out.println(" 发生异常被中断.");
}
System.out.println("清洗线程,运行结束.");
}
}
//主线程
public static void main(String args[]) {
Thread hThread = new HeatUpWarterThread();
Thread wThread = new WashThread();
hThread.start();
wThread.start();
try {
// 合并烧水-线程
hThread.join();
// 合并清洗-线程
wThread.join();
Thread.currentThread().setName("主线程");
System.out.println("泡茶喝");
} catch (InterruptedException e) {
System.out.println(getCurThreadName() + "发生异常被中断.");
}
System.out.println(getCurThreadName() + " 运行结束.");
}
}
执行结果
烧水线程开始运行.....
清洗线程开始运行.....
洗好水壶
灌上凉水
放在火上
洗茶壶
洗茶杯
拿茶叶
洗完了......
清洗线程,运行结束.
水开了.....
烧水线程,运行结束.
泡茶喝
主线程 运行结束.
上面的代码,继承了thread类实现的多线程,哪个线程先完成还是后完成,都是随机的。两个线程的运行,需要相互的等,等都完成了,才能最终泡茶喝。每个线程的执行结果是无法获取到的,这是潜在的问题。
第三个demo
在Java1.5版本之后,添加了新的类FutureTask,位于java.util.concurrent包中,先看看如何实现,有没有什么改进之处。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* JavaFutureTask多线程演示示例
* @author 程就人生
* @date 2019年11月12日
*/
public class JavaFutureDemo {
//耗時
public static final int SLEEP_GAP = 500;
public static String getCurThreadName() {
return Thread.currentThread().getName();
}
static class HeatUpWarterThread implements Callable<Boolean>{
@Override
public Boolean call() throws Exception{
System.out.println("准备开始烧水的工作.....");
try {
System.out.println("洗好水壶");
System.out.println("灌上凉水");
System.out.println("放在火上");
// 线程睡眠一段时间,代表烧水中
Thread.sleep(SLEEP_GAP);
System.out.println("水开了");
} catch (InterruptedException e) {
System.out.println(" 发生异常被中断.");
return false;
}
System.out.println("烧水工作,运行结束.");
return true;
}
}
static class WashJob implements Callable<Boolean> {
@Override
public Boolean call() throws Exception {
System.out.println("准备开始清洗的工作.....");
try {
System.out.println("洗茶壶");
System.out.println("洗茶杯");
System.out.println("拿茶叶");
// 线程睡眠一段时间,代表清洗中
Thread.sleep(SLEEP_GAP);
System.out.println("洗完了");
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("清洗工作,发生异常被中断.");
return false;
}
System.out.println("清洗工作,运行结束.");
return true;
}
}
//主綫程
public static void main(String args[]) {
//構造燒水的线程
Callable<Boolean> hJob = new HeatUpWarterThread();
FutureTask<Boolean> hTask = new FutureTask<>(hJob);
Thread hThread = new Thread(hTask, "烧水-Thread");
//构造清洗的线程
Callable<Boolean> wJob = new WashJob();
FutureTask<Boolean> wTask = new FutureTask<>(wJob);
Thread wThread = new Thread(wTask, "清洗-Thread");
//开始工作
hThread.start();
wThread.start();
Thread.currentThread().setName("主线程");
try {
//返回烧水的结果
boolean hOk = hTask.get();
//返回清洗的结果
boolean wOk = wTask.get();
if (hOk && wOk) {
System.out.println("泡茶喝");
} else if (!hOk) {
System.out.println("烧水失败,没有茶喝了");
} else if (!wOk) {
System.out.println("杯子洗不了,没有茶喝了");
}
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(getCurThreadName() + "发生异常被中断.");
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println(getCurThreadName() + " 运行结束.");
}
}
运行结果:
准备开始烧水的工作.....
洗好水壶
灌上凉水
放在火上
准备开始清洗的工作.....
洗茶壶
洗茶杯
拿茶叶
水开了
烧水工作,运行结束.
洗完了
清洗工作,运行结束.
泡茶喝
主线程 运行结束.
通过实现Callable接口,在通过FutureTask,就可以获得每个线程的执行结果了,似乎方便了不少。还有更好用一点的,下面看一看google的Guava吧。
第四个demo
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
/**
* 使用google的Guava( 17.0版本) 演示多線程示例
* @author 程就人生
* @date 2019年11月12日
*/
public class GuavaFutureDemo {
public static final int SLEEP_GAP = 500;
public static String getCurThreadName() {
return Thread.currentThread().getName();
}
//燒水線程
static class HotWarterJob implements Callable<Boolean>{
@Override
public Boolean call() throws Exception{
System.out.println("准备开始烧水的工作.....");
try {
System.out.println("洗好水壶");
System.out.println("灌上凉水");
System.out.println("放在火上");
//线程睡眠一段时间,代表烧水中
Thread.sleep(SLEEP_GAP);
System.out.println("水开了");
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("发生异常被中断.");
return false;
}
System.out.println("烧水工作,运行结束.");
return true;
}
}
//清洗線程
static class WashJob implements Callable<Boolean> {
@Override
public Boolean call() throws Exception {
System.out.println("准备开始清洗的工作.....");
try {
System.out.println("洗茶壶");
System.out.println("洗茶杯");
System.out.println("拿茶叶");
//线程睡眠一段时间,代表清洗中
Thread.sleep(SLEEP_GAP);
System.out.println("洗完了");
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(" 清洗工作 发生异常被中断.");
return false;
}
System.out.println("清洗工作,运行结束.");
return true;
}
}
//泡茶线程,最後要執行的線程,掌握前兩個線程的執行結果
static class MainJob implements Runnable {
boolean warterOk = false;
boolean cupOk = false;
int gap = SLEEP_GAP / 10;
@Override
public void run() {
while (true) {
try {
Thread.sleep(gap);
System.out.println("閒來無事,读书中......");
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(getCurThreadName() + "发生异常被中断.");
}
if (warterOk && cupOk) {
drinkTea(warterOk, cupOk);
}
}
}
public void drinkTea(Boolean wOk, Boolean cOK) {
if (wOk && cOK) {
System.out.println("泡茶喝,茶喝完");
this.warterOk = false;
this.gap = SLEEP_GAP * 100;
} else if (!wOk) {
System.out.println("烧水失败,没有茶喝了");
} else if (!cOK) {
System.out.println("杯子洗不了,没有茶喝了");
}
}
}
public static void main(String args[]) {
//新起一个线程,作为泡茶主线程
MainJob mainJob = new MainJob();
Thread mainThread = new Thread(mainJob);
mainThread.setName("主线程");
mainThread.start();
//烧水的业务逻辑
Callable<Boolean> hotJob = new HotWarterJob();
//清洗的业务逻辑
Callable<Boolean> washJob = new WashJob();
//创建java 线程池
ExecutorService jPool = Executors.newFixedThreadPool(10);
//包装java线程池,构造guava 线程池
ListeningExecutorService gPool = MoreExecutors.listeningDecorator(jPool);
//提交烧水的业务逻辑,取到异步任务
ListenableFuture<Boolean> hotFuture = gPool.submit(hotJob);
//绑定任务执行完成后的回调,到异步任务
Futures.addCallback(hotFuture, new FutureCallback<Boolean>() {
public void onSuccess(Boolean r) {
if (r) {
mainJob.warterOk = true;
}
}
//失敗的處理
public void onFailure(Throwable t) {
System.out.println("烧水失败,没有茶喝了");
}
});
//提交清洗的业务逻辑,取到异步任务
ListenableFuture<Boolean> washFuture = gPool.submit(washJob);
//绑定任务执行完成后的回调,到异步任务
Futures.addCallback(washFuture, new FutureCallback<Boolean>() {
public void onSuccess(Boolean r) {
if (r) {
mainJob.cupOk = true;
}
}
//失敗的處理
public void onFailure(Throwable t) {
System.out.println("杯子洗不了,没有茶喝了");
}
});
}
}
来看看执行的结果:
准备开始烧水的工作.....
洗好水壶
灌上凉水
放在火上
閒來無事,读书中......
准备开始清洗的工作.....
洗茶壶
洗茶杯
拿茶叶
閒來無事,读书中......
閒來無事,读书中......
閒來無事,读书中......
閒來無事,读书中......
閒來無事,读书中......
閒來無事,读书中......
閒來無事,读书中......
閒來無事,读书中......
閒來無事,读书中......
水开了
烧水工作,运行结束.
洗完了
清洗工作,运行结束.
閒來無事,读书中......
泡茶喝,茶喝完
閒來無事,读书中......
在这个demo中,需要引入Guava的架包,版本还不能太高,这是值得注意的。
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>17.0</version>
</dependency>
第五个demo
最后一个,压轴大戏,如何把泡茶的多线程写的再简单、简洁易懂些,下面来看看。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* 线程池的使用示例
* @author 程就人生
* @date 2019年11月12日
*/
public class JavaFuturePoolDemo {
public static final int SLEEP_GAP = 500;
public static String getCurThreadName() {
return Thread.currentThread().getName();
}
//烧水线程
static class HotWarterJob implements Callable<Boolean> {
@Override
public Boolean call() throws Exception {
System.out.println("准备开始烧水的工作.....");
try {
System.out.println("洗好水壶");
System.out.println("灌上凉水");
System.out.println("放在火上");
//线程睡眠一段时间,代表烧水中
Thread.sleep(SLEEP_GAP);
System.out.println("水开了.......");
} catch (InterruptedException e) {
System.out.println(" 发生异常被中断.");
return false;
}
System.out.println(" 运行结束.");
return true;
}
}
//清洗线程
static class WashJob implements Callable<Boolean> {
@Override
public Boolean call() throws Exception {
System.out.println("准备开始清洗的工作.....");
try {
System.out.println("洗茶壶");
System.out.println("洗茶杯");
System.out.println("拿茶叶");
//线程睡眠一段时间,代表清洗中
Thread.sleep(SLEEP_GAP);
System.out.println("洗完了..........");
} catch (InterruptedException e) {
System.out.println("清洗工作,发生异常被中断.");
return false;
}
System.out.println("清洗工作,运行结束.");
return true;
}
}
public static void main(String args[]) {
Callable<Boolean> hJob = new HotWarterJob();//异步逻辑
Callable<Boolean> wJob = new WashJob();//异步逻辑
ExecutorService pool = Executors.newFixedThreadPool(10);
Future<Boolean> hTask = pool.submit(hJob);
Future<Boolean> wTask = pool.submit(wJob);
try {
//返回执行结果
boolean warterOk = hTask.get();
boolean cupOk = wTask.get();
Thread.currentThread().setName("主线程");
if (warterOk && cupOk) {
System.out.println("泡茶喝");
} else if (!warterOk) {
System.out.println("烧水失败,没有茶喝了");
} else if (!cupOk) {
System.out.println("杯子洗不了,没有茶喝了");
}
} catch (InterruptedException e) {
System.out.println(getCurThreadName() + "发生异常被中断.");
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println(getCurThreadName() + " 运行结束.");
//关闭线程池,释放资源
pool.shutdown();
}
}
通过这五种线程写法的整理,对多线程的理解是不是清晰多了,特别是最后一种方法写起来很简单;代码呢,还是需要反复体会的,最好多加练习,熟能生巧。
参考资料:
https://www.cnblogs.com/crazymakercircle/p/9904544.html
网友评论