前言 特点分析
线程池ThreadPool 销毁线程池
Executor类 Callable接口
线程池使用 Callable启动线程
ExecutorService接口的方法
前言
在上一篇Java—实现多线程程序 | 入门的文章中,我们初步了解Thread类的用法
第一次书写多线程程序,算是完成一个Java学习的里程碑
接下来我们继续进入多线程的学习
线程池ThreadPool
线程池:线程的缓冲池,目的就是提高效率。 new Thread().start(),线程是内存中的一个独立的方法栈区,JVM没有能力开辟内存空间和OS交互
而线程会频繁地与OS交互浪费资源,线程池就是开辟一个空间,专门用于存储线程对象,可以理解为集合,当线程需要时,就从池子里调用出来,使用结束再丢回线程池,保证了线程不会“死掉”,反复利用,达到高效
线程池的目的是为了提高线程的利用率,减少创建和销毁线程的开销,降低系统资源的消耗,提高系统的稳定性和可靠性。在高并发的环境中,线程池可以有效地控制并发数量,防止线程因互相竞争而产生死锁、饥饿或者系统资源耗尽等问题。
tips:后面学到的数据库连接池也是类似的效果
jdk5开始内置线程池
Executor类
使用静态方法创建线程池
public static newFixedThreadPool(int nThreads)
创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。
int nThreads是传入线程的数量
方法的返回值是ExecutorService接口的实现类,管理池子里面的线程
线程池使用
创建线程池, 线程数量是两个
线程数量虽然可以自行定义,但最好不要过多,浪费资源
ExecutorService es = Executors.newFixedThreadPool(2);
创建线程对象,线程池管理对象Service,调用方法submit提交线程任务,当然,前提是得先写一个Runnable
public class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("线程开始运行");
}
}
ExecutorService接口的方法
submit(Runnable r)提交线程执行的任务
public class ThreadTest {
public static void main(String[] args) {
ExecutorService es = Executors.newFixedThreadPool(2);
Runnable runnable = new MyRunnable();
es.submit(runnable);
}
}
特点分析
这一次运行代码不知道你们是否发现了不同之处
以往的代码运行时,程序会自动结束不再运行,而这次你可以看到,程序一直在后台保活,直到我们手动关闭。这就是上文所说线程池的作用:用则拿,不用则还,线程不死
这里的中文乱码比较懒,没有调整,将就着看哈哈,如果你也遇到了中文乱码的问题而没有找到合适的方法,可以看我的另一篇文章,我想它会对你有所帮助⬇⬇⬇解决IntelliJ IDEA 代码运行时中文出现乱码
同时,为了更直观地看到线程的执行,我们还可以查看一下线程的名字,然后把runnab对象多submit几次
System.out.println(Thread.currentThread().getName()+"线程开始运行");
public class ThreadTest {
public static void main(String[] args) {
ExecutorService es = Executors.newFixedThreadPool(2);
Runnable runnable = new MyRunnable();
es.submit(runnable);
es.submit(runnable);
es.submit(runnable);
es.submit(runnable);
es.submit(runnable);
}
}
销毁线程池
当我们不需要使用线程池时,总得让它结束,所以我们就可以把它销毁,自行结束
es.shutdown();
此时我们再运行程序,当所有线程不再使用时,运行到这行代码,线程池就被销毁了
Callable接口
Callable也能像Runnable一样建线程,但和Runnable接口有区别
Callable接口有返回值,且Callable的call方法可以抛出异常(Runnable没有)
Callable的抽象方法只有一个:call
我们尝试使用一下相关代码
public class MyCall implements Callable<V> {
public V call() throws Exception {
return null;
}
}
注意此处的<V>为泛型,即你需要返回数据的类型,此处我们可以改成String,然后return字符串
public class MyCall implements Callable<String> {
public String call() throws Exception {
return "返回字符串";
}
}
Callable启动线程
线程调用重写方法call,需要使用到线程池,我们依旧可以使用submit,把submit(Runnable r)改为submit(Callable c)提交线程执行的任务,但当我们运行时没有任何输出结果
这时候我们就要使用Future接口,Future submit()方法提交线程任务后,方法有个返回值Future接口类型,而它则可以获取到线程执行后返回的结果
public class CallText {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建线程池
ExecutorService es = Executors.newFixedThreadPool(2);
//创建线程
MyCall myCall = new MyCall();
//提交线程任务,使用Callable接口实现类
Future<String> future = es.submit(myCall);//返回接口类型Future
//接口的方法get,获取线程的返回值
String str = future.get();
System.out.println("str = "+str);
}
}