java-多线程、线程池
什么是线程
- 进程:并发执行的程序在一个数据集合上的执行过程,它由进程控制块、程序段、数据段3部分组成。进程之间的存储空间各自独立,一个进程可以包含若干线程。
- 线程:在进程中并发地处理事件,和所属进程使用同一个存储空间,他有就绪、阻塞、运行3种状态。线程之间共享存储空间,线程可以创建和撤销另一个线程。
什么是多线程
假如你有一堆砖头,单线程情况下一个人去搬,多线程就是将砖头分多份,多个人同时搬。多线程是并发的,因此处理一件事情很快。
线程的3种状态
一个线程就是一个搬砖工人,在一个人搬砖的情况下:
- 就绪:搬砖的人准备好开工了。
- 阻塞:搬砖人类趴下了,就没人干了,这件事就停止了,也就是阻塞了。
- 停止:砖搬完了,不用干了
阻塞是说单个线程的,多线程中每个线程都存在各自的3种状态。
3种:就绪、阻塞、停止
5种:新建、就绪、运行、阻塞、停止
7种:新建、就绪、运行、等待、阻塞、超时等待、停止
多线程的3种方式
你要执行的方法,然后我们使用不同方式来多线程执行。
public class Method{
public void run() {
System.out.println("方法1");
}
}
1.继承Thread
线程类,实现父类 Thread
public class Thread1 extends Thread {
//将调用方法的类传入线程类
private Method method;
public Thread1 (Method method){
this.method=method;
}
//Thread.run来调用类方法
@Override
public void run() {
method.run();
}
}
创建线程:实际上调用Thread的start方法执行。
public static void main(String[] args) {
//创建3个线程
new Thread1(new Method()).start();
new Thread1(new Method()).start();
new Thread1(new Method()).start();
}
2.实现Runnable接口
public class Thread2 implements Runnable {
//传入方法类
private Method method;
public Thread2(Method method){
this.method=method;
}
//使用接口的run方法来调用执行的方法
@Override
public void run() {
method.run();
}
}
测试
还是使用了Thread.start().
public static void main(String[] args) {
//创建3个线程
new Thread(new Thread2(new Method())).start();
new Thread(new Thread2(new Method())).start();
new Thread(new Thread2(new Method())).start();
}
分析Runnable接口方式:
首先你的线程类实现了Runnable接口,然后查看Thread类源码,发现它也是实现Runnable接口的。
此时我们进入:new Thread( Runnable runnable ),发现它调用了init方法,在init方法中将Runnable 类型的值交给了Thread的成员变量,简写如下:
public class Thread implements Runnable{ //将自定义的线程类传入 private Runnable target; public Thread(Runnable target){ this.target=target; } // 下面这一段是Thread类中的原内容 @Override public void run() { if (target != null) { target.run(); //调用自定义的线程类的run方法 } } }
如果你学过代理模式,你恐怕已经悟了,这不就是使用Thread类代理了你自定义的线程类吗?
同时可以知道:静态代理可以使继承父类变为实现接口。因为java不支持多继承,但支持多实现。
3.实现Callable<>接口
通常情况会使用Runnable来构建线程,但是它存在一个问题,就是无法获得返回值。
Callable接口可以返回值,泛型则是指返回值类型,即:Callable<返回值类型>.
线程类:
public class Thread3 implements Callable<String> {
private Method method;
public Thread3(Method method){
this.method=method;
}
@Override
public String call() throws Exception {
this.method.run();
return "线程创建成功!";
}
}
测试:
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> task1=new FutureTask<> (new Thread3(new Method1()));
FutureTask<String> task2=new FutureTask<> (new Thread3(new Method2()));
FutureTask<String> task3=new FutureTask<> (new Thread3(new Method3()));
new Thread(task1).start();
new Thread(task2).start();
new Thread(task3).start();
task1.get(); //线程1返回值
}
其实FutureTask是Runnable的实现类,也实现了run方法,只不过是由FutureTask类来调用你的任务类,并在run方法中调用并获取返回值。
java线程池
当我们使用多线程时,需要创建多个线程并销毁它们,这个过程会消耗内存,而有时候我们直接创建线程是杂乱无章的,这样一来就会增加无意义的消耗。因此我们需要有一个统一管理这些线程的工具,这个工具就叫做线程池。
- Executors :你可以理解为线程的工具类,为我们提供了很多写好的创建线程池的方法。
- Executor:一个接口,所有线程池的根接口,只有一个execute()方法来执行线程。
- ThreadPoolExecutor:创建线程的类,一般用它来创建自定义线程池,是Executor的实现类。
new ThreadPoolExecutor()
- 参数1:线程池最小线程数
- 参数2:线程池最大线程数
- 参数3:线程存活时间
- 参数4:存活时间的时间单位
- 参数5:工作队列对象
- 参数6:线程工厂,可缺省
- 参数7:阻塞时的处理程序,可缺省。
创建好线程池后,调用线程池的execute() 方法来执行一个线程任务,参数为Runnable类型,(创建线程的三种方法都是Runnable接口的实现)。
使用线程池来开启多线程:
上面可以发现Executors类种提供多种线程池,自定义时要传多个参数,因此我们先用默认的线程池试一试:
public class MainExecutor {
public static void main(String[] args) {
//使用Executors类的方法创建一个可以做定时任务的线程池
Executor executor= Executors.newCachedThreadPool();
//使用线程池启动3个线程
executor.execute(new Thread1(new Method1()));
executor.execute(new Thread1(new Method2()));
executor.execute(new Thread1(new Method3()));
}
}
线程池的种类还是要看队列是怎么用的。