目录
五、线程池的实现(java.util.concurrent.Executor接口)
一、继承Thread类
- 定义子类继承Thread类。
- 子类中重写Thread类中的run方法。
- 创建Thread子类对象,即创建了线程对象。
- 调用线程对象start方法:启动线程,调用run方法。
/**
* 1、继承thread类,thread类又实现了runnale接口,本质上是调用得runnable得run方法
*/
class Thread01 extends Thread {
@Override
public void run() {
System.out.println("1继承thread类创建线程方式");
}
}
Thread01 thread01 = new Thread01();
thread01.start();
二、实现runnabe接口
- 定义子类,实现Runnable接口。
- 子类中重写Runnable接口中的run方法。
- 通过Thread类含参构造器创建线程对象。
- 将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
- 调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法。
/**
* 2、实现Runable接口
* 相比第一种继承Thread类的方式,使用了面向接口,将任务与线程进行分离,有利于解耦
*/
class Thread02 implements Runnable {
@Override
public void run() {
System.out.println("2实现Runable接口创建线程方式");
}
}
Thread thread02 = new Thread(new Thread02());
thread02.start();
三、实现Callable接口
- 定义子类,实现Callable接口。
- 子类中重写Callable接口中的call方法。
- 通过Thread类含参构造器创建线程对象。
- 将Callable接口的子类对象作为实际参数传递给Thread类的构造器中。
- 调用Thread类的start方法:开启线程,调用Callable子类接口的call方法。
class Thread04 implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("4实现Callable接口创建线程方式");
return 1010;
}
//创建callable接口实现类的对象
Thread04 thread04 = new Thread04();
//将此callable接口实现类的对象作为传递到futuretask构造器中,创建futuretask的对象
FutureTask<Integer> futureTask = new FutureTask<Integer>(thread04);
//将futuretask的对象作为参数传递到thread类的构造器中,创建thread对象,并调用start()
new Thread(futureTask).start();
Integer callValue = futureTask.get();
System.out.println("callnable返回结果:"+callValue);
Callable与使用Runnable相比:
- 相比run()方法,可以有返回值
- 方法可以抛出异常
- 支持泛型的返回值
- 需要借助FutureTask类,比如获取返回结果
四、匿名内部类
适用于创建启动线程次数较少的环境,书写更加简便
//方式1:相当于继承了Thread类,作为子类重写run()实现
new Thread("BB") {
@Override
public void run() {
System.out.println("3-1匿名内部类创建线程方式");
}
}.start();
//方式2:实现Runnable,Runnable作为匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("3-2匿名内部类创建线程方式");
}
}, "AA").start();
//方式3:可以取替原来的匿名类实现多线程的方式
new Thread(() -> {
System.out.println("3-3lambda创建线程方式");
}, "AA").start();
五、线程池的实现(java.util.concurrent.Executor接口)
降低了创建线程和销毁线程时间开销和资源浪费,提高响应速度,便于线程管理
public static void main(String[] args) { //创建一个具有固定线程数(6)的线程池
ExecutorService pool = Executors.newFixedThreadPool(6);
//使用Lambda表达式创建Runnable对象
Runnable target = () -> { for(int i = 0 ; i < 100; i++){ System.out.println(Thread.currentThread().getName()+ " 的i值为:"+i); }
};
//向线程池提交两个线程
pool.submit(target);
pool.submit(target); //关闭线程池
pool.shutdown();
}
JDK 5.0起提供了线程池相关API:ExecutorService 和 Executors
ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
- void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执行Runnable
- <T> Future submit(Callable task):执行任务,有返回值,一般又来执行Callable
- void shutdown() :关闭连接池
Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
- Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池
- Executors.newFixedThreadPool(n); 创建一个可重用固定线程数的线程池
- Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池
- Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
六、Java的调度方法
同优先级组成先到先出队列,先到先服务,使用时间片(一段时间换一个)策略。
对高优先级,使用优先调度的抢占式(高优先级的线程抢占CPU)策略。
1、线程的优先级
- 线程的优先级等级
- MAX_PRIORITY:10
- MIN _PRIORITY:1
- NORM_PRIORITY:5
- 涉及的方法
- getPriority() :返回线程优先值
- setPriority(int newPriority) :改变线程的优先级
- 说明
- 线程创建时继承父线程的优先级。
- 低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用。
2、线程的生命周期
在说生命周期前先看看关于线程的几种状态
- 新建: 当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态。
- 就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是没分配到CPU资源。
- 运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态, run()方法定义了线程的操作和功能。
- 阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出 CPU 并临时中止自己的执行,进入阻塞状态。
- 死亡:线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束。