作为Java出身的老码农实现多线程成为一个必备技能,今天盘点一下实现多线程的方法,首先从使用的线程池类型来分有如下这些类别的多线程实现。
可缓存线程池
/**
* 工作线程的数量几乎没有限制,最大线程数不能超过INT最大值,即可灵活的往线程池中添加线程
* 若长时间未往线程池中提交任务,即现成空闲的时长大道默认1分钟,则该现成将自动终止。终止后若有新的任务提交到线程池会自动创建新的线程
* 由于对线程数量限制比较宽松,则需要我们在使用该类线程池的过程中需要谨慎控制任务的数量,以免由于启用过多的线程运行导致系统瘫痪
*
*/
public class CachedThreadPollSample {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
//给以上创建的线程池创建100个任务进去,当0,99打印完毕之后,我们会发现控制台还未自动关闭,需要等待一段时间才会关闭,正是因为”可缓冲线程池“的
//特性,当线程空闲之后会等待默认的一分钟时段若还未有任务进入,将自动结束
for (int i = 0; i < 100; i++) {
final int index = i;
executorService.execute(()-> System.out.println("当前线程:"+Thread.currentThread()+"------"+index));
System.out.println("线程池大小:"+((ThreadPoolExecutor)executorService).getPoolSize());
}
}
}
固定大小线程池
/**
* FixedThreadPool指定线程池大小,即线程数量是固定的,如果提交的任务数量超过了线程池的大小,则会将任务存入池队列(阻塞队列)中
* 该类型的线程池完全达到了节省线程创建与销毁时间的效果。当然缺点就是,当线程空闲的适合不会自动结束进行销毁依然会占用系统资源
*
*/
public class FixedThreadPoolSample {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
//创建100个任务进入该线程池,我们会发现当0-99数字输出结束之后,只要我们不手动关闭控制台,则控制台会一直处于开启状态,不进行关闭,正是
//因为该类线程池是固定大小的线程池,即使线程空闲的时候也不会自动释放线程资源
//同时我们会根据打印出来的线程池大小看到线程池的大小是随着任务的增多递增直至线程数量达到指定的线程池大小10则不再进行增加
for (int i = 0; i < 100; i++) {
final int index = i;
executorService.execute(()-> System.out.println("当前线程:"+Thread.currentThread()+"------"+index));
System.out.println("线程池大小:"+((ThreadPoolExecutor)executorService).getPoolSize());
}
}
}
定长线程池
/**
* 定长线程池(newScheduleThreadPool)
* 可定时以及周期性的执行任务
*
*/
public class ScheduleThreadPoolSample {
public static void main(String[] args) {
//定义可容纳5个线程的定长线程池,即同时可执行五个定时任务
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
//以上定义了线程池大小为5的定长线程池,同时我们下面定义了100个任务延迟10秒执行并打印出时间差,理论时间差应该是10秒左右
final long timestample = System.currentTimeMillis();
for (int i = 0; i < 100; i++) {
final int index = i;
executorService.schedule(()->{
System.out.println(Thread.currentThread()+"================任务"+index+"==========="+(System.currentTimeMillis()-timestample));
System.out.println("线程池大小:"+((ThreadPoolExecutor)executorService).getPoolSize());
},10, TimeUnit.SECONDS);
}
}
}
单一线程
/**
* 单线程化的Executor,只会创建一个线程进行执行任务
* 可保证任务是按顺序执行
*/
public class SingleThreadExecutorSample {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
//放100个任务进入以上所创建的线程池,我们会发现在控制台依次打印出0-99的数字
//且线程大小一直处于1个,同时也不会自动释放
for (int i = 0; i < 100; i++) {
final int index = i;
executorService.execute(()->{
System.out.println("当前线程:"+Thread.currentThread()+"----------"+index);
});
}
}
}
再来从获取异步线程池获取任务执行结果方面,说说线程池执行任务的两种方式:
使用CountDownLatch
public class ThreadPoolSampleCountDownLatch {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(5);
AtomicReference<String> res = new AtomicReference<>("");
CountDownLatch countDownLatch = new CountDownLatch(1);
executorService.submit(() -> {
res.set(new ThreadPoolSampleCountDownLatch().print());
countDownLatch.countDown();
});
countDownLatch.await();
// returns null if the task has finished correctly.
System.out.println(res.get());
}
public String print() {
return "ssss";
}
}
使用Future
使用Future获取任务执行结果则显得更加简洁。
public class ThreadPoolSample {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(5);
// Future 对象可以用来检查 Runnable 是否已经执行完毕。
Future future = executorService.submit(() -> "sss");
try {
// returns null if the task has finished correctly.
System.out.println(future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}