总结:使用实现多线程有四种方式:
1.继承Thread类;
2.实现Runnable接口;
3.使用Callable和FutureTask实现有返回值的多线程;
4.使用ExecutorService和Executors工具类实现线程池(如果需要线程的返回值,需要在线程中实现Callable和Future接口)
public class MyThread extends Thread {
private int threadId;
public MyThread(int threadId) {
this.threadId = threadId;
}
@Override
public void run() {
//代码逻辑
System.out.println("Hello, I'm thread " + threadId);
}
}
public class Main {
public static void main(String[] args) {
MyThread thread1 = new MyThread(1);
MyThread thread2 = new MyThread(2);
thread1.start();
thread2.start();
}
}
@Test
public void testRunableThread() {
List<Address> addressList = addressService.list();
//计算器,初始值是线程总数,依次递减,当等于0时,全部任务完成
CountDownLatch latch = new CountDownLatch(addressList.size());
for (Address address : addressList) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
address.setList(liverService.list(Wrappers.<Liver>lambdaQuery().eq(Liver::getAddressId, address.getId())));
//每次一个线程任务完成-1
latch.countDown();
}
});
thread.start();
}
try {
//主线程等待latch==0时执行
latch.await();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("addressList的数据为:::" + addressList);
}
注:CountDownLatch是Java的一个多线程同步工具,它可以用来协调多个线程的执行。CountDownLatch本身是线程安全的,因为它内部使用了原子操作和线程安全的机制。
CountDownLatch是一个计数器, 初始值为线程的总数
CountDownLatch.countDown();该方法每次都会 CountDownLatch-1 操作
CountDownLatch.await(); 该方法所在线程会等 CountDownLatch=0 时才会执行后续操作
@Test
public void testallableThread() {
List<Address> addressList = addressService.list();
//计算器,初始值是线程总数,依次递减,当等于0时,全部任务完成
CountDownLatch latch = new CountDownLatch(addressList.size());
List<FutureTask<List<Liver>>> taskList = new ArrayList<>(addressList.size());
for (Address address : addressList) {
FutureTask<List<Liver>> futureTask = new FutureTask<>(new Callable<List<Liver>>() {
@Override
public List<Liver> call() {
List<Liver> liverList = liverService.list(Wrappers.<Liver>lambdaQuery().eq(Liver::getAddressId, address.getId()));
address.setList(liverList);
//每次一个线程任务完成-1
latch.countDown();
return liverList;
}
});
taskList.add(futureTask);
Thread thread = new Thread(futureTask);
thread.start();
}
CompletableFuture.supplyAsync(new Supplier<Object>() {
@Override
public Object get() {
return null;
}
});
try {
//主线程等待latch==0时执行
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
List<Liver> liverList = new ArrayList<>();
for (FutureTask<List<Liver>> futureTask : taskList) {
List<Liver> livers = new ArrayList<>();
try {
//获取每条线程返回结果
livers = futureTask.get();
} catch (Exception e) {
e.printStackTrace();
} finally {
liverList.addAll(livers);
}
}
System.out.println("addressList的数据为:::" + addressList);
System.out.println("liverList的数据为:::" + liverList);
}
}
注:thread默认参数时runable类型,所以如果需要使用thread运行callable,可以使用FutureTask封装一下
FutureTask底层继承了runable接口接口和获取callable执行结果Future接口
public class ThreadPool {
// 最大线程数
public static final int THREAD_POOL_SIZE = 20;
// 创建指定线程数量的线程池
// ExecutorService ExecutorService = Executors.newFixedThreadPool(10);
/**
* 创建线程工厂,自定义线程命名前缀
*/
public static ThreadFactory namedThreadFactory() {
return new ThreadFactoryBuilder().setNameFormat("order-runner-%d").build();
}
/**
* 创建线程池,其中任务队列需要结合实际情况设置合理的容量
* newFixedThreadPool 创建指定线程线程池
* newFixedThreadPool 创建指定线程线程
*/
public static ExecutorService MyThreadPoolExecutor(int nThreads) {
return new ThreadPoolExecutor(
// 核心线程数
nThreads,
// 最大线程数
THREAD_POOL_SIZE,
// 空闲线程存活时间
0L,
// 空闲线程存活时间单位
TimeUnit.SECONDS,
// 任务队列
new LinkedBlockingQueue<>(),
//线程名称
namedThreadFactory()
);
}
@Test
public void testCompletionThread() {
List<Address> addressList = addressService.list();
//获取线程池
ExecutorService executorService = ThreadPool.MyThreadPoolExecutor(addressList.size());
//ExecutorCompletionService来并发获取任务结果
ExecutorCompletionService<List<Liver>> completionService = new ExecutorCompletionService<>(executorService);
for (Address address : addressList) {
completionService.submit(new Callable<List<Liver>>() {
@Override
public List<Liver> call(){
List<Liver> liverList = liverService.list(Wrappers.<Liver>lambdaQuery().eq(Liver::getAddressId, address.getId()));
address.setList(liverList);
return liverList;
}
});
}
List<Liver> liverList = new ArrayList<>();
try {
for (int i = 0; i < addressList.size(); i++) {
//所有线程完成之后使用ExecutorCompletionService.take方法获取结果集
Future<List<Liver>> future = completionService.take();
List<Liver> livers = future.get();
liverList.addAll(livers);
}
} catch (Exception e) {
e.printStackTrace();
}
executorService.shutdown();
System.out.println("addressList数据为:::" + addressList);
System.out.println("liverList数据为:::" + liverList);
}
//executorService为线程池对象
ExecutorCompletionService<String> completionService = new ExecutorCompletionService<>(executorService);
// 获取已完成的任务
Future<String> future = completionService.take();
// 获取任务结果
String str = future.get();
System.out.println(str);
注:在此实例中使用ExecutorCompletionService非阻塞获取返回值,ExecutorCompletionService可以用来并发获取结果集
- 阻塞获取结果:take()方法是阻塞的,它会等待直到有任务完成并返回已完成的Future对象。如果队列中没有已完成的任务,则会一直阻塞。
- 非阻塞获取结果:poll()方法可以立即返回一个已完成的Future对象,如果队列中没有已完成的任务,则返回null。
@Test
public void testThreadPool() {
List<Address> addressList = addressService.list();
ExecutorService executorService = ThreadPool.MyThreadPoolExecutor(addressList.size());
//计算器,初始值是线程总数,依次递减,当等于0时,全部任务完成
CountDownLatch latch = new CountDownLatch(addressList.size());
List<FutureTask<List<Liver>>> list = new ArrayList<>(addressList.size());
for (Address address : addressList) {
FutureTask<List<Liver>> futureTask = new FutureTask<>(new Callable<List<Liver>>() {
@Override
public List<Liver> call() {
List<Liver> liverList = liverService.list(Wrappers.<Liver>lambdaQuery().eq(Liver::getAddressId, address.getId()));
address.setList(liverList);
//每次一个线程任务完成-1
latch.countDown();
return liverList;
}
});
executorService.submit(futureTask);
list.add(futureTask);
}
List<Liver> liverList = new ArrayList<>();
try {
//主线程等待latch==0时执行
latch.await();
for (FutureTask<List<Liver>> futureTask : list) {
List<Liver> livers = futureTask.get();
liverList.addAll(livers);
}
} catch (Exception e) {
e.printStackTrace();
throw new CustomException(e.getMessage());
}
System.out.println("addressList数据为:::" + addressList);
System.out.println("liverList数据为:::" + liverList);
}
区别和使用场景 runable和callable的区别在于runable是没有返回值的,callable有返回值
使用线程池时,如果需要不需要获取返回结果使用runable调用run方法即可,如果需要获取返回值则使用callable调用call方法获取返回值
线程池executorService.sumit()方法 提交任务到线程池,并可以获取执行结果,一般搭配callable使用
线程池executorService.execute()方法 也是让线程池执行任务,但是没有办法获取返回值,一般搭配runable使用