Java多线程学习--从浅到深

蜀州凯哥 2022-09-23 09:06:53 阅读数:890

java学习多线程线程浅到

目录

一.多线程基础
1.实现多线程的方法:
2.callable返回值使用什么接收:
3.简单线程用到的方法:
4.线程的生命周期
二.线程池
1.使用工具类创建线程池-Executors
2.使用ThreadPoolExecutor创建线程池--推荐
3.Executor 和 ExecutorService 的区别
收起目录
一.多线程基础

1.实现多线程的方法:

.继承Thread类

.实现Runnable接口-重写run方法

.实现Callable接口-重写call方法

2.callable返回值使用什么接收:

.Future - 接口

.FutureTask - 类,可以new出来,传进去的callable的call方法只会执行一次。

3.简单线程用到的方法:

8149bee6eae24fb7879d1ad6e421657d.png
①sleep(long millis): 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行) 

②join():指等待t线程终止。

③yield():暂停当前正在执行的线程对象,并执行其他线程。

④setPriority(): 更改线程的优先级。

.MIN_PRIORITY = 1

.NORM_PRIORITY = 5

.MAX_PRIORITY = 10

⑤interrupt():不要以为它是中断某个线程!它只是线线程发送一个中断信号,让线程在无限等待时(如死锁时) 能抛出异常,从而结束线程,但是如果你吃掉了这个异常,那么这个线程还是不会中断的!

⑥wait()

⑦.notify()

⑧.notifyall()

4.线程的生命周期

.新建,就绪,运行,阻塞,死亡。

.新建:new 就绪:start 运行:run 阻塞:sleep,阻赛io,等待通知notify

二.线程池

1.使用工具类创建线程池-Executors

//创建固定大小的线程池
//核心线程数:参数
//最大线程数:参数
//线程存活时间:0L(TimeUnit.MILLISECONDS毫秒)
//队列选择:LinkedBlockingQueue  
ExecutorService pool = Executors.newFixedThreadPool(5);

//创建缓存线程池
//核心线程数:0
//最大线程数:2147483647(Integer.MAX_VALUE)
//线程存活时间:60L(TimeUnit.SECONDS秒)
//队列选择:SynchronousQueue
ExecutorService pool = Executors.newCachedThreadPool();

//创建单个线程的线程池
//核心线程数:1
//最大线程数:1
//线程存活时间:0L(TimeUnit.MILLISECONDS毫秒)
//队列选择:LinkedBlockingQueue
ExecutorService pool = Executors.newSingleThreadExecutor();

//创建延迟或定时线程的线程池
//核心线程数:参数
//最大线程数:2147483647(Integer.MAX_VALUE)
//线程存活时间:0L(TimeUnit.NANOSECONDS纳秒)
//队列选择:DelayedWorkQueue
ExecutorService pool = Executors.newScheduledThreadPool(5);

//创建单个延迟或定时线程的线程池
//核心线程数:1
//最大线程数:2147483647(Integer.MAX_VALUE)
//线程存活时间:0L(TimeUnit.NANOSECONDS纳秒)
//队列选择:DelayedWorkQueue
ExecutorService pool = Executors.newSingleThreadScheduledExecutor();

//JDK1.8
//创建线程很耗时的线程池
//核心线程数:10
//最大线程数:0x7FFFFFFF = 2147483647(Integer.MAX_VALUE)最大整型数int
//线程存活时间:未发现
//队列选择:未发现
ExecutorService pool = Executors.newWorkStealingPool(10);
2.使用ThreadPoolExecutor创建线程池--推荐

//创建单个延迟或定时线程的线程池
//核心线程数:2
//最大线程数:5
//线程存活时间:60L(TimeUnit.SECONDS秒)
//队列选择:ArrayBlockingQueue
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,5,60L, TimeUnit.SECONDS, new ArrayBlockingQueue(10));
ThreadPoolExecutor参数说明

public ThreadPoolExecutor(int corePoolSize,

int maximumPoolSize,

long keepAliveTime,

TimeUnit unit,

BlockingQueue<Runnable> workQueue,

ThreadFactory threadFactory,

RejectedExecutionHandler handler) {

if (corePoolSize < 0 ||

maximumPoolSize <= 0 ||

maximumPoolSize < corePoolSize ||

keepAliveTime < 0)

throw new IllegalArgumentException();

if (workQueue == null || threadFactory == null || handler == null)

throw new NullPointerException();

this.corePoolSize = corePoolSize;

this.maximumPoolSize = maximumPoolSize;

this.workQueue = workQueue;

this.keepAliveTime = unit.toNanos(keepAliveTime);

this.threadFactory = threadFactory;

this.handler = handler;

}

.corePoolSize

核心线程数。在创建线程池之后,默认情况下线程池中并没有任何的线程,而是等待任务到来才创建线程去执行任务,当线程池中的线程数目达到 corePoolSize后,新来的任务将会被添加到缓存队列中,也就是那个workQueue,除非调用 ThreadPoolExecutor#prestartAllCoreThreads() 方法或者是 ThreadPoolExecutor # prestartCoreThread() 方法(从这两个方法的名字就可以看出是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或一个线程)。

PS:很多人不知道这个数该填多少合适,其实也不必特别纠结,根据实际情况填写就好,实在不知道,就按照阿里工程师的写法取下列值就好了:

int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();

.maximumPoolSize

线程池中的最大线程数。表示线程池中最多可以创建多少个线程,很多人以为它的作用是这样的:”当线程池中的任务数超过 corePoolSize 后,线程池会继续创建线程,直到线程池中的线程数小于maximumPoolSize“,其实这种理解是完全错误的。它真正的作用是:当线程池中的线程数等于 corePoolSize 并且 workQueue 已满,这时就要看当前线程数是否大于 maximumPoolSize,如果小于maximumPoolSize 定义的值,则会继续创建线程去执行任务, 否则将会调用去相应的任务拒绝策略来拒绝这个任务。另外超过 corePoolSize的线程被称做"Idle Thread", 这部分线程会有一个最大空闲存活时间(keepAliveTime),如果超过这个空闲存活时间还没有任务被分配,则会将这部分线程进行回收。

.keepAliveTime

控制"idle Thread"的空闲存活时间。这个idle Thread就是上面提到的超过 corePoolSize 后新创建的那些线程,默认情况下,只有当线程池中的线程数大于corePoolSize,且这些"idle Thread"并没有被分配任务时,这个参数才会起作用。另外,如果调用了 ThreadPoolExecutor#allowCoreThreadTimeOut(boolean) 的方法,在线程池中的线程数不大于corePoolSize,且这些core Thread 也没有被分配任务时,keepAliveTime 参数也会起作用。

.unit

参数keepAliveTime的时间单位,共7种取值,在TimeUtil中定义:

TimeUnit.DAYS; //天

TimeUnit.HOURS; //小时

TimeUnit.MINUTES; //分钟

TimeUnit.SECONDS; //秒

TimeUnit.MILLISECONDS; //毫秒

TimeUnit.MICROSECONDS; //微妙

TimeUnit.NANOSECONDS; //纳秒

.workQueue

阻塞队列。如果当前线程池中的线程数目>=corePoolSize,则每来一个任务,会尝试将其添加到该队列当中,注意只要超过了 corePoolSize 就会把任务添加到该缓存队列,添加可能成功也可能不成功,如果成功的话就会等待空闲线程去执行该任务,若添加失败(一般是队列已满),就会根据当前线程池的状态决定如何处理该任务(若线程数 < maximumPoolSize 则新建线程;若线程数 >= maximumPoolSize,则会根据拒绝策略做具体处理)。

常用的阻塞队列有:

1)ArrayBlockingQueue //基于数组的先进先出队列,此队列创建时必须指定大小;

2)LinkedBlockingQueue //基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;

3)synchronousQueue //这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。

.threadFactory

线程工厂。用来为线程池创建线程,当我们不指定线程工厂时,线程池内部会调用Executors.defaultThreadFactory()创建默认的线程工厂,其后续创建的线程优先级都是Thread.NORM_PRIORITY。如果我们指定线程工厂,我们可以对产生的线程进行一定的操作。

.handler

拒绝执行策略。当线程池的缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:

ThreadPoolExecutor.AbortPolicy: // 丢弃任务并抛出RejectedExecutionException异常。

ThreadPoolExecutor.DiscardPolicy: // 也是丢弃任务,但是不抛出异常。

ThreadPoolExecutor.DiscardOldestPolicy: // 丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)

ThreadPoolExecutor.CallerRunsPolicy: // 由调用线程处理该任务

3.Executor 和 ExecutorService 的区别

0a48e134d65b4760a113cb4197032b92.png 

版权声明:本文为[蜀州凯哥]所创,转载请带上原文链接,感谢。 https://blog.csdn.net/m0_72088858/article/details/127003376