线程状态
- NEW 线程刚被创建,但还没调用start方法
- RUNNABLE 调用start方法后,包含运行状态和阻塞状态(如IO操作,此时不占用CPU资源)
- BLOCKED,阻塞,如synchronized的时候等待别的线程释放锁
- WAITING,等待,如调用join
- TIMED_WAITING,限时等待,如调用sleep
- TERMINATED,线程终止
线程安全分析
成员变量和静态变量是否线程安全?
如果没有共享,则线程安全,如果被共享了,但只是只读,则线程安全,如果有读写操作,则要考虑线程安全
从一个90%的人都答错的问题开始
我们知道,超卖是由于没有考虑并发问题造成的,所以要加锁。那扣用户积分是不是也要考虑并发问题呢?答案是肯定的。但如果用synchronized(this)效率是极其低下的,我们想要的效果是每个用户拥有自己的锁,这样不同用户之间就互不影响了。实现如下:
public void buyGoodsBySync(Integer userId, int integral) {
synchronized (userId) {
log.info("获取到锁");
User user = getById(userId);
//减去积分
user.setIntegral(user.getIntegral() - integral);
updateById(user);
log.info("释放锁");
}
}
这么写能实现想要的效果么?
答案是如果userId小于等于127能实现,大于127则不能实现。
要回答这个问题需要两个知识点,Integer的享元模式和synchronized的实现原理。
在[-128,127]之间,Integer会从IntegerCache中取,所以是同一个对象,超过127则不是同一个对象。
synchronized的实现原理
synchronized又叫对象锁,他只能把对象当成锁,所有java对象都有对象头,里面有个MarkWord区域,看下图,当Thread-2去获取锁时,会去obj的对象头的MarkWord区域看锁是否被占用,没有被占用则占用锁,并关联一个Monitor(又叫监视器或管程)对象,同时放入Owner中并计数,所以synchronized是可重入锁。这时Thread-1和Thread-3进来,发现锁被占用了,就放入EntryList中,进入blocked状态。等Thread-2处理完毕,系统随机在EntryList中选取一个线程占有锁并执行,因为并不是先进入等待队列就能优先获取锁所以synchronized是非公平锁。