java线程、线程池

Java 线程、线程池

1.创建线程的方式

一般表述为三种方式:继承Thread、实现Runable接口、实现Callback接口(可以获取线程执行之后的结果)。实际上,后两者只是创建了一个可执行任务,最终还是要用多线程的方式执行。

public class HelloWorld {
    
    
    public static void main(String[] args) {
    
    
        DogThread dogThread = new DogThread();
        dogThread.start();
        System.out.println("hello world!");
    }


    static class DogThread extends Thread{
    
    

        @Override
        public void run() {
    
    
            int i = 0;
            while (i++ < 10){
    
    
                System.out.println(i);
            }
        }
    }
}
public class HelloWorld {
    
    
    public static void main(String[] args) {
    
    
        Thread thread = new Thread(new Dog());
        thread.start();
        System.out.println("hello world!");
    }


    static class Dog implements Runnable{
    
    

        @Override
        public void run() {
    
    
            int i = 0;
            while (i++ < 10){
    
    
                System.out.println(i);
            }
        }
    }
}
// todo: CallBack 实例

2.线程的生命周期

在这里插入图片描述

新建状态(New):

当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();

就绪状态(Runnable):

当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;

运行状态(Running):

当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就 绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;

阻塞状态(Blocked):

处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:

  • 等待阻塞 – 运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
  • 同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
  • 其他阻塞 – 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

死亡状态(Dead):

线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

3.sleep 和 wait 的区别

  • 方法所属的类不同,sleep()是Thread类的方法,wait() 是定义在Object上的方法(锁是对象级别的,sleep是线程级别的)
  • 对于锁资源的处理方式不同,sleep()不是放锁,wait()释放锁
  • sleep() 可以使用在任何代码块,wait()必须在同步代码中
  • wait()必须与notify()/notifyAll()配套使用

4.线程安全

当多个线程访问同一个对象时,如果不用进行额外的同步控制或者其它的协调操作,调用这个对象的行为都可以获得正确的结果,即这个对象是线程安全的。
构成线程不安全的3要素:多线程环境、访问同一资源、资源具有状态性。

5.ThreadLocal 类

为每个线程创建一个副本,实现在同一线程的上下文中传递相同的变量副本。

6. 线程池

6.1、作用以及使用场景

线程池的作用:线程的执行时间=创建线程时间T1+线程中执行任务时间T2+线程销毁时间T3,线程池的主要作用是节省线程执行时间(减少T1+T3)。
适用场景:并发的线程数量很多,并且每个线程执行一个时间很短的任务就结束了。

6.2、运行原理

如果当前线程池中的线程数目小于corePoolSize,则每来一个任务,就会创建一个线程去执行这个任务;如果当前线程池中的线程数目>=corePoolSize,则每来一个任务,会尝试将其添加到任务缓存队列当中,若添加成功,则该任务会等待空闲线程将其取出去执行;若添加失败(一般来说是任务缓存队列已满),则会尝试创建新的线程去执行这个任务;如果当前线程池中的线程数目达到maximumPoolSize,则会采取任务拒绝策略进行处理;如果线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数目不大于corePoolSize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。

6.3、组成部分

由四个基本部分组成:

  • 线程池管理:用于创建管理线程池,包括创建线程池、销毁线程池,添加新任务。
  • 工作线程:线程池中的线程,在没有任务时候处于等待状态,可以循环的执行任务。
  • 任务接口:每个人物必须是实现的接口,以供工作线程调度任务的执行,主要规定了任务的入口、任务执行完的收尾工作、任务的执行状态。
  • 任务队列:用于存放没有处理的任务,提供一种缓冲机制。

6.4、线程池核心类 ThreadPoolExecutor的构造参数

  • corePoolSize:核心池大小;
  • maximumPoolSize: 线程池最大线程数。
  • keepAliveTime: 表示线程没有任务执行时最多保持多久时间会终止。
  • unit: 参数keepAliveTime的时间单位。
  • workQueue:一个阻塞队列,用来存储等待执行的任务。
  • threadFactory:线程工厂,主要用来创建线程;
  • handler: 表示当拒绝处理任务时的策略

6.5、execute()和submit()

execute()通过这个方法向线程池提交一个任务,submit() 内部调用execute(),也可以向线程池提交任务,并利用Futrue来获取任务执行结果。

6.6、如何配置线程池大小

一般根据任务的类型来配置内存池大小,如果是CPU密集型任务,需要尽量压榨CPU,参考值可以设置为Ncpu+1;如果是IO密集型,参考值可以设置为2*Ncpu。具体需要根据实际情况调整。

6.7、线程池状态

在这里插入图片描述
当创建线程池后,初始时,线程池处于RUNNING状态;
如果调用了shutdown()方法,则线程池处于SHUTDOWN状态,此时线程池不能够接受新的任务,它会等待所有任务执行完毕;
如果调用了shutdownNow()方法,则线程池处于STOP状态,此时线程池不能接受新的任务,并且会去尝试终止正在执行的任务;
当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。
线程池彻底终止,就变成TERMINATED状态。

6.8、常用线程池

  • newCacheThreadPool(0, Interger.MAX_VALUE):可缓存线程池,先查看池中有没有以前建立的线程,如果有,就直接使用。如果没有,就建一个新的线程加入池中,缓存型池子通常用于执行一些生存期很短的异步型任务。
  • newFixedThreadPool(n, n ,…, LinkedBlockingQueue):创建一个可重用固定个数的线程池,以共享的无界队列方式来运行这些线程。
  • newScheduledThreadPool(corePoolSize(n), Integer.MAX_VALUE,0 …)支持定时及周期性任务执行。
  • newSingleThreadExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(无界队列)),创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

猜你喜欢

转载自blog.csdn.net/cc890824/article/details/114929186