基本方法
在做一些稍微复杂的业务时,常常会用到多线程,使用多线程就会涉及到线程的等待、通知以及线程之间的通信,java中的线程怎么做到呢,下面开始讲解
基本用到以下这些方法
thread.join(),
object.wait(),
object.notify(),
CountdownLatch,
CyclicBarrier,
FutureTask,
Callable 。
场景1、线程依次执行
在主线程中,假如有两个线程 A、B,想要确保让A执行完,再执行B,那就可以使用最简单的方法
threadA.start();
threadA.join();
threadB.start();
只需要线程A调用join()方法
场景2、线程通知后再执行
同样有A、B两个线程,等A干完了一件事,再等B干完,B干完再通知A,让A继续干。
创建Object作为公共锁
Object lock = new Object();
Thread A = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
System.out.println("A的第一件事 执行完了");
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("A的第二件事");
}
}
});
Thread B = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
System.out.println("B:我开始干了");
lock.notify(); //执行notify,线程A就重新拾取锁钥匙,继续干
}
}
});
A.start();
B.start();
场景3、线程同步运行,全部结束再继续执行
啥意思呢,场景1中方法,会等待A执行完,再执行B,那B才开始运行,那要是有A、B、C、D……n个线程,这样效率不就大大降低,怎么才能让所有线程同时开始,并等所有线程结束后再继续,这时需要CountDownLatch。
int worker = 2; 假如总共就2个线程A、B
CountDownLatch countDownLatch = new CountDownLatch(worker);
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我在等其他线程全部干完");
try {
countDownLatch.await();
System.out.println("其他都结束了, 我开始干了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
//线程
Thread A = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("A:我干完了");
countDownLatch.countDown();
}
});
Thread B = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("B:我也干完了");
countDownLatch.countDown();
countDownLatch相当于是个倒计数器,本身worker是2,当A调用countDown(); 就剩1
B又调用一次,线程没完成的已经为0了,此时await()地方就重新开始
}
});
A.start();
B.start();
场景4、线程同步运行,并确保是一起开始的
当有一些特殊需求,举个例子,有A、B、C三个运动员,他们要比赛50米,那么会有个预备动作,或者说是预热运动,等待他们各就各位之后,裁判员说跑,那么他们才开始,那么线程是如何实现这样的骚操作,靠CyclicBarrier,CyclicBarrier和CountDownLatch很像,CountDownLatch是递减,CyclicBarrier则是递增。
创建这个等待器,worker依然是2
CyclicBarrier cyclicBarrier = new CyclicBarrier(2);
Thread A = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);
cyclicBarrier.await();
System.out.println("A");
} catch (Exception e1) {
}
}
});
Thread B = new Thread(new Runnable() {
@Override
public void run() {
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
} catch (BrokenBarrierException e) {
}
System.out.println("B");
}
});
A.start();
B.start();
此时的A需要5秒钟准备,而B不会开始,会等A调用await()之后,A、B才一起开始
场景5、获取线程回调结果
有时候需要线程去算一些东西,或者处理一些任务,而主线程中只需要拿到结果,再往下执行,相对于场景1中join(),不同的是能获取到回调结果。
1、创建任务处理方法
Callable<Integer> callable = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println("任务开始");
int result = 100;
return result;
}
};
2、新建这个么回调任务
FutureTask<Integer> futureTask = new FutureTask<>(callable);
new Thread(futureTask).start();
try {
System.out.println("执行任务前");
System.out.println("执行结果:" + futureTask.get());
System.out.println("获取结果后");
} catch (Exception e) {
e.printStackTrace();
}
此时调用的get()方法会阻塞主线程,然后 Callable 内部开始执行,并返回运算结果,
得到结果后主线程继续执行。
当然,如果你不希望阻塞主线程,可以考虑利使用用线程池之类的东西,比如ExecutorService,
把FutureTask 放到线程池去管理执行