关于线程的二三事

程序,进程,线程之间的关系

程序:一组指令的有序集合,是一个静态的实体。对应磁盘上的文件。

进程:一个动态的实体,有自己的生命周期。因创建二产生。因调度而运行。因等待资源或事件而被处于等待状态,因完成任务而被撤销。反应了一个程序运行的全部动态过程。

线程:线程是进程的一个实体。可以理解为进程的某一次行为。比如qq音乐就是一个程序,当他运行的时候就是一个进程,而里面的添加好友,添加歌曲就是一个个线程。

在一个进程中每个线程都自动绑定了一个栈内存。

创建线程的三种方法

继承Thread类,重写run方法

public  class myThread extends Thread{
   @Override
   public void run() {

   }
 }

  public static void main(String[] args) {
     myThread myThread = new myThread();
     myThread.start();
   }

  

实现runnable接口,重写run方法

 1 class MyThread implements Runnable{
 2    @Override
 3    public void run() {
 4      
 5    }
 6  }
 7 
 8    public static void main(String[] args) {
 9      Thread thread = new Thread(new MyThread());
10      thread.start();
11    }

 

实现callable接口,重写call方法

 1 class MyThread implements Callable{
 2    @Override
 3    public Object call() throws Exception {
 4      return null;
 5    }
 6  }
 7 
 8    public static void main(String[] args) {
 9      FutureTask futureTask = new FutureTask<>(new MyThread());
10      Thread thread = new Thread(futureTask);
11      thread.start();
12    }

线程的生命周期

 

新建:刚new出来的线程

就绪:调用.start()方法,线程准备工作做好了,等待获取cpu分配资源,当抢到cpu分配资源就运行

运行:当就绪的线程被调度获取cpu资源时,线程才属于运行状态

阻塞:在运行时因为其他原因,如sleep,wait将线程阻塞,只有等待唤醒并且重新获得cpu分配资源才能运行,阻塞和运行之间隔了一个就绪

死亡:线程正常执行完毕后或者线程被强行提前终止或者出现异常导致结束,线程释放资源,并销毁

什么是线程池

线程池:频繁的创建和销毁线程大大增加了系统的资源消耗,同时任务等待线程的创建也影响了系统响应速度,于是引出了线程池的概念。让线程池管理线程。当有任务执行时直接调用线程池里面的线程来执行任务,既减少的系统的资源消耗,也提高了系统响应速度,并且还能控制并发数量和执行顺序。

线程池的体系结构

java.util.concurrent.Executor:线程池的顶级接口,定义了线程池的最基本方法

 1 public interface Executor {
 2 
 3     /**
 4      * Executes the given command at some time in the future.  The command
 5      * may execute in a new thread, in a pooled thread, or in the calling
 6      * thread, at the discretion of the {@code Executor} implementation.
 7      *
 8      * @param command the runnable task
 9      * @throws RejectedExecutionException if this task cannot be
10      * accepted for execution
11      * @throws NullPointerException if command is null
12      */
13     void execute(Runnable command);
14 }

java.util.concurrent.ExecutorService:继承Executor,定义常用方法

 1 public interface ExecutorService extends Executor {
 2 
 3  
 4     void shutdown();
 5 
 6     List<Runnable> shutdownNow();
 7 
 8     boolean isShutdown();
 9 
10     boolean isTerminated();
11 
12     boolean awaitTermination(long timeout, TimeUnit unit)
13         throws InterruptedException;
14 
15     <T> Future<T> submit(Callable<T> task);
16 
17     <T> Future<T> submit(Runnable task, T result);
18 
19     Future<?> submit(Runnable task);
20 
21     <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
22         throws InterruptedException;
23 
24     <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
25                                   long timeout, TimeUnit unit)
26         throws InterruptedException;
27 
28     <T> T invokeAny(Collection<? extends Callable<T>> tasks)
29         throws InterruptedException, ExecutionException;
30 
31     <T> T invokeAny(Collection<? extends Callable<T>> tasks,
32                     long timeout, TimeUnit unit)
33         throws InterruptedException, ExecutionException, TimeoutException;
34 }

java.util.concurrent.ThreadPoolExecutor:继承了AbstractExecutorService,是线程池的核心实现类

java.util.concurrent.Executors:线程池的工具类,提供了创建各种线程池的方法

 工作中用线程池创建线程的方法

直接创建线程池

根据当前业务的不同可以直接创建线程池设置参数:

 1  public static void main(String[] args) {
 2 
 3      ThreadPoolExecutor ES = new ThreadPoolExecutor(int corePoolSize,
 4      int maximumPoolSize,
 5      long keepAliveTime,
 6      TimeUnit unit,
 7      BlockingQueue<Runnable> workQueue) {
 8        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
 9          Executors.defaultThreadFactory(), defaultHandler);
10    );
11      ES.execute(任务);
12 
13    }

int corePoolSize:核心工作线程的数量,即无论能不能获得到任务都不会关闭的线程
int maximumPoolSize:最大工作线程的数量,最大并发量就是由他控制
long keepAliveTime:除核心工作线程之外的线程被开启的保存时间,当超过这个时间为获取到任务会自动关闭
TimeUnit unit:时间的单位
BlockingQueue<Runnable> workQueue:存放任务的队列
RejectedExecutionHandler:饱和策略,即线程所有线程都在执行任务,而存放任务的队列全部饱满的情况下,有新的任务进入该如何处理。

线程池的三种队列

SynchronousQueue:无缓冲等待队列,是一个不储存元素的阻塞队列。会直接将任务交给消费者,必须等队列中添加的元素被消费后才能添加新的元素。一般要求maximumPoolSize为无界(Integer .MAX_VALUE),避免线程拒绝执行操作。

LinkedBlockingQueue:无界缓存等待队列,底层是一个链表实现。当当前执行的线程数量达到corePoolSize数量时,剩余的元素就会在阻塞在列中等待。这个时候的maximumPoolSize就相当于无效了。虽然说是无界但是可以人为指定队列大小,因为记录队列的大小的参数是int类型,所有通常意义上的无界其实就是队列长度的最大值Integer.MAX_VALUE。默认情况下也是Integer.MAX_VALUE

ArrayBlockingQueue:有界缓存等待队列,底层是一个数组。可以指定缓存队列的大小。当正在执行的线程数量等于corePoolSize时,多余的元素缓存在ArrayBlockingQueue队列中等待有空闲的线程时继续执行,当ArrayBlockingQueue已满时,加入ArrayBlockingQueue失败,会开启新的线程去执行,当线程数量已经达到最大线程maximuPoolSize,再有新的元素尝试加入的时候会报错。

线程池的四种饱和策略

AbortPolicy:默认饱和策略。在任务不能再提交的时候,抛出异常(RejectedExecutionException),及时反馈程序运行状态。如果是比较关键的业务,推荐使用此饱和策略,这样在系统不能承载更大的并发量的时候,能够及时通过异常发现

DiscardPolicy:丢弃任务,但是不抛出异常。如果线程队列已满,则后续提交的任务都会被抛弃并且没有任何异常抛出。一般无关紧要的任务可以使用此策略

DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务

CallerRunsPolicy:如果任务被拒绝,由调用线程直接执行此任务(假设你在一个线程A中调用线程池执行任务,使用此策略之后任务被拒绝会由当前线程即A线程执行此任务)

用线程池工具类创建线程

newCachedThreadPool

newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需求,可以灵活回收空闲线程,若无可回收,则新建线程。

1 public static ExecutorService newCachedThreadPool() {
2         return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
3                                       60L, TimeUnit.SECONDS,
4                                       new SynchronousQueue<Runnable>());
5     }

底层:返回ThreadPoolExecutor实例,corePoolSize为0,maximumPoolSize为Intger.MAX_VALUE;keepAlikeTime为60L;unit为TimeUnit.SECONDS;workQueue为SynchronousQueue(同步队列)

执行过程:当有新任务到来则插入SynchronousQueue,由于核心线程为0,开启额外线程来执行任务,当空闲的额外线程获取任务时间超过指定大小的时间,额外线程就会销毁。

适用于执行很多短期异步的小程序或者负载均衡较轻的服务器

newFixedThreadPool

newFixedThreadPool:创建一个定长线程池,可以控制最大并发数,超出的线程会在队列中等待

1 public static ExecutorService newFixedThreadPool(int nThreads) {
2         return new ThreadPoolExecutor(nThreads, nThreads,
3                                       0L, TimeUnit.MILLISECONDS,
4                                       new LinkedBlockingQueue<Runnable>());
5     }

底层:返回ThreadPoolExecutor实例,接收参数为所设的的线程数量n,corePoolSize为n,maximumPoolSize为n,keepAlikeTime为0L,unit为TimeUnit.MILLISECONDS; workQueue为new LinkedBlockingQueue(无界阻塞队列)

执行过程:创建可容纳固定数量线程的池子,每个线程存活时间是无限的,当池子满了就不添加线程了,如果池子中的所有线程均在繁忙状态,对于新任务就会进入阻塞队列中

对于执行长期的任务,性能好很多

newSingleThreadExecutor

newSingleThreadExecutor:创建一个单一化的线程池,它只会用唯一的工作线程来执行任务,保证保证所有任务按照指定顺序(FIFO,LIFO,优先级)执行。

1  public static ExecutorService newSingleThreadExecutor() {
2         return new FinalizableDelegatedExecutorService
3             (new ThreadPoolExecutor(1, 1,
4                                     0L, TimeUnit.MILLISECONDS,
5                                     new LinkedBlockingQueue<Runnable>()));
6     }

底层:FinalizableDelegatedExecutorService包装的ThreadPoolExecutor实例,corePoolSize为1,maximumPoolSize为1,keepAlikeTime为0L,unit为TimeUnit.MILLISECONDS,workQueue为new LinkedBlockingQueue(无界阻塞队列)

执行过程:创建只有一个线程的线程池,且线程的存活时间是无限的;当该线程正繁忙时,对于新任务会进入阻塞队列中

适用于一个任务一个任务执行的场景。

newSchedThreadPool

newSchedThreadPool:创建一个定期或者延迟执行任务的定长线程,支持定时及周期性任务执行

 1  public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
 2         return new ScheduledThreadPoolExecutor(corePoolSize);
 3     }
 4 
 5  public ScheduledFuture<?> schedule(Runnable command,
 6                                        long delay, TimeUnit unit);
 7 
 8 public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
 9                                                   long initialDelay,
10                                                   long period,
11                                                   TimeUnit unit);
12 
13 public static void main(String[] args) {
14 
15  
16    Executors.newScheduledThreadPool(5).schedule(任务,延迟的时间,延迟的时间单位);
17 
18  Executors.newScheduledThreadPool(5).scheduleAtFixedRate(任务,延时时间,间隔时间,时间单位);
19 
20   }

 底层:创建ScheduledThreadPoolExecutor实例,corePoolSize为传递来的参数,maximumPoolSize为Integer.MAX_VALUE;keepAliveTime为0;unit为:TimeUnit.NANOSECONDS;workQueue为:new DelayedWorkQueue() 一个按超时时间升序排序的队列

执行过程:创建一个固定大小的线程池,线程池内线程存活时间无限制,线程池可以支持定时及周期性任务执行,如果所有线程均处于繁忙状态,对于新任务会进入DelayedWorkQueue队列中,这是一种按照超时时间排序的队列结构

适用于周期性的执行任务场景

不建议使用线程池工具类,这些返回的实例其实底层还是ThreadPoolExecutor,有些时候自己创建线程池配置参数可能更符合实际情况,而且多用工具类不利于对线程池底层的理解

posted @ 2020-08-31 16:00  咸鱼要逆天  阅读(239)  评论(0)    收藏  举报