并发和并行
并行: 指两个或多个时间在同一刻发生(同时发生);
并发: 指两个或多个时间再同一个时间段内发生(同一段时间发生)
线程与进程
进程: 是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个线程;线程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个线程从创建,运行到消亡的过程.
进程: 进程内部的一个独立执行单元;一个进程可以同时并发的运行多个线程,可以理解为一个进程便相当于一个单CPU操作系统,而线程便是这个系统中运行的多个任务
区别
进程: 每个进程有独立的内存空间,进程中的数据存放空间(堆空间和栈空间),是独立的,至少有一个进程
线程: 堆空间是共享的,栈空间是独立的,线程消耗资源比进程小的多
线程的两种模型
用户级线程(ULT)
用户程序实现,不依赖操作系统核心;
由第三方拓展的应用来管理线程(自己安装的软件的线程);
不需要用户态/内核态切换,速度快
内核对ULT 无感知,线程阻塞则进程(包括它的所有线程)阻塞
内核级线程(KLT)
系统内核管理线程,有操作系统管理线程;
内核保存线程的状态和上下文信息,线程阻塞不会引起进程阻塞;
线程的创建,调度和管理由内核完成,效率比ULT要慢,比进程操作快
Jvm线程模型
JVM虚拟机采用的线程模型:内核级线程(KLT)
Java线程创建是依赖于系统内核,通过JVM调用系统库创建内核线程,内核线程与Java-Thread是1:1 的映射关系
创建线程
继承Thread
继承Thread,重写该类的run方法,该类的run()方法的方法体代表了线程需要完成的任务,调用改子类的start()方法来启动线程
public class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName()+"新的线程"+i);
}
}
}
Thread 类常用方法
构造方法:
public Thread() : 创建一个新的线程对象
public Thread(String name): 创建一个指定名字的新的线程对象
public Thread(Runnable target): 分配一个带有指定目标新的线程对象
public Thread(Runnable target,String name):分配一个带有指定目标新的线程对象并指定名字
成员方法:
public String getName() :឴ 获取当前线程名称
public void setName(String name) :设置线程名称
public void start() :开启线程,再新的执行路径中执行run方法
public static void sleep(long millis) :让当前线程休眠指定时间,单位:毫秒值
public static Thread currentThread() : ឴获得执行当前方法的线程对象
实现Runnable
实现Runnable接口,并重写该接口的run()方法,并创建实现对象,用来作为Thread的target 创建 Thread对象,调用线程的start()方法启动线程
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
public class Demo {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
Thread t = new Thread(mr, "子线程");
t.start();
for (int i = 0; i < 20; i++) {
System.out.println("主线程 " + i);
}
}
}
实现Callable
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println(Thread.currentThread().getName()+":正在执行任务");
return "分线程";
}
}
public class Test2 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable myCallable = new MyCallable();
FutureTask futureTask = new FutureTask(myCallable);
Thread thread = new Thread(futureTask,"线程1");
thread.start();
System.out.println(Thread.currentThread().getName()+":正在执行");
//该方法不能在前程启动之前执行
String a = (String) futureTask.get();
System.out.println(a);
}
}
//输出
线程1:正在执行任务
分线程
main:正在执行
分线程
线程池创建
ExecutorService service = Executors.newFixedThreadPool(2);
Runnable task = new Run();
service.submit(task);
线程安全
概述: 两个或以上的线程再访问共享资源时仍能得到正确的结果则称为线程安全
当我们使用多个线程访问统一资源的时候,且多个线程对资源有写的操作,就容易出现线程安全问题;
Java中提供了同步机制(synchronized)来解决多线程并发访问一个个资源的安全性问题
线程同步
同步代码块
synchronized关键字可以用于方法中的某个区域,标识对这个区块的资源实行互斥访问
同步代码块格式
synchronized(同步锁){
需要同步操作的代码
}
同步锁注意事项:
1.锁对象 可以使任意类型
2.多个线程对象,要使用同一把锁
public class Test4 {
private static Object lock = new Object();
private static Integer count = 100;
public static void main(String[] args) {
MyThread thread1 = new MyThread("thread1");
MyThread thread2 = new MyThread("thread2");
MyThread thread3 = new MyThread("thread3");
thread1.start();
thread2.start();
thread3.start();
}
static class MyThread extends Thread{
public MyThread(String name){
super(name);
}
@Override
public void run() {
while (true){
synchronized(lock){
if(count > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在售卖:" + count-- );
}
}
if( count < 1) {
break;
}
}
}
}
}
同步方法
概述: 使用synchronized修饰的方法,就叫同步方法,保证A线程执行该方法的时候,其他线程只能在方法外面等着
public synchronized void method(){
可能会产生线程安全问题的代码
}
同步锁:对于非static方法,同步锁就是this;对于static方法,我们使用当前方法所在类的字节码对象(类名.class)
public class Test5 {
private static Object lock = new Object();
private static Integer count = 100;
public static void main(String[] args) {
MyThread thread1 = new MyThread("thread1");
MyThread thread2 = new MyThread("thread2");
MyThread thread3 = new MyThread("thread3");
thread1.start();
thread2.start();
thread3.start();
}
static class MyThread extends Thread{
public MyThread(String name){
super(name);
}
@Override
public void run() {
while (true){
rob();
if(count < 1){
break;
}
}
}
public static synchronized void rob(){
if(count > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在售卖:" + count-- );
}
}
}
}
锁机制
java.util.concurrent.locks.Lock 提供了比同步代码块和同步方法更加广泛的锁定操作,此外,它的功能更加强大,更体现面向对象
Lock常用方法:
public void lock(): 加同步锁
public void unlock():释放同步锁
public class Test6 {
private static Lock lock = new ReentrantLock();
private static Integer count = 100;
public static void main(String[] args) {
MyThread thread1 = new MyThread("thread1");
MyThread thread2 = new MyThread("thread2");
MyThread thread3 = new MyThread("thread3");
thread1.start();
thread2.start();
thread3.start();
}
static class MyThread extends Thread{
public MyThread(String name){
super(name);
}
@Override
public void run() {
while (true){
lock.lock();
if(count > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在售卖:" + count-- );
}
lock.unlock();
if( count < 1) {
break;
}
}
}
}
}
线程的生命周期
线程 | 导致状态发生条件 |
---|---|
NEW(新建) | 线程刚被创建,但是并未启动 |
Runnable(可运行) | 线程可以在Java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器.一个正在Java虚拟机中执行的线程处于这一状态. |
Blocked(锁阻塞) | 当一个线程试图获取一个对象锁,而该对象被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态 |
Waiting(无线等待) | 一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态.进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能唤醒 |
Timed Waiting(计时等待) | 同waiting状态,有几个方法有超时参数,调用他们将进入Time Waiting状态.这一状态将一直保持到超时期满或者接收到唤醒通知.带有超时参数常用方法Thread.sleep,Object.wait |
Teminated(被终止) | 因为run方法正常退出而死亡,或者因为有捕获的异常终止了run方法而死亡 |
package cn.gdmcmc.esi.Thread;
/**
* @Description:
* @Author:lighter
* @Date:2020/5/25 9:21
* @Version 1.0
*/
public class Test3 {
private static Object lock = new Object();
private static Object lock2 = new Object();
public static void main(String[] args) throws InterruptedException {
Thread1 thread1 = new Thread1("thread1");
Thread2 thread2 = new Thread2("thread2");
thread2.start();
thread1.start();
//Thread.sleep(10000);
synchronized (lock){
lock.wait();
}
System.out.println(Thread.currentThread().getName()+"等待唤醒后运行");
}
static class Thread1 extends Thread {
public Thread1(String name){
super(name);
}
@Override
public void run() {
try {
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName()+"休眠5秒后运行");
synchronized (lock){
lock.notifyAll();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class Thread2 extends Thread{
public Thread2(String name){
super(name);
}
@Override
public void run() {
try {
//Thread.sleep(10000);
synchronized (lock){
lock.wait();
//lock.wait(10000);
}
//synchronized (lock2){
//lock2.wait();
//lock2.wait(10000);
//}
System.out.println(Thread.currentThread().getName()+"等待被唤醒后运行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
线程终止的四种方式
run()结束自动终止
线程run()运行结束或异常终止
stop()方法启用
public class Test8 {
public static void main(String[] args) {
Thread thread = new MyThread();
thread.start();
Thread.sleep(10);
thread.stop();
}
static class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(i);
}
}
}
}
//输出为0-257 不一定
volatile标志位(外部控制的自然死亡)
public class Test9 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thr( "1");
thread.start();
//修改退出标志,使线程终止
Thr.flag = true;
}
static class Thr extends Thread{
//
private static volatile boolean flag = false;
public Thr(String name){
super(name);
}
@Override
public void run() {
System.out.println("第" + Thread.currentThread().getName() + "个线程创建");
if (flag){
//退出标志生效位置
try {
int i = 1/0;
} catch (Exception e) {
throw new RuntimeException("中断进程");
}
}
System.out.println("第" + Thread.currentThread().getName() + "个线程终止");
}
}
}
//输出
第1个线程创建
Exception in thread "1" java.lang.RuntimeException: 中断进程
at cn.gdmcmc.esi.Thread.Test9$Thr.run(Test9.java:37)
interrupt()中断运行态和阻塞态线程
public class Test10 {
public static void main(String[] args) {
Thread thread = new MyThread("1");
thread.start();
thread.interrupt();
}
static class MyThread extends Thread{
public MyThread(String name) {
super(name);
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" 线程创建");
boolean b = Thread.currentThread().isInterrupted();
if (b) {
try {
int i = 1 / 0;
} catch (Exception e) {
throw new RuntimeException("线程中断");
}
}
System.out.println(Thread.currentThread().getName()+" 线程终止");
}
}
}
//输出
1 线程创建
Exception in thread "1" java.lang.RuntimeException: 线程中断
at cn.gdmcmc.esi.Thread.Test10$MyThread.run(Test10.java:31)
线程池
概述
其实就是一个容纳多个线程的容器,其中的线程可以反复只用,省去了频繁的创建线程对象的操作,无需反复创建线程而消耗过多资源
假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间。
如果:T1 + T3 远大于 T2,则可以采用线程池,以提高服务器性能。
好处:
1. 降低资源消耗。减少了创建和销毁线程的次数,每个线程都可以被重复利⽤,可执⾏多个任务。
2.提⾼响应速度。当任务到达时,任务可以不需要的等到线程创建就能⽴即执⾏。
3.提⾼线程的可管理性。可以根据系统的承受能⼒,调整线程池中⼯作线线程的数⽬,防⽌因为消
耗过多的内存,⽽把服务器累趴下(每个线程需要⼤约1MB内存,线程开的越多,消耗的内存也
就越⼤,最后死机)。
线程池的创建方式
Java⾥⾯线程池的顶级接⼝是 java.util.concurrent.Executor ,但是严格意义上讲 Executor 并不
是⼀个线程池,⽽只是⼀个执⾏线程的⼯具。真正的线程池接⼝是 java.util.concurrent.ExecutorService 。
源码
public class Test11 {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(2);
Runnable task = new Run();
service.submit(task);
service.submit(task);
service.submit(task);
}
static class Run implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程执行");
}
}
}
//输出
pool-1-thread-1线程执行
pool-1-thread-2线程执行
pool-1-thread-1线程执行
public class Test12 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService service = Executors.newFixedThreadPool(2);
Call call = new Call();
Future<String> f = service.submit(call);
service.submit(call);
System.out.println(f.get());
}
static class Call implements Callable{
@Override
public String call() throws Exception {
System.out.println(Thread.currentThread().getName()+"线程运行");
return Thread.currentThread().getName();
}
}
}
//输出
pool-1-thread-2线程运行
pool-1-thread-1线程运行
pool-1-thread-1
线程池的分类
newCachedThreadPool(可缓存线城池)
是一种线程数量不定的线程池,并且其最大线程数为Integer.MAX_VALUE,这个数是很大的,一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。但是线程池中的空闲线程都有超时限制,这个超时时长是60秒,超过60秒闲置线程就会被回收。调用execute将重用以前构造的线程(如果线程可用)。这类线程池比较适合执行大量的耗时较少的任务,当整个线程池都处于闲置状态时,线程池中的线程都会超时被停止。
特点:
1.核心线程数为0
2.最大线程数为Integer.MAX_VALUE
3.阻塞队列是SynchronousQueue
4.非核心线程空闲存活时间为60秒
工作机制:
1.提交任务
2.因为没有核心线程,所以任务直接加到SynchronousQueue队列
3.判断是否有空闲线程,如果有,就去取出任务执行
4.如果没有空闲线程,就新建一个线程执行
5.执行完任务的线程,还可以存活60秒,如果在这期间,接到任务,可以继续活下去;否则,被销毁
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
service.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName()+"线程");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
//输出
ool-1-thread-5线程
pool-1-thread-2线程
pool-1-thread-3线程
pool-1-thread-7线程
pool-1-thread-8线程
pool-1-thread-4线程
pool-1-thread-1线程
pool-1-thread-6线程
pool-1-thread-9线程
pool-1-thread-10线程
public class Test13 {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
service.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+"线程");
} catch (Exception e) {
e.printStackTrace();
}
}
});
service.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+"线程");
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
//输出
pool-1-thread-1线程
pool-1-thread-1线程
使用场景: 用于并发执行大量短期的小任务。
newFixedThreadPool(定长线程池)
创建一个指定工作线程数量的线程池,每当提交一个任务就创建一个工作线程,当线程 处于空闲状态时,它们并不会被回收,除非线程池被关闭了,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列(没有大小限制)中。由于newFixedThreadPool只有核心线程并且这些核心线程不会被回收,这样它更加快速底相应外界的请求。
特点:
1.核心线程数和最大线程数大小一样
2.没有所谓的非空闲时间,即keepAliveTime为0
3.阻塞队列为可设置容量队列LinkedBlockingQueue
工作机制:
1.提交任务
2.如果线程数少于核心线程,创建核心线程执行任务
3.如果线程数等于核心线程,把任务添加到LinkedBlockingQueue阻塞队列
4.如果线程执行完任务,去阻塞队列取任务,继续执行。
ExecutorService service = Executors.newFixedThreadPool(2);
Runnable task = new Run();
service.submit(task);
newFixedThreadPool使用了无界的阻塞队列LinkedBlockingQueue,如果线程获取一个任务后,任务的执行时间比较长(示例,睡眠10s),会导致队列的任务越积越多,导致机器内存使用不停飙升, 最终导致OutOfMemoryError。
FixedThreadPool 适用于处理CPU密集型的任务,确保CPU在长期被工作线程使用的情况下,尽可能的少的分配线程,即适用执行长期的任务。
newScheduledThreadPool(定长线程池)
创建一个线程池,它的核心线程数量是固定的,而非核心线程数是没有限制的,并且当非核心线程闲置时会被立即回收,它可安排给定延迟后运行命令或者定期地执行。这类线程池主要用于执行定时任务和具有固定周期的重复任务。
特点:
1.最大线程数为Integer.MAX_VALUE
2.阻塞队列是DelayedWorkQueue
3.keepAliveTime为0
4.scheduleAtFixedRate() :按某种速率周期执行
5.scheduleWithFixedDelay():在某个延迟后执行
工作机制:
1.添加一个任务
2.线程池中的线程从 DelayQueue 中取任务
3.线程从 DelayQueue 中获取 time 大于等于当前时间的task
4.执行完后修改这个 task 的 time 为下次被执行的时间
5.这个 task 放回DelayQueue队列中
public class Test14 {
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
//设置延迟2S后执行
service.schedule(new MyRun(),2, TimeUnit.SECONDS);
//设置延迟2S后,每隔1s执行一次
service.scheduleAtFixedRate(new MyRun(),2,1, TimeUnit.SECONDS);
service.schedule(new MyRun(),2, TimeUnit.SECONDS);
service.execute(new MyRun1());
service.execute(new MyRun1());
service.execute(new MyRun1());
service.execute(new MyRun1());
service.execute(new MyRun1());
}
static class MyRun implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+">>>"+System.currentTimeMillis());
}
}
static class MyRun1 implements Runnable{
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
}
}
//输出
pool-1-thread-1
pool-1-thread-2
pool-1-thread-2
pool-1-thread-1
pool-1-thread-1>>>1591173057219
pool-1-thread-1>>>1591173057219
pool-1-thread-1>>>1591173057219
pool-1-thread-1>>>1591173057219
pool-1-thread-1>>>1591173057219
pool-1-thread-1>>>1591173058207
pool-1-thread-1>>>1591173059205
pool-1-thread-2
pool-1-thread-1>>>1591173060207
pool-1-thread-1>>>1591173061206
pool-1-thread-1>>>1591173062206
pool-1-thread-1>>>1591173063205
使用场景:周期性执行任务的场景,需要限制线程数量的场景
newSingleThreadExecutor(单线程化线程池)
这类线程池内部只有一个核心线程,以无界队列方式来执行该线程,这使得这些任务之间不需要处理线程同步的问题,它确保所有的任务都在同一个线程中按顺序中执行,并且可以在任意给定的时间不会有多个线程是活动的
特点:
1.核心线程数为1
2.最大线程数也为1
3.阻塞队列是LinkedBlockingQueue
4.keepAliveTime为0
工作机制:
1.提交任务
2.线程池是否有一条线程在,如果没有,新建线程执行任务
3.如果有,将任务加到阻塞队列
4.当前的唯一线程,从队列取任务,执行完一个,再继续取,一个人(一条线程)夜以继日地干活。
public class Test15 {
private static int num = 0;
public static void main(String[] args) {
ExecutorService service = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+">>"+(++num));
}
});
}
}
}
//输出
pool-1-thread-1>>1
pool-1-thread-1>>2
pool-1-thread-1>>3
pool-1-thread-1>>4
pool-1-thread-1>>5
pool-1-thread-1>>6
pool-1-thread-1>>7
pool-1-thread-1>>8
pool-1-thread-1>>9
pool-1-thread-1>>10
//有线程安全
public class Test15 {
private static int num = 0;
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+">>"+(++num));
}
});
}
}
}
//输出
pool-1-thread-2>>1
pool-1-thread-1>>1
pool-1-thread-3>>2
pool-1-thread-5>>3
pool-1-thread-4>>4
pool-1-thread-6>>5
pool-1-thread-7>>6
pool-1-thread-8>>7
pool-1-thread-10>>8
pool-1-thread-9>>9
使用场景:适用于串行执行任务的场景,一个任务一个任务地执行。
线程池的组成
线程池管理器(ThreadPool)
用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务
工作线程(PoolWorker)
程池中线程,在没有任务时处于等待状态,可以循环的执行任务
任务接口(Task)
每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等
任务队列(taskQueue)
用于存放没有处理的任务。提供一种缓冲机制
线程池的参数作用
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
corePoolSize: 线程池核心线程数最大值
maximumPoolSize: 线程池最大线程数大小
keepAliveTime: 线程池中非核心线程空闲的存活时间大小
unit: 线程空闲存活时间单位
workQueue: 存放任务的阻塞队列
threadFactory: 用于设置创建线程的工厂,可以给创建的线程设置有意义的名字,可方便排查问题
handler: 线城池的饱和策略事件,主要有四种类型。
线程池执行任务流程
execute()方法执行
1.提交一个任务,线程池里存活的核心线程数小于线程数corePoolSize时,线程池会创建一个核心线程去处理提交的任务。
2.如果线程池核心线程数已满,即线程数已经等于corePoolSize,一个新提交的任务,会被放进任务队列workQueue排队等待执行。
3.当线程池里面存活的线程数已经等于corePoolSize了,并且任务队列workQueue也满,判断线程数是否达到maximumPoolSize,即最大线程数是否已满,如果没到达,创建一个非核心线程执行提交的任务。
4.如果当前的线程数达到了maximumPoolSize,还有新的任务过来的话,直接采用拒绝策略处理。
拒绝策略类型
1.AbortPolicy(抛出一个异常,默认的)
2.DiscardPolicy(直接丢弃任务)
3.DiscardOldestPolicy(丢弃队列里最老的任务,将当前这个任务继续提交给线程池)
4.CallerRunsPolicy(交给线程池调用所在的线程进行处理)
线程池异常处理
1.try/catch捕获异常
2.submit执行,Future.get接受异常
3.重写TreadPoolExecutor.afterExecute方法,处理传递的异常引用
线程池的工作队列
ArrayBlockingQueue:(有界队列)是一个用数组实现的有界阻塞队列,按FIFO排序量
LinkedBlockingQueue:(可设置容量队列)基于链表结构的阻塞队列,按FIFO排序任务,容量可以选择进行设置,不设置的话,将是一个无边界的阻塞队列,最大长度为Integer.MAX_VALUE,吞吐量通常要高于ArrayBlockingQuene;newFixedThreadPool线程池使用了这个队列
DelayQueue:(延迟队列)是一个任务定时周期的延迟执行的队列。根据指定的执行时间从小到大排序,否则根据插入到队列的先后排序。newScheduledThreadPool线程池使用了这个队列。
PriorityBlockingQueue:(优先级队列)是具有优先级的无界阻塞队列;
SynchronousQueue:(同步队列)一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene,newCachedThreadPool线程池使用了这个队列。
线程池的状态
RUNNING
1.该状态的线程池会接收新任务,并处理阻塞队列中的任务;
2.调用线程池的shutdown()方法,可以切换到SHUTDOWN状态;
3.调用线程池的shutdownNow()方法,可以切换到STOP状态;
SHUTDOWN
1.该状态的线程池不会接收新任务,但会处理阻塞队列中的任务;
2.队列为空,并且线程池中执行的任务也为空,进入TIDYING状态;
STOP
1.该状态的线程不会接收新任务,也不会处理阻塞队列中的任务,而且会中断正在运行的任务;
2.线程池中执行的任务为空,进入TIDYING状态;
TIDYING
1.该状态表明所有的任务已经运行终止,记录的任务数量为0。
2.terminated()执行完毕,进入TERMINATED状态
TERMINATED
1.该状态表示线程池彻底终止
源码分析
run()
Thread(class)
//私有成员变量
private Runnable target;
//构造方法
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
//init
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
//init
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
if (g == null) {
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. */
if (security != null) {
g = security.getThreadGroup();
}
/* If the security doesn't have a strong opinion of the matter
use the parent thread group. */
if (g == null) {
g = parent.getThreadGroup();
}
}
/* checkAccess regardless of whether or not threadgroup is
explicitly passed in. */
g.checkAccess();
/*
* Do we have the required permissions?
*/
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
tid = nextThreadID();
}
@Override
public void run() {
if (target != null) {
target.run();
}
}
Runnable(interface)
public abstract void run();
备注: 当实现Runnable 接口创建线程, target 不为空,线程执行的 Runnable重写的 run方法;
当继承Thread 父类,target 为null,线程执行的Thread重写的run方法
当继承Thread和重写Runnable 一起创建线程时,线程执行是继承Thread重写的run方法
public class Test7 {
public static void main(String[] args) {
//匿名内部类
/*
已经重写了父类的方法,就不会运行这一部分的代码,就不会运行Runnbale中实现的代码
if (target != null) {
target.run();
}
*/
new Thread(() ->{
System.out.println("执行Runnable重写的run()");
}){
@Override
public void run() {
System.out.println("执行Thread重写的run()");
}
}.start();
}
}
//输出
System.out.println("执行Thread重写的run()");
public class Test7 {
public static void main(String[] args) {
Runnable target = new MyRunnable();
Thread thread = new MyThread(target);
thread.start();
}
static class MyThread extends Thread{
private Runnable target;
public MyThread(Runnable target) {
this.target = target;
}
@Override
public void run() {
if (target != null) {
target.run();
}
System.out.println("执行Thread重写的run()");
}
}
static class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("执行Runnable重写的run()");
}
}
}
//输出
执行Runnable重写的run()
执行Thread重写的run()
start()
public synchronized void start() {
//一个线程只能运行一次,如果再运行,threadStatus!= 0 ,抛异常
if (threadStatus != 0)
throw new IllegalThreadStateException();
//添加进线程组
group.add(this);
boolean started = false;
try {
//调用native方法执行线程run方法
start0();
started = true;
} finally {
try {
if (!started) {
//启动失败,从线程组移除当前线程
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void star0();
线程拓展
join():顺序执行
thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。
比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。
方式一:
Thread t1 = new Thread(new Runnable() {
@Override public void run() {
System.out.println("t1 is finished");
}
});
Thread t2 = new Thread(new Runnable() {
@Override public void run() {
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t2 is finished");
}
});
Thread t3 = new Thread(new Runnable() {
@Override public void run() {
try {
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t3 is finished");
}
});
t3.start();
t2.start();
t1.start();
方式二:
Runnable runnable = new Runnable() {
@Override public void run() {
System.out.println(Thread.currentThread().getName() + "执行完成");
}
};
Thread t1 = new Thread(runnable, "t1");
Thread t2 = new Thread(runnable, "t2");
Thread t3 = new Thread(runnable, "t3");
try {
t1.start();
t1.join();
t2.start();
t2.join();
t3.start();
t3.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized和ReentrantLock的区别
synchronized是和if、else、for、while一样的关键字,ReentrantLock是类,这是二者的本质区别。既然ReentrantLock是类,那么它就提供了比synchronized更多更灵活的特性,可以被继承、可以有方法、可以有各种各样的类变量,ReentrantLock比synchronized的扩展性体现在几点上:
(1)ReentrantLock可以对获取锁的等待时间进行设置,这样就避免了死锁
(2)ReentrantLock可以获取各种锁的信息
(3)ReentrantLock可以灵活地实现多路通知
ReadWriteLock接口
读写锁:
允许多个用户读,但只允许一个用户写,以此来保持它的完整性;
首先明确一下,不是说ReentrantLock不好,只是ReentrantLock某些时候有局限。如果使用ReentrantLock,可能本身是为了防止线程A在写数据、线程B在读数据造成的数据不一致,但这样,如果线程C在读数据、线程D也在读数据,读数据是不会改变数据的,没有必要加锁,但是还是加锁了,降低了程序的性能。
因为这个,才诞生了读写锁ReadWriteLock。ReadWriteLock是一个读写锁接口,ReentrantReadWriteLock是ReadWriteLock接口的一个具体实现,实现了读写的分离,读锁是共享的,写锁是独占的,读和读之间不会互斥,读和写、写和读、写和写之间才会互斥,提升了读写的性能
public class Test16 {
public static void main(String[] args) {
MyLock mylock = new MyLock();
ExecutorService service = Executors.newCachedThreadPool();
ExecutorService service1 = Executors.newFixedThreadPool(20);
for (int i = 0; i < 20; i++) {
String I = String.valueOf(i);
if(i ==2 || i == 4){
service1.execute(()->{
mylock.put(I);
});
}else {
service1.execute(()->{
mylock.get();
});
}
}
service1.shutdown();
}
static class MyLock{
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock newLock = new ReentrantLock();
private final Lock readLock = lock.readLock();
private final Lock writeLock = lock.writeLock();
private List<String> list = new ArrayList<>();
public void put(String str){
writeLock.lock();
System.out.println(Thread.currentThread().getName()+"put,拿到锁");
try {
Thread.sleep(5000);
list.add(str);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
writeLock.unlock();
System.out.println(Thread.currentThread().getName()+"put,释放锁");
}
}
public void get(){
readLock.lock();
System.out.println(Thread.currentThread().getName()+"get,拿到锁");
try {
System.out.println(list);
} catch (Exception e) {
e.printStackTrace();
} finally {
readLock.unlock();
System.out.println(Thread.currentThread().getName()+"get,释放锁");
}
}
}
}
wait()和sleep()方法的不同
最大的不同是在等待时wait会释放锁,而sleep一直持有锁。Wait通常被用于线程间交互,sleep通常被用于暂停执行。
1.sleep方法是Thread类的静态方法,wait()是Object超类的成员方法
2.sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。在调用sleep()方法的过程中,线程不会释放对象锁。
而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备
3.sleep方法需要抛异常,wait方法不需要
4.sleep方法可以在任何地方使用,wait方法只能在同步方法和同步代码块中使用
wait()/notify()/nitifyAll()
1.wait()使当前线程阻塞,前提是 必须先获得锁,一般配合synchronized 关键字使用,即,一般在synchronized 同步代码块里使用 wait()、notify/notifyAll() 方法
2.由于 wait()、notify/notifyAll() 在synchronized 代码块执行,说明当前线程一定是获取了锁的。
当线程执行wait()方法时候,会释放当前的锁,然后让出CPU,进入等待状态。
只有当 notify/notifyAll() 被执行时候,才会唤醒一个或多个正处于等待状态的线程,然后继续往下执行,直到执行完synchronized 代码块的代码或是中途遇到wait() ,再次释放锁。
也就是说,notify/notifyAll() 的执行只是唤醒沉睡的线程,而不会立即释放锁,锁的释放要看代码块的具体执行情况。所以在编程中,尽量在使用了notify/notifyAll() 后立即退出临界区,以唤醒其他线程让其获得锁
3.wait() 需要被try catch包围,以便发生异常中断也可以使wait等待的线程唤醒。
4.notify 和wait 的顺序不能错,如果A线程先执行notify方法,B线程在执行wait方法,那么B线程是无法被唤醒的。
5.notify方法只唤醒一个等待(对象的)线程并使该线程开始执行。所以如果有多个线程等待一个对象,这个方法只会唤醒其中一个线程,选择哪个线程取决于操作系统对多线程管理的实现。notifyAll 会唤醒所有等待(对象的)线程,尽管哪一个线程将会第一个处理取决于操作系统的实现。如果当前情况下有多个线程需要被唤醒,推荐使用notifyAll 方法。比如在生产者-消费者里面的使用,每次都需要唤醒所有的消费者或是生产者,以判断程序是否可以继续往下执行。
6.要注意,notify唤醒沉睡的线程后,线程会接着上次的执行继续往下执行。所以在进行条件判断时候,可以先把 wait 语句忽略不计来进行考虑;显然,要确保程序一定要执行,并且要保证程序直到满足一定的条件再执行,要使用while进行等待,直到满足条件才继续往下执行。如下代码:
public class K {
//状态锁
private Object lock;
//条件变量
private int now,need;
public void produce(int num){
//同步
synchronized (lock){
//当前有的不满足需要,进行等待,直到满足条件
while(now < need){
try {
//等待阻塞
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我被唤醒了!");
}
// 做其他的事情
}
}
}
if/while 区别
只有当前值满足需要值的时候,线程才可以往下执行,所以,必须使用while 循环阻塞。注意,wait() 当被唤醒时候,只是让while循环继续往下走.如果此处用if的话,意味着if继续往下走,会跳出if语句块。
public class Test18 {
private static Object lock = new Object();
private static Integer num = 10;
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
Thread thread1 = new Thread1("thread1");
Thread thread2 = new Thread2("thread2");
service.scheduleAtFixedRate(()->{
--num;
synchronized (lock){
lock.notifyAll();
}
},1,1, TimeUnit.SECONDS);
thread1.start();
thread2.start();
}
static class Demo1{
public static void whileDemo(){
synchronized (lock){
while (num > 5){
System.out.println(Thread.currentThread().getName()+"开始执行,num:"+num);
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"被唤醒,num:"+num);
}
}
}
static class Thread1 extends Thread{
public Thread1(String name) {
super(name);
}
@Override
public void run() {
Demo1.whileDemo();
}
}
static class Demo2{
public static void ifDemo(){
synchronized (lock){
if (num > 5){
System.out.println(Thread.currentThread().getName()+"开始执行,num:"+num );
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"被唤醒,num:"+num);
}
}
}
static class Thread2 extends Thread{
public Thread2(String name) {
super(name);
}
@Override
public void run() {
Demo2.ifDemo();
}
}
}
//输出
thread2开始执行,num:10
thread1开始执行,num:10
thread1开始执行,num:9
thread2被唤醒,num:9
thread1开始执行,num:8
thread1开始执行,num:7
thread1开始执行,num:6
thread1被唤醒,num:5
if判断,被唤醒后直接顺序执行,然后线程执行完毕就停止了
while判断,只要条件成立,就一直执行,形成阻塞,然后知道条件不成立,跳出循环,线程执行完毕
线程实现生产者和消费者
public class Test19 {
public static void main(String[] args) {
AbstractStorage storage = new Storage();
ExecutorService service = Executors.newCachedThreadPool();
//消费
service.execute(new Produce(10, storage));
service.execute(new Produce(20, storage));
service.execute(new Produce(30, storage));
service.execute(new Produce(40, storage));
service.execute(new Produce(50, storage));
service.execute(new Produce(60, storage));
service.execute(new Produce(80, storage));
//生产
service.execute(new Consumer(60,storage));
service.execute(new Consumer(50,storage));
service.execute(new Consumer(40,storage));
service.execute(new Consumer(30,storage));
service.execute(new Consumer(10,storage));
service.execute(new Consumer(20,storage));
service.execute(new Consumer(80,storage));
service.shutdown();
}
interface AbstractStorage{
void consume(Integer num);
void produce(Integer num);
}
static class Storage implements AbstractStorage{
//仓库容量
private static final int MAX_SIZE = 100;
//仓库存储的载体
private List list = new LinkedList();
@Override
public void consume(Integer num) {
synchronized (list){
while (list.size() + num > MAX_SIZE){
System.out.println("待生产量:"+ num + "+ 库存量:" + list.size() + ">最大容量,暂不能生产");
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (Integer i = 0; i < num; i++) {
list.add(new Object());
}
System.out.println("已生产:"+num +",现仓库容量:"+list.size());
list.notifyAll();
}
}
@Override
public void produce(Integer num) {
synchronized (list){
while (num > list.size()){
System.out.println("待消费量:" + num +"> 库存量:"+list.size() + " 暂不能消费" );
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (Integer i = 0; i < num; i++) {
list.remove(0);
}
System.out.println("已经消费:" + num +",现库存量:"+list.size());
list.notifyAll();
}
}
}
//消费者
static class Produce implements Runnable{
private Integer num;
private AbstractStorage storage;
public Produce(){
}
public Produce(Integer num, AbstractStorage storage) {
this.num = num;
this.storage = storage;
}
@Override
public void run() {
storage.produce(this.num);
}
}
//生产者
static class Consumer implements Runnable{
private Integer num;
private AbstractStorage storage;
public Consumer(){
}
public Consumer(Integer num, AbstractStorage storage) {
this.num = num;
this.storage = storage;
}
@Override
public void run() {
storage.consume(this.num);
}
}
}
//输出
待消费量:20> 库存量:0 暂不能消费
待消费量:50> 库存量:0 暂不能消费
待消费量:80> 库存量:0 暂不能消费
待消费量:40> 库存量:0 暂不能消费
待消费量:30> 库存量:0 暂不能消费
待消费量:10> 库存量:0 暂不能消费
待消费量:60> 库存量:0 暂不能消费
已生产:60,现仓库容量:60
已经消费:60,现库存量:0
待消费量:10> 库存量:0 暂不能消费
已生产:10,现仓库容量:10
待消费量:30> 库存量:10 暂不能消费
待消费量:40> 库存量:10 暂不能消费
待消费量:80> 库存量:10 暂不能消费
待消费量:50> 库存量:10 暂不能消费
待消费量:20> 库存量:10 暂不能消费
已生产:50,现仓库容量:60
已经消费:20,现库存量:40
待消费量:50> 库存量:40 暂不能消费
待消费量:80> 库存量:40 暂不能消费
已经消费:40,现库存量:0
待消费量:30> 库存量:0 暂不能消费
已生产:80,现仓库容量:80
已生产:20,现仓库容量:100
已经消费:10,现库存量:90
待生产量:30+ 库存量:90>最大容量,暂不能生产
待生产量:40+ 库存量:90>最大容量,暂不能生产
已经消费:30,现库存量:60
待消费量:80> 库存量:60 暂不能消费
已经消费:50,现库存量:10
待消费量:80> 库存量:10 暂不能消费
已生产:40,现仓库容量:50
已生产:30,现仓库容量:80
已经消费:80,现库存量:0
自己调节生产和消费总数目,发现生产数量 >= 消费数量,线程能全不执行完毕,生产数量 < 消费数量,消费者线程有一个没有执行完毕,还是在等待状态