1. The traditional way Thread.join()
/**
* Start threads in the traditional way , Perform tasks , Achieve blocking
*
* @see Thread#join()
*
*/
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
log.info("do something in Runnable");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
log.info("is executing");
Thread.sleep(1000);
// Let the thread stop , wait for thread Thread execution complete , Achieve blocking
thread.join();
log.info("Normal thread is done");
log.info(Thread.currentThread().getName() + ": is done");
}
defects :
- You need to get the object of the thread to call Thread.join(), If the current thread needs to wait 10 Threads , The code becomes very complex
- Unable to define and get the return value of thread
2. Thread pool + AQS Components :CountDownLatch, Do not get the return value
2.1 Wait for a thread
public static void main(String[] args) {
// Initial value , Leave child threads to decrement
CountDownLatch countDownLatch = new CountDownLatch(1);
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(new Runnable() {
@Override
public void run() {
log.info("runnable executed");
// First execution CountDownLatch It will be reduced to 0, amount to await() Only wait for the thread to execute once ;
countDownLatch.countDown();
}
}
// Let the main thread block
countDownLatch.await();
}
2.2 Waiting for multiple threads
Use while The loop blocks the current thread
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
// Notice that the count is 100 within , because for Cycle is 100 Time
CountDownLatch countDownLatch = new CountDownLatch(100)
Runnable runnable = new Runnable() {
@Override
public void run() {
// It's the first time I've executed this sentence CountDownLatch The value of a 99, The first 100 It's just the same as 0
// If the value is taken as 101 Even larger , The main thread is blocked forever , because CountDownLatch At least for 101-100 = 1, It's not a wake-up rule
countDownLatch.countDown();
log.info(countDownLatch.getCount() + " ");
}
};
for (int i = 0; i < 100; i++) {
executorService.submit(runnable);
}
// When CountDownLatch The value of is 0, await() The state is awakened , The main thread continues to execute
countDownLatch.await();
log.info("main thread is done");
executorService.shutdown();
}
defects :
- CountDownLatch Object only maintains the decrement of the counter , Can’t recycle
- It needs to be clear that there are several threads to execute before blocking
- Unable to define and get the return value of thread
Compared with the traditional way :
- Use a global counter , Implement a thread waiting for multiple threads
- Delegate a thread blocked task to CountDownLatch To achieve
3. AQS Components FutureTask + Callable / Runnable, Get the return value
3.1 Preliminary knowledge – Source code
- FutureTask Construction method of
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW;
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result); // The interior will Runnable Package as Callable
this.state = NEW;
}
- packing Runnable by Callable
Pass in Runnable Construction method of , It’s actually using Callable Characteristics of – Provide return value
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}
executorService.submit()
The return value is Future<> , What the runtime generates is FutureTask The object of
3.2 Use FutureTask + Runnable Implementation waiting for multiple threads , And get the thread return value
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Runnable runnable = new Runnable() {
@Override
public void run() {
log.info(" ");
}
};
// You can use linked lists to optimize , The next example optimizes
Stack<Future> stack = new Stack<>();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10; i++) {
Future<String> result = executorService.submit(runnable, "this result only instanceof String");
stack.push(result);
}
while (!stack.isEmpty()) {
String result = (String) stack.pop().get();
sb.append(result);
}
log.info(sb.toString());
log.info("main thread is done");
executorService.shutdown();
}
- defects
Runnable Use executorService.sumbit() You need to specify a return value of string type
Runnable Can only handle String Return value of type , And it’s a unified process , The return values of all threads are written dead when the thread pool is received
Need to use Callable To solve the problem of return value expansion
FutureTask.get()
It’s blocked waiting for the target thread to get the return value , When you get the return value , Thread execution completed
3.3 Use FutureTask + Callable Implementation waiting for multiple threads , And get the thread return value
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool();
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
log.info("callable is executing");
return "result N ";
}
};
LinkedList<Future> list = new LinkedList<>();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10; i++) {
Future<String> result = executorService.submit(callable);
list.addLast(result);
}
while (list.size() != 0) {
String o = (String) list.removeFirst().get();
sb.append(o);
}
log.info("result :{}", sb);
log.info("main thread is done");
executorService.shutdown();
}