线程退出
- 尽量少用stop()方法(已过时方法),因为该方法会直接终止线程,释放持有的锁,从而无法保证事物的一致性。
如下伪代码,就会造成User的id和name不是同一个值,因为可能没设置好setName操作就强制退出释放锁了:
public class TestStop {
private static User u = new User();
public static class Cc extends Thread{
@Override
public void run() {
synchronized (u){
Long v= System.currentTimeMillis();
u.setId(v);
//执行其他业务
u.setName(v.toString());
}
}
}
@Test
public void t(){
while (true){
Thread t = new Cc();
t.start();
t.stop();
}
}
}
- 可以通过调用interrupt()方法中断线程,中断后当前线程的isInterrupted()会变为true,然后根据sInterrupted()的状态去做一些操作退出这个线程。需要注意的是如果线程调用了Thread.sleep(),会因中断而抛出异常,它会清除掉中断标记,所以有时需要在catch中重新调用interrupt().
wait(),sleep()与suspend()的比较
- wait()需要在synchronized语句中使用,通过notify()唤醒其中一条wait的线程,
- sleep会让线程陷入等待状态,但是不会像wait那样释放锁
- suspend()(已过时方法,因为如果操作不当可能导致线程永远挂起,不释放锁)即挂起线程,这个时候线程会陷入等待状态,但是不会释放锁,需要通过resume()才能继续执行
join
- join()表示无限等待,它会一直阻塞当前线程,直到目标线程执行完毕
- join(long mills),如目标线程执行时间超过了mills,当前线程会因为“等不及”继续执行下去
- join的核心实现代码为:
//如果没有结束就wait
while (isAlive()){
wait(0)
}
yield
执行yield (),会让出cpu资源,相当于把它当做一个优先级很低的线程来执行
守护线程Daemon
像垃圾回收那样的线程,在后台执行。如果除了守护线程没有其他线程在执行,守护线程会自动退出。把一个线程变成守护线程的操作为:
Thread t = new Cc();
t.setDaemon(true);
错误的加锁
不要做类似
private static Integer i = 0;
public static class Cc extends Thread{
@Override
public void run() {
synchronized (i){
i++;
}
}
因为Integer是不可变对象,i++;实际为
i = Integer.valueOf(i.intValue()+1);
这样会导致线程加锁的对象不一致,导致临界区代码控制出现问题