一、 为什么要多线程
-
CPU和IO设备之间的速度存在很大的差异,提高CPU利用率
-
提高服务端并发量
线程安全问题:
有共享数据的情况下使用多线程可能会导致线程安全问题
原子性:时间片轮转导致
可见性:CPU和内存之间有缓存/工作内存和主内存
有序性:指令重排序
实现线程安全的方法:
互斥同步:悲观
非阻塞同步:乐观
无同步方案:避免共享数据
二、Java线程的实现
HotSpot的每一个java线程都直接映射到一个操作系统原生的线程来实现
也就是说,线程的调度是完全交给操作系统来实现的
java使用抢占式调度,可以主动让出CPU资源(如yield()方法),但没法主动获取CPU
只有Thread.start()
方法可以调用本地方法来新建一个线程
三、Java线程状态
new:创建后尚未启动
runnable:对应操作系统线程的Running和Ready两种状态,Runnable状态的线程可能正在执行,也可能在等待操作系统分配时间片
waiting:需要等待其他线程显示唤醒,否则不会被分配时间片
timed waiting:一定时间后会由系统自动唤醒
blocked:程序等待进入同步区域时会进入该状态,等待获取一个排他锁(等待是主动的,阻塞是被动的)
terminated:线程已终止
三、线程的使用:
中断机制:
(下文所说的“阻塞”是指OS层面的线程阻塞,而非上述的blocked状态)
interrupt()
If this thread is blocked in an invocation of the wait(), wait(long), or wait(long, int) methods of the Object class, or of the join(), join(long), join(long, int), sleep(long), or sleep(long, int), methods of this class, then its interrupt status will be cleared and it will receive an InterruptedException.
如果一个线程因为Object.wait()
,Thread.join()
,Thread.sleep()
这几个方法而阻塞时,调用该线程的interrupt()
方法,会清除线程的中断状态,同时该方法会抛出一个InterruptedException
。
这里要注意的是,如果线程是处于上述的阻塞状态,中断状态会被清除,在抛出异常后再调用isInterrupted()
返回false,但是如果线程并不处于上述的阻塞状态,则中断状态会被置位。
isInterrupted()
判断该线程是否被中断,也就是线程是否被置位,并不会影响中断状态。
Thread.interrupted()
静态方法,判断该线程是否被中断,清楚中断状态
该方法常用于判断当前线程是否中断
我的理解:(仅供参考)
笔者以为,这里中断的含义可以理解为中断阻塞,上述的线程抛出InterruptedException
异常,实际上已经重新得到了时间片,结束了阻塞。另外,由于LockSupport.park()
方法阻塞的线程在被中断后,也会结束阻塞重新进入调度。
// interrupt方法强行中断了Thread.sleep()的阻塞
public class MyThread implements Runnable{
Dog dog=new Dog();
@Override
public void run() {
long start = System.currentTimeMillis();
try {
Thread.sleep(5000); //线程阻塞5s
} catch (InterruptedException e) {
e.printStackTrace();
}
long stop = System.currentTimeMillis();
System.out.println(stop-start); //输入耗时
}
}
public class Test {
public static void main(String[] args){
Runnable myThread=new MyThread();
Thread myThread1=new Thread(myThread,"myThread1");
myThread1.start();
myThread1.interrupt();
}
}
//结果输出时间为0,说明线程提前结束阻塞
当然这里需要强调的是,interrupt无法中断synchronized阻塞和IO阻塞。事实上从JAVA线程状态来看,只有这两种状态才是blocked,上述的可被中断的阻塞都是waiting或timed waiting。
另外,还可以用上述的3个方法自己实现一些逻辑,例如通过判断是否中断来结束死循环等,不再赘述。
线程的协作:
-
Thread
join()
t1.join();
会让当前运行的线程阻塞,直到t1线程死亡,当前线程才重新进入调度。底层是用
Object.wait()
实现的
-
Thread.sleep()
不会释放锁,会响应中断
-
Object.wait()/Object.notify()
必须在synchronized中使用,Object必须是持有Monitor的对象
会释放锁,(release the ownership of this monitor)
会响应中断
-
Condition
await()/signal()
底层是用LockSupport实现的
阻塞和唤起必须是同一个Condition对象
会释放Lock
-
LockSupport.park()/Locksupport.unpark()
可以在任何地方调用,和锁没有关系
先unpark(),再park()不会阻塞
线程中断会唤起该线程重新参与调度
四、参考资料
- 《java核心技术卷Ⅰ》
- 《深入理解虚拟机:JVM高级特性与最佳实践(第三版)》
- Java 并发 - 线程基础 | Java 全栈知识体系 (pdai.tech)
鄙人只是一名在读的软件工程专业的本科生,正在复习找工作,故而将复习时遇到的一些有意思的东西总结出来,既是加深理解,也是便于日后复习。
鄙人才疏学浅,若文中有谬误之处,还望诸位不吝斧正,以免误人子弟。若有同道中人想一同讨论学习,也可以联系我=>2938189276@qq.com
路漫漫其修远兮,吾将上下而求索。