美文网首页
线程的学习总结

线程的学习总结

作者: 132xin | 来源:发表于2020-07-21 17:41 被阅读0次

    我打算从线程得生命周期开始总结多线程:

    线程的生命周期:

    image.png
    • 新建状态:

    线程对象创建之后,线程进入新建状态.

    • 就绪状态

    当线程对象创建之后,调用start()方法,线程进入就绪的状态,此时的线程不一定会立即执行,需要等待CPU的调度执行。

    • 运行状态

    当就绪状态的线程得到CPU的调度执行之后,开始运行,此时线程处于正在的执行状态。注意的是,只有处于就绪状态的线程才能被CPU调度执行进入运行状态。

    • 阻塞状态

    处于运行状态的线程由于某种原因,暂时放弃CPU的使用权,停止执行,这时线程进入阻塞状态,直到进入就绪状态等带CPU的调度执行,才能进入运行态。根据产生阻塞状态的原因不同,分为三种阻塞状态。

    (1):等待阻塞状态:通过调用了wait()方法,线程进入阻塞状态,wait()会让出Cpu的资源,并且会释放对象锁,但是只能在同步代码块或同步方法重使用,需要调用notify()/notifyAll()进行唤醒。

    (2):同步阻塞状态:线程在获取Synchroized同步锁的时候,获取不到时,会进入阻塞状态。注意通过Lock进行同步是不会出现阻塞状态的。

    (3):其他同步状态:通过sleep(),join等待线程和I/O处理时,线程会进入阻塞的状态,这种线程不需要手动唤醒,当sleep状态超时,join线程执行完成,I/o处理完成时,线程会自动重新进入就绪状态。

    • 死亡状态

    线程的run方法执行完或者出现异常时,该线程进入死亡的状态,结束生命周期。

    总结一下sleep()和wait()的方法的区别:

    sleep()是Thread的方法,而wait()是Object的方法

    sleep()不会释放对象锁,wait()释放对象锁,进入对象锁的等待线程重,只有通过显示的唤醒操作notify()/notifyAll()线程才会进入对象锁池中获得锁进入运行状态。

    sleep()方法导致了程序暂停执行指定的时间,让出 cpu 该其他线程,但是他的监控状态依然保持着,当指定的时间到了又会自动恢复运行状态。

    Java线程的创建

    • 继承Thread类
      通过继承Thread类,并重写run的方法,在调用的时候进行实例化,并且通过start()方法来启动线程。
    public class CreateThread {
         public static void main(String[] args) {
             MyThread myThread =new MyThread();
             //启动线程
             myThread.start();
         }
    }
    class MyThread extends Thread{
        @Override
        public void run() {
            
        }
    }
    
    
    • 实现Runnable接口
      Thread类中有一个构造方法Thread(Runnable target),传入的参数是Runnable,所以可以通过实现Runnable接口的方式进行创建线程,并且重写run的方法。
    public class CreateThread {
         public static void main(String[] args) {
             Thread thread=new Thread(new MyThreadRunable());
             //启动线程
             thread.start();
         }
    }
    class MyThreadRunable implements Runnable{
    
        @Override
        public void run() {
        }
        
    }
    
    • 线程池的方式
      通过线程池创建线程有以下这些优点。
      通过重用线程池中的线程,可以避免线程的重复创建和销毁带来的性能消耗的问题。
      可以控制线程的最大并发数,避免大量的线程之间因互相抢占资源而导致系统阻塞的现象。
      进行线程管理,提供定时,循环间隔执行等功能。

    线程池的工作策略:

    若线程池中的线程数量未达到核心线程数,则会直接启动一个核心线程执行任务。
    如果线程中的线程的数量已经达到或者超过线程池中的核心数量时,任务会被插入到任务列表中等待执行。

    如果任务列表中无法插入,说明列表由于任务列表已经满了,此时更据线程池中的线程数量来取决下一步,如果线程池数量还没有达到线程池中最大的线程数,则会启动一个非核心线程执行任务。如果线程池中的数量已经达到线程规定的最大值,则拒接执行任务,ThreadPoolExecutor会调用RejectedExecutionHandler的rejectedExecution来通知调用者。

    线程池的分类

    newFixThreadPool:

    线程数量固定的线程池,所有线程都是核心线程,当线程空闲时会被回收,能快速响应外界的请求。

    newCachedThreadPool

    创建一个可根据需要创建新线程的线程池,但是以前构造的线程可用时重用他们,调用execute将重用构造的线程。如果没有线程可用的,则会创建一个新线程并添加到线程池中。终止并从缓存中移除那些已有60秒钟没有被使用的线程。因此,长时间保持空闲的线程不会占用资源。

    newScheduledThreadPool

    核心线程数量固定,非核心线程数量不定的线程池,可以执行定时任务和固定周期的任务。

    newSingleThreadExecutor

    只有一个核心线程,这个线程可以在线程死亡后,重新启动一个线程来代替原来的线程继续执行下去。可以确保所有任务都在同一个线程中按顺序执行,好处是无需自立线程同步的问题。

     //创建只有一个核心线程的线程池,线程以队列顺序来执行
             ExecutorService singPool=Executors.newSingleThreadExecutor();
             //创建一个线程数量固定的线程池,所有线程都是核心线程,线程空闲时不回收,能快速响应外界请求。
             ExecutorService fixedThreadPool=Executors.newFixedThreadPool(2);
             //创建一个线程数量不固定的线程池,线程数量最大为Integer.MAX_VALUE,只有非核心线程,空闲线程超时回收。
             ExecutorService cachedThreadPool=Executors.newCachedThreadPool();
             //创建一个核心线程数量固定,非核心线程不固定,可进行定时任务。
             ExecutorService scheduledThreadPool=Executors.newScheduledThreadPool(2);
    

    线程池的原理

    实际上线程是通过ThreadPoolExecutor并通过一系列参数来配置各种线程池。

    corePoolSize核心线程数,一般会在线程中一直存活。
    maximumPoolSize线程池中最大的线程数量。
    keepAliveTime非核心线程超时时间,如果超高这个时长,闲置的非核心线程就会被回收。
    unit用指定keepAliveTime参数的时间单位。
    workQueue任务队列,通过线程池的execut()方法提交的Runnable对象存储在这个参数中。
    threadFactory:线程工厂,可创建新线程。
    handler在线程池无法执行新任务时进行调度。

    通过Executors创建线程有一些这些弊端。

    newFixedThreadPool和newSingleThreadThreadExecutor主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
    newCachedThreadPool和newScheduledThreadPool主要问题是线程数量最大是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。
    解决方法是通过ThreadPoolExecutor创建线程池,便于明确线程池的运行规则,规避资源耗尽的风险。

    ThreadPoolExcutor的构造参数

    public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,
                                  TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue,
                                  ThreadFactory threadFactory,
                                  RejectedExecutionHandler handler) 
    

    线程池的简单使用
    先看一下ThreadPoolExecutor中比较重要的四个方法

    void execute(Runnable command) 执行给定的任务
    Future<?> submit(Runnable task) 提交一个可执行的任务执行,并返回结果。
    List<Runnable> shutdownNow() 尝试停止所有主动执行的任务,停止等待任务的处理,并且返回正在等待执行的任务列表。
    void shutdown() 关闭线程池的连接。

    newSingleThreadExecutor线程池

    public class CreateThread {
         public static void main(String[] args) {
             //创建只有一个核心线程的线程池,线程以队列顺序来执行
             ExecutorService singPool=Executors.newSingleThreadExecutor();
             ExecutorService scheduledThreadPool=Executors.newScheduledThreadPool(2);
             singPool.execute(new Runnable1());
             singPool.execute(new Runnable1());
             singPool.execute(new Runnable1());
             singPool.execute(new Runnable1());
         }
       
    }
    class Runnable1 implements Runnable{
    
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
        
    }
    
    image.png

    newFixedThreadPool线程池

    public class CreateThread {
         public static void main(String[] args) {
             //创建一个线程数量固定的线程池,所有线程都是核心线程,线程空闲时不回收,能快速响应外界请求。
             ExecutorService fixedThreadPool=Executors.newFixedThreadPool(3);
             fixedThreadPool.execute(new Runnable1());
             fixedThreadPool.execute(new Runnable1());
             fixedThreadPool.execute(new Runnable1());
             fixedThreadPool.execute(new Runnable1());
         }
    }
    class Runnable1 implements Runnable{
    
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
        
    }
    
    image.png

    newCachedThreadPool线程池:

    public class CreateThread {
         public static void main(String[] args) {
             //创建一个线程数量不固定的线程池,线程数量最大为Integer.MAX_VALUE,只有非核心线程,空闲线程超时回收。
             ExecutorService cachedThreadPool=Executors.newCachedThreadPool();
             cachedThreadPool.execute(new Runnable1());
             cachedThreadPool.execute(new Runnable1());
             cachedThreadPool.execute(new Runnable1());
             cachedThreadPool.execute(new Runnable1());
         }
    }
    class Runnable1 implements Runnable{
    
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
        
    }
    
    image.png

    newScheduledThreadPool线程池

    public class CreateThread {
         public static void main(String[] args) {
             //创建一个核心线程数量固定,非核心线程不固定,可进行定时任务。
             ExecutorService scheduledThreadPool=Executors.newScheduledThreadPool(3);
            scheduledThreadPool.execute(new Runnable1());
            scheduledThreadPool.execute(new Runnable1());
            scheduledThreadPool.execute(new Runnable1());
            scheduledThreadPool.execute(new Runnable1());
         }
    }
    class Runnable1 implements Runnable{
    
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
        
    }
    
    
    image.png

    参考链接:

    https://www.jianshu.com/p/89b19904cd23
    http://www.360doc.com/content/19/0314/20/25472797_821520399.shtml
    https://www.jianshu.com/p/4b89d681c5a0

    相关文章

      网友评论

          本文标题:线程的学习总结

          本文链接:https://www.haomeiwen.com/subject/strwkktx.html