线程安全要符合两点:原子性&可见性(加入个人理解:一致性,主内存和和线程内存的一致性) 结合这两点展开对volatile和synchronize的理解
-
使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
-
使用stop方法强行终止线程(这个方法不推荐使用,因为stop和suspend、resume一样,也可能发生不可预料的结果)因为stop会释放锁,数据一致性得不到保障。
-
使用interrupt方法中断线程。
(1)线程处于阻塞状态,如使用了sleep方法。 (2)使用while(!isInterrupted()){…throw new InterruptedException…} (推荐方法:因为异常可以向上抛出,事件可以传播) (3)interrupt与return结合(不能像使用throw new InterruptedException那样传播事件)
suspend、resume为什么废弃?
- 使用不当,造成同步方法独占,其他线程无法访问,造成死锁。
- 容易出现线程的暂停导致数据不同步。 Thread.suspend 天生容易引起死锁。如果目标线程挂起时在保护系统关键资源的监视器上持有锁,那么其他线程在目标线程恢复之前都无法访问这个资源。如果要恢复目标线程的线程在调用 resume 之前试图锁定这个监视器,死锁就发生了。这种死锁一般自身表现为“冻结( frozen )”进程。
GC是一个守护线程
关于变量是否线程安全的问题: int num = 0在add方法外,如果多个run方法调用add方法时:非安全。 如果num = 0 在方法里面,则安全。
如果对这个num进行set get value,都要加synchronize,只对set加不对get加synchronize,那么另外的线程在get时会出现“脏读”。 可以参考《java并发编程》65页。
上面这一块儿也挺重要的,仔细阅读。
synchronize锁重入:自己可以再次获取自己的内部锁。
synchronize代码块带你的同步性问题,79也了解一下,第一遍不太懂。
package com.sankuai.qcs.risk.web.service;
/**
- Description:
- @author: zhangleilei
- @date: 2018/2/5 */
public class Test { public static void main(String[] args) { Add[] adder = new Add[10]; for (int i = 0; i < 10; i++) { adder[i] = new Add(); adder[i].start(); } } }
class Add extends Thread { volatile public static int count = 0;
synchronized public void add() { for (int i = 0; i < 10; i++) { count++; } System.out.println("Thread:" + Thread.currentThread().getName() + ",Count:" + count); }
@Override
public void run() {
super.run();
add();
}
}
Thread:Thread-0,Count:10 Thread:Thread-1,Count:20 Thread:Thread-2,Count:30 Thread:Thread-3,Count:40 Thread:Thread-4,Count:50 Thread:Thread-5,Count:60 Thread:Thread-6,Count:70 Thread:Thread-7,Count:80 Thread:Thread-8,Count:90 Thread:Thread-9,Count:100