多线程理论知识
一、线程模型
用户线程和内核线程
1、多对一模型
多对一线程模型,又叫作用户级线程模型,即多个用户线程对应到同一个内核线程上,线程的创建、调度、同步的所有细节全部由进程的用户空间线程来处理
优点:
- 用户线程的很多操作对内核来说都是透明的,不需要用户态和内核态的频繁切换,使线程的创建、调度、同步等非常快。
缺点:
- 由于多个用户线程对应到同一个内核下线程,如果其中一个用户线程阻塞,那么该其他用户线程也无法执行。
- 内核并不知道用户态有哪些线程,无法像内核线程一样实现较完整的调度、优先级等。
场景:
- 许多语言实现的协程库基本上都属于这种方式,比如 python 的 gevent
2、一对一模型
一对一模型,又叫作**内核级线程模型 **,即一个用户线程对应一个内核线程,内核负责每个线程的调度、可以调度到其他处理器上面。
优点:
- 实现简单
缺点:
- 对用户线程的大部分操作都会映射到内核线程上,引起用户态和内核态的频繁转换;
- 内核为每个线程都映射调度实体,如果系统出现大量线程,会对系统性能有影响。
场景:
- Java使用的就是一对一线程模型,所以在Java中启动一个线程要谨慎。
3、多对多模型
多对多模型,又叫作两级线程模型,它是博采众长之后的产物,充分吸收前两种线程模型的优点且尽量规避他们的缺点。
在此模型下,用户线程与内核线程是多对多的映射模型,通常用户线程大于等于内核线程
首先,区别于多对一模型,多对多模型中的一个进程可以与多个内核线程关联,于是进程内的多个用户线程可以绑定不同的内核线程,这点和一对一模型相似;
其次,又区别于一对一模型,它的进程里的所有用户线程并不与内核线程一一绑定,而是可以动态绑定内核线程,当某个内核线程因为其绑定的用户线程的阻塞操作被内核调用让出CPU时,其关联的进行中其余用户线程可以重新与其他内核线程绑定运行;*
所以,多对多模型即不是多对一模型那么完全靠自己调度的,也不是一对一模型完全靠操作系统调度的,而是中间态(自身调度与系统调度协同工作),因为这种模型的高度复杂性,操作系统内核开发者一般不会使用,所以更多的时候是作为第三方库的形式出现。
优点:
- 兼具多对一的轻量
- 由于对应了多个内核线程,则一个用户阻塞时,其他用户线程仍然可以执行
- 由于对应了多个内核线程,则可以实现较完整的调度、优先级等
缺点:
- 实现复杂
场景:
- Go语言中的 goroutine 调度器就是采用的这种实现方案,在Go语言中一个进程可以启动成千上万个goroutine,这也是其出道以来就自带“高并发”光环的重要原因。
总结:
- 线程为用户线程和内核线程
- 线程模型又多度一模型、一对一模型、多对多模型
- 操作系统一般只实现了一对一模型
- Java使用的是一对一模型,所以它的一个线程对应于一个内核线程,调度完全交给操作系统来处理
- Go语言使用的是多对多线程模型,这也是高并发的原因,它的线程模型与Java中的ForkJoinPool非常类似
- Python的gevent使用的是多对一线程模型
参照:
二、线程创建的8种方式
1、继承Thread类
public class CreatingThread01 extends Thread {
@Override
public void run(){
System.out.println(getName() + "is running");
}
public static void main(String[] args) {
new CreatingThread01().start();
new CreatingThread01().start();
new CreatingThread01().start();
new CreatingThread01().start();
}
}
2、实现Runnable接口
public class CreatingThread02 implements RUnnable {
@Override
public void run(){
System.out.println(Thread.currentThread().getName() + "is running");
}
public static void main(String[] args) {
new Thread(new CreatingThread02()).start();
new Thread(new CreatingThread02()).start();
new Thread(new CreatingThread02()).start();
new Thread(new CreatingThread02()).start();
}
}
3、匿名内部类
public class CreatingThread03 {
public static void main(String[] args) {
// thread 匿名类,重写Thread的run() 方法
new Thread(){
@Override
public void run(){
System.out.println(getName() + "is running");
}
}.start();
// Runnable匿名类.实现其run()方法
new Thread(new Runnable(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "is running");
}
}).start();
// 同Runnable,使用lambda表达式函数式编程
new Thread(()->{
System.out.println(Thread.currentThread().getName() + "is running");
}).start();
}
}
4、实现Callabe接口
public class CreatingThread04 implements Callable<Long> {
@Override
public Long call() throws Exception {
Thread.sleep(2000L);
System.out.println(Thread.currentThread().getName() + "is running");
return Thread.currentThread().getId();
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<Long> task = new FutureTask<>(new CreatingThread04());
new Thread(task).start();
System.out.println("等待任务完成");
Long result = task.get();
System.out.println("任务结果:" + result);
}
}
5、定时器(java.util.Timer)
public class CreatingThread05 {
public static void main(String[] args) {
Timer timer = new Timer();
//每一秒执行一次
timer.schedule(new TimerTask(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "is running");
}
}, 0, 1000);
}
}
6、线程池
- 也叫螺纹池
public class CreatingThread06 {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(5);
IntStream().range(0, 100).forEach(i -> {
threadPool.execute(()->
System.out.println(Thread.currentThread().getName() + "is running");
);
});
}
}
7、并行计算(Java8 + )
public class CreatingThread07 {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
// 串行 ,打印结果为12345
list.stream().froEach(System.out::print);
System.out.println();
// 并行,打印结果随机,比如 52341
list.parallelStream().forEach(System.out.print);
}
}
8、@EnabeAsync+@Async注解
-
也称为弹簧异步方法
-
@EnableAsync、@Async是Spring支持的
// 为了方便用的SrpingBoot哈,反正主要能支持这个@EnableAsync注解节即可(Spring都支持)
@SpringBootApplication
@EnableASync //开启Async
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
@Service
public class CreatingThread08Service {
@Async
public void call() {
System.out.println(Thread.currentThread().getName() + "is running");
}
}
// 开始测试,和直接用Service方法一样
@RunuWith(SptringRunner.class)
@SpringBootTest(classes = Application.class)
public class CreatingThread08Test {
@Autowired
private CreatingThread08Service creatingThread08Service;
@Test
public void test() {
creatingThread08Servie.call();//task-3 is running;
creatingThread08Servie.call();//task-2 is running;
creatingThread08Servie.call();//task-1 is running;
creatingThread08Servie.call();//task-4 is running;
}
}
三、Java线程池-体系结构
结构图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-StcfExfy-1578881034130)(C:\Users\86134\AppData\Local\Temp\1578799027209.png)]
上面都是线程池中非常重要的接口和类
1、Executor:
-
线程池顶级接口
-
public interface Executor { void execute(Runnable command); }
2、ExecutorService:
-
线程池次级接口,对Executor做了一些扩展,增加了一些功能
-
主要增加了关闭线程池、执行有返回值的任务、批量执行任务的方法
-
public interface ExecutorService extends Executor { // 关闭线程池,不再接受任务,但是已经提交的会执行 void shutdown(); // 立即关闭线程池,尝试停止正在运行的任务,未执行的任务将不再执行 // 被强迫停止及未执行的任务将以列表的形式返回 List<Runnable> shutdownNow(); // 检测线程池是否已关闭 boolean isShutdown(); // 检测线程池是否已终止,只有在shutdown() 或 shutdownNow() 之后调用才有可能为true boolean isTerminated(); // 在指定时间内 线程达到终止状态了才会返回true boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException; // 执行有返回值的任务,任务的返回值为task.call()的结果 <T> Future<T> submit(Callable<T> task); // 执行有返回值的任务,任务的返回值为这里传入的result // 当然只有当任务执行完成了调用get() 时才会返回 <T> Future<T> submit(Runnable task, T result); // 执行有返回值的任务,任务的返回值为null // 当然只有当任务执行完成了调用 get() 时才会返回 Future<T> submit(Runnable task); // 批量执行任务,只要当这些任务都完成了这个方法才会返回 <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException; // 在执行时间内批量执行任务,未执行完成的任务将被取消 // 这里的timeout 是所有任务的总时间,不是单个任务的时间 <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException; // 返回任意一个已完成任务的执行结果,未执行完成的任务将被取消 <T> T invokeAny(Collection<? extends Callable<t>> tasks) throws InterruptedException, ExecutionException; // 在执行时间内如果有任务完成,则返回任意一个已完成任务的执行结果, // 未执行完成的任务将被取消 <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throw InterruptedException, ExecutionException, TimeoutException; }
3、ScheduledExecutorService:
-
对ExecutorService做了一些扩展,增加了一些定时任务相关的功能
-
包含2大类:执行一次,重复多次执行
-
public interface ScheduledExecutorService extends ExecutorService { // 在指定延时后执行一次 public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit); // 在指定延迟后执行一次 public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit); // 在指定延迟手开始执行,并在之后以指定=时间间隔=重复执行(间隔不包含任务执行时间) // 相当于之后的延时以==任务开始==计算 public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit); // 在指定延迟后开始执行,并在之后以指定是=延时=重复执行, // 相当于之后的延时以==任务结束==计算 public ScheduledFuture<?> scheduledWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit); }
4、AbstractExecutorService:
-
抽象类,运用模板方法设计模式实现了一部分方法
-
public abstract class AbstractExecutorService implements ExecutorService { /** * 实现了ExecutorService接口以下方法 */ public Future<?> submit(Runnable task); public <T> Future<T> submit(Runnable task, T result); public <T> Future<T> submit(Callable<T> task); public <T> T invokeAny(Collection<? extends Callable<T>> tasks); public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit); public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks); public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit); /** * 自己内部方法 */ protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) { return new FutureTask<T>(runnable, value); } protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { return new FutureTask<T>(callable); } }
5、ThreadPoolExecutor:
- 普通线程池类,也就是我们平时说的线程池类,包括最基本的线程池操作相关的方法实现;
- 线程池的主要实现逻辑都在这里面,比如线程的创建、任务的处理、拒绝策略等
6、ScheduledThreadPoolExecutor:
- 定时任务线程池类,用于实现定时任务相关功能
- 讲任务包装成定时任务,并按照定时策略来执行
7、ForkJoinPool:
- 新型线程池类,Java7中新增的线程池类,基于工作窃取理论实现,运用于大任务拆小任务、任务无限多的场景;
- 与Go中的线程模型特别雷士,都是基于工作窃取理论实现,特别适合处理归并排序这种先分后合的场景
8、Executors:
- 线程池工具类,定义了一些快速实现线程池方法
- 定义了一系列快速实现线程池的方法-------newXXX();不过阿里手册不建议使用这个类来新建线程池