多线程第二天
一、线程的六种状态
1、new 创建线程
2、Runnable(执行状态) java中将线程的就绪(Ready)和运行中(running)中统称为执行状态,此时的线程获取了cup的执行权
3、Blocked(线程阻塞) 线程此时具有争夺cup执行权的条件,但是还没有抢到时间片。正在运行的线程通过wait()或者sleep()可以到Blocked状态。
4、Wait(无限等待) 正在执行的线程可通过wait()进入该状态,相当于线程休眠的状态,但是自己无法苏醒,需要其他线程通过notify()或者notifyall()来唤醒该线程,唤醒后的状态时线程阻塞态。
5、TimeWait(计时等待) 可以通过sleep()或者wait()加时间参数来进入该状态,在计时期满或者收到了唤醒时,线程转换为线程阻塞态,等待获取cup的执行权
6、Teminated(终止) 当线程正常执行run方法结束后,或者收到没有捕获的异常,都会导致线程终止
二、线程通信
2.1线程通信概述
概念:当多个线程共同完成一个任务,但是处理的动作却不相同。
为什么要线程通信?
当需要多个线程公共去执行一个任务时,需要让线程有序的执行,线程之间就需要通信机制。
实现线程通信的机制?
等待唤醒机制。
2.2等待唤醒机制
等待唤醒机制是一种线程协作的机制。
一个线程在进行了规定的操作后,进入到了wait状态。等待其他线程执行完指定的代码后,通过notify将wait状态的线程唤醒,如果有多个wait的线程,可以通过notifyall将所有的等待线程唤醒。
等待唤醒机制的方法:
wait():将线程变为等待状态。
notify():唤醒等待状态的线程,变成阻塞态,如果能直接获取线程锁,就直接变成可运行态。
notifyAll():唤醒所有等待状态的线程。
调用等待唤醒机制方法应该注意的细节:
(1)wait()和notify()方法要通过同一个锁对象调用
(2)wait()和notify()方法都属于object类
(3)wait()和notify()要在同步代码、同步方法,因为必须通过锁调用这些方法。
2.3生产者和消费者问题
生产者和消费者问题是经典的等待唤醒机制问题。
场景模拟:两个线程共同实现一个交互的任务逻辑
饺子馆:
食客线程:向老板点了一份饺子,如果有饺子,食客就开始吃饺子,吃完后将饺子的改为false。并唤醒老板线程。如果没有饺子,食客线程进入等待。
老板线程:如果现在有饺子,老板线程等待。如果没有饺子,老板线程做饺子,做好后改变饺子的状态,并唤醒食客的线程。
饺子状态:有true,无false
三、线程池
1、什么是线程池?
就是一个容纳多个线程的容器,其中的线程可以反复使用,避免了反复创建销毁线程的过程
2、为什么需要线程池?
我们需要线程时,需要创建线程,线程用完后需要销毁线程。线程的创建和销毁都需要耗费时间。如果有大量的创建和销毁的过程,就会浪费大量的系统资源。
3、使用线程池的好处?
1、节省系统资源 2、加快响应速度 3、方便管理线程
4、怎么创建线程池?
java官方提供了java.util.concurrent.executor包,但是它并不是一个线程池,而是一个执行线程的工具,真正的线程池是java.util.concurrent.executorService,线程是很难配置的,
public static void main(String[] args) { ExecutorService es = Executors.newCachedThreadPool(); for(int i=0;i<20;i++){ final int index=i; es.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"---"+index); } }); } }
5.2 newFixedThreadPool(int ThreadCount) 定容缓冲池
特点:缓冲池有初始容量,池中的线程可以反复使用
如果线程池中没有空闲的线程,那么请求等待
如果有线程被终止,线程池中会再创建一个新的线程
线程池中的线程不会自己回收,除非调用显示shutdown方法
public static void main(String[] args) { ExecutorService es = Executors.newFixedThreadPool(20); for (int i=0;i<100;i++){ final int index=i; es.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"----"+index); } }); } }
5.3newScheduledThreadPool()定时缓冲池
特点:支持定时和循环任务执行
延迟定时,延迟的时间间隔使用调用开始,并不受线程运行时间长短的影响
public static void main(String[] args) { ScheduledExecutorService se = Executors.newScheduledThreadPool(3); for(int i= 0;i<200;i++){ final int index = i; se.schedule(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"--->"+index); } },5, TimeUnit.SECONDS); } }
5.4 newSingleThreadExecutor() 单一线程池
特点:最多只开启一个线程,对于队列中的Runnable,挨个执行
public static void main(String[] args) { ExecutorService es = Executors.newSingleThreadExecutor(); for(int i= 0;i<200;i++){ final int index = i; es.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"--->"+index); } }); } }