多线程编程
2020-12-27 本文已影响0人
NengLee
什么是进程?
进程可以说是一个"执行中程序",是操作系统进行资源分配和调度的一个地独立单位,是应用程序运行的载体。
什么是线程?
线程(thread)是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。
类比OOP思想,进程如果是火车,那么线程就是车厢
区别
- 线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位,线程执行任务,进程不执行是载体
- 一个进程有一个或者多个线程组成,线程是一个进程中代码的不同执行路线
- 进程之间相互独立,但是同一个进程下个各个线程共享程序内存空间
- 调度和切换:线程上下文切换比进程上下文切换的要快
多线程
多线程是指在单位程序中可以同时运行多个不同线程,执行不同任务
比单线程更具有优势:
- 更高的运行效率
- 多线程是模块化的编程模型
- 与进程相比,线程的创建和切换开销更小
并行和并发
Erlang 之父 Joe Armstrong 用一张5岁小孩都能看懂的图解释了并发与并行的区别
并行与并发 并行:同时执行任务。
并发:交替运行任务。
不能脱开时间线概念,例如一分钟并发量,一分钟的时间内有多少任务执行完毕。
多线程创建方式
-
继承Thread,重写run方法
-
实现Runnable接口,重写run方法
区别: Thread是对线程的程序,Runnable对任务/逻辑的抽象
如何停止终止线程
1. stop() 具有暴力销毁
@Deprecated
public final void stop() {
stop(new ThreadDeath());
}
@Deprecated
public final void stop(Throwable obj) {
throw new UnsupportedOperationException();
}
public class ThreadDeath extends Error {
private static final long serialVersionUID = -4417128565033088268L;
}
- 虽然打上
@Deprecated
,官方不建议用,非常危险不可控,随时在run
某个执行点终止销毁,并且会在任务中持有的锁全部释放。即线程不安全! - 如何理解释放锁,假设在多线程编程中,A-Thread的
run
持有外部资源集合list
加锁,同时B、C-Thread也在对list
进行操作。突然对A-Thread进行了Stop
,这么就间接对list
锁的释放,于此同时导致B、C-Thread操作数据错乱。
2. interrupt()、interrupted()、isinterrpted() 标志函数
-
interrupt
方法:用于中断线程,标记设置为true
,但是不会停止线程,在中断状态下如果线程处于阻塞状态(sleep/wait/join...),就会产生InterruptedException
异常,并且线程处于阻塞,会立即中断标识立即清除为false
-
interrupted
方法:测试当期线程是否处于中断状态,是返回true
,并且调用后立即将中断标识清除false
-
isinterrpted
方法:作用域该方法的线程对象所属的对应线程,(线程对象对应的线程不一定是当期运行的线程),调用后不会清除中断标记
interrupt()... 源码
/**
*将线程的中断标记设置为true,但不会停止线程
*/
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
}
/**
*静态获取,测试当前线程(当前线程是指运行interrupted()方法的线程)是否已经中断,且清除中断状态
*/
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
/**
*测试线程(调用该方法的线程)是否已经中断,不清除中断状态。
*/
public boolean isInterrupted() {
return isInterrupted(false);
}
/**
* privat - 设置
*/
private native boolean isInterrupted(boolean ClearInterrupted);
- 在
sleep,wait,join
等,就会中断抛InterruptException
异常,就会清除中断标志位isInterrupted()
状态 - 解决方案需要在
catch
中手动重置Thread.currentThread().interrupt();
参考:
@Test
public void testMain() throws InterruptedException {
MyThread myThread = new MyThread();
myThread.start();
System.out.println("=== testMain1 :" + myThread.getName() + " " + myThread.isInterrupted());
myThread.interrupt(); //中断线程,标识
System.out.println("=== testMain2 :" + myThread.getName() + " " + myThread.isInterrupted());
}
class MyThread extends Thread {
@Override
public void run() {
System.out.println("继承Thread,重写run方法" + Thread.currentThread().getName());
System.out.println(Thread.currentThread().getName() + " Top interrupt is " + isInterrupted());
while (true) {
try {
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + " while try interrupt: " + isInterrupted());
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(Thread.currentThread().getName() + " while catch interrupt: " + isInterrupted());
Thread.currentThread().interrupt(); //手动修改处
System.out.println(Thread.currentThread().getName() + " while catch interrupt: " + isInterrupted() + " (手动修改后)");
}
System.out.println("run End while");
}
}
}
Logs输出
线程的生命周期
- 新建:
new Thread
- 就绪:等待时间轮转机制ing
- 运行:
run
- 阻塞:cpu挂起当前线程
sleep (long millis)
wait (long timeout)
- 死亡:任务完成即销毁
start()
启动线程之后才有关联起来,不是new Thread()
关联,这里只获取对象 Thread parent = currentThread();
join()
关联线程,串行可以调度 xxxThread等待就绪的线程,在此优先执行
yield()
让当前Thread交时间片,CPU重新(包括当期让出)交给其他Thread
sleep(millis)
当前Thread处于阻塞,挂起Time后在就绪,重新等待CPU时间轮转
-
interrupt()
标志中断线程作用
wait()
Object对象的方法,如果没有设置时间,需要通过notify
进行唤醒
stop()
暴力销毁线程,会释放资源锁,如在多线程会出现不可Error
setDeamon()
设置当前线程为守护线程,依赖其他线程,如进程中的所有线程执行完毕,守护线程也会跟着销毁,多用于辅助维护线程