并发编程
1.1
😭加油
2.1.进程和线程的区别
进程是操作系统分配资源的最小单位。
线程是依赖进程而存在的,线程是cpu调度的最小单位,也是进程的子集,是一段计算逻辑的cpu调度载体。
进程之间可以通过磁盘,网络进行通信,而同一个进程的资源都是共享的,所以线程之间可以直接通过内存来进行通信。
2.2.上下文切换
在实际应用中,cpu物理核数N是固定的,有时候看到的表象是有N+个线程在同时执行,是操作系统分配cpu执行时间片造成的假象。
线程1切换到线程2这个过程叫上下文切换。从线程1切换到线程2的时候,对于线程1要做的事情是保存现场(寄存器负责保存当前线程下的正在使用的数据,程序计数器负责记录指令已执行的行号),对于线程2要做得事情就是还原现场(通过寄存器和程序计数器来还原数据和当前要执行的指令)。
2.3.并行和并发
并行是同一时刻执行多个任务,但是多个任务都有独立的资源支撑。
并发是同一时刻执行多个任务,但是多个任务共享依赖同一个资源。
最终来看,并行是同时执行,并发是交替执行。
3.1.Java线程的创建和启动
main方法本身也是线程。
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
public class OnlyMain {
public static void main(String[] args) {
// Java虚拟线程的管理接口
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
// 不需要获取同步monitor和synchronized信息,仅获取线程和线程堆栈信息
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
for (ThreadInfo threadInfo : threadInfos) {
System.out.println("["+threadInfo.getThreadId()+"] ["+threadInfo.getThreadName()+"]");
}
}
}
[5] [Monitor Ctrl-Break] 监控Ctrl-Break中断信号的
[4] [Signal Dispatcher] 分发处理发送给JVM信号的线程
[3] [Finalizer] 调用对象finalize方法的线程
[2] [Reference Handler] 清除Reference的线程
[1] [main] main线程,用户程序入口
创建线程方式
1:使用或者继承Thread类
public static void main(String[] args) { Thread thread = new Thread(() -> System.out.println(Thread.currentThread().getName() + ":运行线程"), "t1"); thread.start(); }
2.实现Runnable接口配合Thread类
public static void main(String[] args) { Runnable runnable = () -> { System.out.println(Thread.currentThread().getName() + ":运行线程"); }; Thread thread = new Thread(runnable,"t2"); thread.start(); }
3.实现Callable接口配合FutureTask类和Thread类,这种方式可以获取线程中的返回值和异常
public static void main(String[] args) throws ExecutionException, InterruptedException { Callable<String> callable = () -> { System.out.println(Thread.currentThread().getName() + ":运行线程"); return "success"; }; FutureTask<String> futureTask = new FutureTask<>(callable); Thread thread = new Thread(futureTask, "t3"); thread.start(); String state = futureTask.get(); System.out.println("返回状态:" + state); }
3.2.Java线程的生命周期
线程在操作系统层面有5种状态,在Java层面有6种状态。
操作系统:
新建:仅是在语言层面创建了线程对象,还未与操作系统线程关联。
就绪:该线程已与操作系统线程完成了关联,可以由CPU调度执行了。
运行:获取了CPU时间片运行中的状态,当CPU时间片用完,会从【运行状态】转换至【就绪状态】,会导致线程的上下文切换。
阻塞:如果在线程执行中发生了IO,会从【运行状态】转换至【阻塞状态】,会导致线程的上下文切换,等IO完成之后会由操作系统唤醒阻塞的线程切换至【就绪状态】,只要阻塞状态的线程一直不被唤醒,就一直不会被调度执行。
死亡:表示线程已经执行完毕,生命周期已经结束,不会再切换状态。
Java层面:
初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
运行(RUNNABLE):Java线程将就绪(ready)和运行中(running)统称为运行状态,线程在等待CPU时间片分配的过程被称为ready,线程在获取CPU时间片执行的过程被称为running。
阻塞(BLOCKED):线程在等待获取锁的过程叫阻塞状态。
等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
终止(TERMINATED):表示线程执行完毕。
public enum State { /** * Thread state for a thread which has not yet started. */ NEW, /** * Thread state for a runnable thread. A thread in the runnable * state is executing in the Java virtual machine but it may * be waiting for other resources from the operating system * such as processor. */ RUNNABLE, /** * Thread state for a thread blocked waiting for a monitor lock. * A thread in the blocked state is waiting for a monitor lock * to enter a synchronized block/method or * reenter a synchronized block/method after calling * {@link Object#wait() Object.wait}. */ BLOCKED, /** * Thread state for a waiting thread. * A thread is in the waiting state due to calling one of the * following methods: * <ul> * <li>{@link Object#wait() Object.wait} with no timeout</li> * <li>{@link #join() Thread.join} with no timeout</li> * <li>{@link LockSupport#park() LockSupport.park}</li> * </ul> * * <p>A thread in the waiting state is waiting for another thread to * perform a particular action. * * For example, a thread that has called <tt>Object.wait()</tt> * on an object is waiting for another thread to call * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on * that object. A thread that has called <tt>Thread.join()</tt> * is waiting for a specified thread to terminate. */ WAITING, /** * Thread state for a waiting thread with a specified waiting time. * A thread is in the timed waiting state due to calling one of * the following methods with a specified positive waiting time: * <ul> * <li>{@link #sleep Thread.sleep}</li> * <li>{@link Object#wait(long) Object.wait} with timeout</li> * <li>{@link #join(long) Thread.join} with timeout</li> * <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li> * <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li> * </ul> */ TIMED_WAITING, /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED; }
3.3.sleep方法
调用之后会将线程状态从RUNNABLE转换成TIMED_WAITING,并且不会释放对象锁,其他现场调用interrupt方法可以中断睡眠并抛出异常。
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("执行完成");
},"t1");
t1.start();
log.info("线程t1的执行状态:"+t1.getState());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("线程t1的执行状态:"+t1.getState());
t1.interrupt();
}
3.4.线程的优先级
在Java中是可以给线程设置优先级的,优先级范围是1~10,默认优先级一般都是5,调度器一般会忽略优先级,众生平等。
public static void main(String[] args) { Runnable r1 = () -> { int count = 0; for (; ; ) { log.info("r1 -> {}", count); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }; Runnable r2 = () -> { int count = 0; for (; ; ) { log.info("r2 -> {}", count); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }; Thread t1 = new Thread(r1,"t1"); t1.setPriority(Thread.MIN_PRIORITY); Thread t2 = new Thread(r2,"t2"); t2.setPriority(Thread.MAX_PRIORITY); t1.start(); t2.start(); }
3.5.join方法
调用子线程的join方法之后主线程会进行阻塞,直到子线程执行完成之后,主线程才会继续执行,一般用于等待异步线程执行完结果之后才能继续运行的场景。
public static int count = 0; public static void main(String[] args) { log.info("开始执行"); Thread t1 = new Thread(()->{ try { Thread.sleep(3000); count++; Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } }); t1.start(); try { t1.join(); } catch (InterruptedException e) { e.printStackTrace(); } log.info("最终结果:{}",count); } 看执行时间相差六秒 22:05:10.246 [main] INFO com.data.source.service.impl.UserServiceImpl - 开始执行 22:05:16.291 [main] INFO com.data.source.service.impl.UserServiceImpl - 最终结果:1 Process finished with exit code 0
3.6.守护线程
主死从随,主线程是主守护现场是从,类似golang的线程和协程的关系
守护线程常用的场景:
jvm垃圾回收线程,当用户线程结束不再产生垃圾,守护线程自然会结束。
中间件的心跳检测,事件监听,当任务终止之后,守护线程自然会结束。
public static void main(String[] args) { log.info("主线程开始执行"); Thread t1 = new Thread(()->{ try { log.info("子线程开始执行"); Thread.sleep(3000); log.info("子线程执行结束"); } catch (InterruptedException e) { e.printStackTrace(); } }); t1.setDaemon(true); t1.start(); log.info("主线程执行结束"); } 22:12:42.917 [main] INFO com.data.source.service.impl.UserServiceImpl - 主线程开始执行 22:12:42.950 [main] INFO com.data.source.service.impl.UserServiceImpl - 主线程执行结束 22:12:42.950 [Thread-0] INFO com.data.source.service.impl.UserServiceImpl - 子线程开始执行
3.7.利用中断机制正确终止线程
t1线程正常中断,中断状态不会被重置
public static void main(String[] args) { Thread t1 = new Thread(() -> { while (true) { Thread currentThread = Thread.currentThread(); boolean interrupted = currentThread.isInterrupted(); if (interrupted) { break; } log.info("任务执行完成.."); } }); t1.start(); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } t1.interrupt(); log.info("t1线程的中断状态:{}", t1.isInterrupted()); }
21:52:11.241 [Thread-0] INFO com.data.source.service.impl.UserServiceImpl - 任务执行完成..
21:52:11.242 [main] INFO com.data.source.service.impl.UserServiceImpl - t1线程的中断状态:true
如果线程出现了阻塞状态[Thread.sleep() Thread.join() 对象锁obj.wait()],使用中断之后会抛出中断异常并将中断标识位清除为flase
public static void main(String[] args) { Thread t1 = new Thread(() -> { while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); break; } log.info("任务执行完成.."); } }); t1.start(); t1.interrupt(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } log.info("t1线程的中断状态:{}", t1.isInterrupted()); } java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at com.data.source.service.impl.UserServiceImpl.lambda$main$1(UserServiceImpl.java:195) at java.lang.Thread.run(Thread.java:750) 22:02:57.694 [main] INFO com.data.source.service.impl.UserServiceImpl - t1线程的中断状态:false
public static void main(String[] args) {
Object o = new Object();
Thread t1 = new Thread(() -> {
while (true) {
synchronized (o){
try {
o.wait();
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
log.info("任务执行完成..");
}
}
});
t1.start();
t1.interrupt();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("t1线程的中断状态:{}", t1.isInterrupted());
}
java.lang.InterruptedException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at com.data.source.service.impl.UserServiceImpl.lambda$main$1(UserServiceImpl.java:197)
at java.lang.Thread.run(Thread.java:750)
22:10:29.412 [main] INFO com.data.source.service.impl.UserServiceImpl - t1线程的中断状态:false