线程的学习总结
我打算从线程得生命周期开始总结多线程:
线程的生命周期:
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