继承Thread
实现Runnable接口
使用Callable和FutureTask
线程池
创建一个多线程需要执行两个步骤,继承Thread类,创建一个新的线程类,比如命名为mythread类,重写run()方法,将需要并发执行的业务代码编写在run()方法中,那么启动线程的话,我们创建mythread类, 调用mythread.start()来启动线程就可以了。
将需要异步执行的业务逻辑代码写在Runnable实现类的run()方法中,再将Runnable实例作为target执行目标传入Thread实例,其完整步骤如下:定义一个新类实现Runnable接口,实现Runnable接口中的run()抽象方法,将线程代码逻辑写在该run()实现方法中,通过Thread类创建线程对象,将Runnable实例作为实际参数传递给Thread类的构造器,由Thread构造器将该Runnable实例赋值给自己的target执行目标属性,调用Thread实例的start()方法启动线程,线程启动之后,线程的run()方法将被JVM执行,该run()方法将调用target属性的run()方法,从而完成Runnable实现类中业务代码逻辑的并发执行 。
// 1、创建一个类实现Runnable接口
class Number implements Runnable{
// 2. 重写Runnable接口的run()
@Override
public void run() {
for (int i = 0; i <100 ; i++) {
if(i%2==0){
// System.out.println(i);
//输出线程名
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
public class ThreadRunnable {
public static void main(String[] args) {
// 3、创建实现Runnable接口类的对象
Number number=new Number();
// 4、将对象作为Thread类的参数
Thread thread=new Thread(number,"线程1");
thread.start();
}
}
继承Thread类用于多个线程并发完成各自的任务,访问各自的数据资源
实现Runnable接口用于多个线程并发完成同一个任务,访问同一份数据资源,数据共享资源需要使用原子类型或者进行线程同步控制
创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例(从java8开始可以直接使用Lambda表达式创建Callable对象)。
使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值
使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)
调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
class cal implements Callable{
private int num=0;
@Override
public Integer call() throws Exception {
for (int i = 0; i <100 ; i++) {
if(i%2==0){
// System.out.println(i);
//输出线程名
System.out.println(Thread.currentThread().getName()+":"+i);
num+=i;
}
}
return num;
}
}
public class CallableThread {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 将接口Callable实现类的对象作为FutureTask的参数
FutureTask futureTask=new FutureTask(new cal());
// 将FutureTask对象作为Thread的参数
new Thread(futureTask, "窗口1").start();
// 还可以获取Callable中call方法的返回值
System.out.println("num的值:"+futureTask.get());
}
}
首先我们了解一下线程池:
什么是线程池?
线程池就是管理一系列线程的资源池。当有任务要处理时,直接从线程池中获取线程来处理,处理完之后线程并不会立即被销毁,而是等待下一个任务。
使用线程池的好处:
ThreadPoolExecutor executor = new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAX_POOL_SIZE,
KEEP_ALIVE_TIME,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(QUEUE_CAPACITY),
new ThreadPoolExecutor.CallerRunsPolicy());
for (int i = 0; i < 10; i++) {
//创建WorkerThread对象(WorkerThread类实现了Runnable 接口)
Runnable worker = new MyRunnable("" + i);
//执行Runnable
executor.execute(worker);
}
//终止线程池
executor.shutdown();
主要是四类线程池
FixedThreadPool
SingleThreadExecutor
CachedThreadPool
ScheduledThreadPool
// 1、提供指定的线程数量的线程池
ExecutorService executorService= Executors.newFixedThreadPool(10);
// ThreadPoolExecutor service=(ThreadPoolExecutor) executorService;
// // 设置线程池的属性
// service.setCorePoolSize(15);
executorService.execute(new MyThread1()); // 执行Runnable接口的实现类
executorService.execute(new MyThread2()); // 执行Runnable接口的实现类
Future submit = executorService.submit(new MyThread3());// 执行Callable接口的实现列
submit()可以接收两种入参:无返回值的Runnable类型的target执行目标实例和有返回值的Callable类型的target执行目标实例,execute()只接收无返回值的target执行目标实例或者无返回值的Thread实例
submit()有返回值,execute()没有返回值
submit()方法在提交异步target执行目标之后会返回Future异步任务实例,以便对target的异步执行过程进行控制,比如取消执行、获取结果等。execute()没有任何返回,target执行目标实例在执行之后没有办法对其异步执行过程进行控制,只能任其执行,直到其执行结束
线程池必须手动通过 ThreadPoolExecutor
的构造函数来声明,避免使用Executors
类创建线程池,会有 OOM 风险。
Executors
返回线程池对象的弊端如下:
FixedThreadPool
和SingleThreadExecutor
:使用的是无界的LinkedBlockingQueue
,任务队列最大长度为 Integer.MAX_VALUE
,可能堆积大量的请求,从而导致 OOM。
CachedThreadPool
:使用的是同步队列 SynchronousQueue
, 允许创建的线程数量为 Integer.MAX_VALUE
,可能会创建大量线程,从而导致 OOM。
ScheduledThreadPool
和 SingleThreadScheduledExecutor
: 使用的无界的延迟阻塞队列DelayedWorkQueue
,任务队列最大长度为 Integer.MAX_VALUE
,可能堆积大量的请求,从而导致 OOM。
说白了就是:使用有界队列,控制线程创建数量。
除了避免 OOM 的原因之外,不推荐使用 Executors
提供的两种快捷的线程池的原因还有:
默认的拒绝策略有以下 4 种:
AbortPolicy:拒绝并抛出异常。
RejectedExecutionHandler.rejectedExecution()
方法。
了解完线程池后,我们对比前面几种方法,他们创建的Thread实例在执行完成之后是不可复用的,实际工作中需要对已创建好的线程实例进行复用,需要用到线程池。
线程的复用是通过while循环实现的,worker会首先获取当前的firstTask进行run,然后不停的循环从等待队列中获取新的任务task,如果有新任务则直接调用task的run方法,不会再去新建一个线程,从而实现复用。源码如下:
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // 释放锁 设置work的state=0 允许中断
boolean completedAbruptly = true;
try {
//一直执行 如果task不为空 或者 从队列中获取的task不为空
while (task != null || (task = getTask()) != null) {
task.run();//执行task中的run方法
}
}
completedAbruptly = false;
} finally {
//1.将 worker 从数组 workers 里删除掉
//2.根据布尔值 allowCoreThreadTimeOut 来决定是否补充新的 Worker 进数组 workers
processWorkerExit(w, completedAbruptly);
}
}