目录
一、多线程实现的方式
并发:在同一时刻,多个指令在单个cpu交替执行
并行:在同一时刻,多个指令在多个cpu同时执行
多线程的实现方式有三种:
1、继承Thread类的方式进行实现
首先自己定义一个类继承Thread,然后再重写run方法,创建子类对象并启动;
下面为自定义类MyThread继承Thread:
package src.com.threadcase01;
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName() + "Hello World");
}
}
}
下面为启动类:
package src.com.threadcase01;
public class ThreadDemo {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}
由运行结果可以观察到线程1和线程2交替输出,可以说明线程1和线程2交替运行;
2、实现Runnable接口的方式进行实现
首先定义一个类实现Runnab接口;重写里面的run方法;然后创建自己的类对象,最后创建Thread类对象,并且启动。
下面为创建的MyRun类实现Runnable接口;
package src.com.threadcase02;
public class MyRun implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() +"HelloWorld");
}
}
}
//Thread.currentThread()返回的是当前线程的对象
下面为自己的类对象:
package src.com.threadcase02;
public class ThreadDemo {
public static void main(String[] args) {
MyRun mr = new MyRun();
//创建线程对象
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}
运行结果依旧为线程1和线程2交替运行。
3、利用Callable接口和Future接口实现
首先要创建一个类MyCallable实现Callable接口;
然后重写call方法(有返回值,可以显示多线程的运行结果);
创建MyCallable的对象(表示多线程的要执行的任务);
创建FutureTask的对象(作用管理多线程运行的结果);
创建Thread对象,并启动;
如下为实现接口:
package src.com.threadcase03;
import java.util.concurrent.Callable;
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i <= 100; i++) {
sum += i;
}
return sum;
}
}
package src.com.threadcase03;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class ThreadDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建MyCallable的对象(表示多线程的要执行的任务)
MyCallable mc = new MyCallable();
//创建FutureTask的对象(作用管理多线程运行的结果)
FutureTask<Integer> ft = new FutureTask<>(mc);
//创建Thread对象
Thread t1 = new Thread(ft);
//启动
t1.start();
//获取返回值
Integer res = ft.get();
System.out.println(res);
}
}
4、对比总结
前两种多线程运行方式中的方法run()没有返回值,无法获取多线程运行的返回结果。
第三种方式可以获取多线程与逆行的结果。
优点 | 缺点 | |
继承Thread类 | 简单,可以直接使用Thread类中的方法 | 可扩展性差,不能再继承其他类 没有返回值 |
实现Runnable接口 | 扩展性强,实现该接口的同时还可以继承其他类 | 较为复杂,并且没有返回值 |
实现Callable接口 | 可以获取多线程与逆行的结果 扩展性强,实现该接口的同时还可以继承其他类 | 复杂 |
二、线程池
1、线程池原理
线程池是一种多线程处理形式。在处理过程中如果任务量大于最大线程数量,会将任务添加到缓存队列中等待。如果缓存队列已满,此时线程池就会有一种拒绝机制用来拒绝新添加的任务。
2、线程池优点
提高线程的利用率;
提高程序的响应速度;
便于统一管理线程对象;
可以控制最大并发数;
3、线程池参数
主要参数:
参数 | |
corePooISize | 线程池的常驻核心线程数;若线程池中的线程数大于此参数,就会将任务排放到缓存队列中。 |
maximumPooISize | 线程池的最大线程数;当缓存队列满时,且已创建的线程数小于此参数,线程池会创建新的线程来执行任务。 |
keepAliveTime | 空闲线程的存活时间;当非 常驻线程空闲时,空闲时间如果超过此参数时间,此线程就会被销毁。 |
unit | keepAliveTime的单位。 |
workQueue | 任务队列;用于传输和保存等待执行任务的阻塞队列 |
threadFactory | 线程工厂;用于创建新线程。 |
拒绝策略 | 用于拒绝任务,当任务大于线程池最大线程数+阻塞队列长度时,进行拒绝。 |
4、运行演示
package org.example;
import java.util.concurrent.*;
public class Test {
public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(3, 5, 1L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 9; i++) {
executorService.execute(()->{
System.out.println(Thread.currentThread().getName() + "===>任务");
});
}
executorService.shutdown();
}
}
运行结果
五个线程完成8个任务,由于任务量为9大于最大线程数+缓存队列长度,所以第九个任务被拒绝。