2 中多线程的方式:
- 继承 Thread 类, 重写 run 方法
- 实现 Runnable 接口, 实现 run 方法
调用 start() 方法启动线程。
线程执行步骤:
启动 ---start()--> 就绪 --获得 CPU --> 执行 ---wait()---> 阻塞 ---notify()---> 执行 ---stop()---> 死亡
- join() 方法: 当前线程阻塞,加入的线程先执行
- yeild() 方法: 放弃当前 CPU 执行权,让优先级高的线程先执行,当前线程仍处于 就绪 状态,仍然可以竞争 CPU
- sleep(millis) 方法: 线程方法,当前方法进入阻塞状态,不是放同步锁, 时间到后, 继续执行。
- wait()方法: 被锁对象方法,当前方法进入阻塞状态,释放当前同步锁, 等待 notify() 方法唤醒
- notify()方法: 通知当前对象的阻塞资源, 进入就绪状态。
- 方法加
synchronized关键字, 锁的是当前对象,并不是方法。 - synchronized(object) {...} , 给 object 加同步锁, 锁定当前代码块。
Thread a, b; Object A, B;
Thread a 持有 B 的同步锁, 同时需要获取 A 的锁; Thread b 持有 A 的同步锁,同时需要获取 B 的锁;
Thread a 想要给 A 加锁,但发现 A 被 Thread b 已经加锁了, 所以等待 Thread b 释放 A 的锁; 同理, Thread b 想要给 B 加锁,但发现 Thread a 已经加锁了,所以也等待 Thread a 释放 B 的锁。 从而造成死锁。
- 一个简单的死锁类
- 当DeadLock类的对象flag==1时(td1),先锁定o1,睡眠500毫秒
- 而td1在睡眠的时候另一个flag==0的对象(td2)线程启动,先锁定o2,睡眠500毫秒
- td1睡眠结束后需要锁定o2才能继续执行,而此时o2已被td2锁定;
- td2睡眠结束后需要锁定o1才能继续执行,而此时o1已被td1锁定;
- td1、td2相互等待,都需要得到对方锁定的资源才能继续执行,从而死锁。
避免死锁:
- 加锁顺序(线程按照一定的顺序加锁)
- 加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
- 死锁检测