intro:读马士兵《Java高并发编程》记录
推荐http://www.cnblogs.com/andy-zhou/p/5339683.html
JMM
synchronized
lambda
new Thread(()->t.m1(),"t1").start();
new Thread((t::m1,"t1").start();
new Thread(new Runnable(){
@Override
public void run(){
t.m1();
}
}).start();
- Synchronized(T.class){} 类似与 public synchronized static void
m()注意静态方法不能锁this对象 - 执行有锁方法可以同时执行无锁方法;
- 业务读和业务写都要加锁,避免脏读(dirtyRead);
- synchronized获得的锁是可重入的;特殊情形:子类同步方法调用父类同步方法也可以
- 出现异常默认情况下锁会被释放,一定要try catch
- 简单程序模拟死锁
public void run(){
System.out.println(flag);
if(flag == 0){
synchronized(o1){
try{
Thread.sleep(500);
} catch(Exception e){
e.printStackTrace();
}
synchronized(o2){
}
}
}
if(flag == 1){
synchronized(o2){
try{
Thread.sleep(500);
} catch(Exception e){
e.printStackTrace();
}
synchronized(o1){
}
}
}
}
voltile
volatile 不同线程间的内存(变量)可见性(理解为线程间通信,轻量级同步),但不能保证原子性(不如Synconized),但效率高,避免数据脏读
因为a=a+count(a++也是)包含了好几步操作,而此时多个线程的执行是无序的,因为没有任何机制来保证多个线程的执行有序性和原子性。volatile存在的意义是,任何线程对a的修改,都会马上被其他线程读取到,因为直接操作主存,没有线程对工作内存和主存的同步。所以,volatile的使用场景是有限的,在有限的一些情形下可以使用 volatile 变量替代锁。要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件:1)对变量的写操作不依赖于当前值。2)该变量没有包含在具有其他变量的不变式中
比如说,a = 10;b = true ;不用加锁,赋值原子性,但是a++和b=!b需要。 这个时候可以用AtomicInteger(计算原子性和数据可见性),系统底层实现,用incrementAndGet去替代++;多个AtomicX之和不具有原子性,因为只保证递加是,但这段程序并不是,要用Synchronized程序段给方法业务逻辑中真正需要加锁的地方加锁,细粒度的锁优化速度
锁的对象发生改变,可以影响到需要之前锁的线程
不要以字符串常量作为锁
线程间通讯:
用wait-notify写很费劲
wait 释放锁,通常和while一起出现
notify 而不会释放锁,提醒处于wait状态的线程,观察者模式
notifyAll通知全体,生产消费要用,effect永远用notifyAll
sleep也不会释放锁
recommend:CountdownLatch
和Voltile相反,如果自己线程不想被别人知道,可以用:
ThreadLocal是空间换时间,即a和b线程各自保存一份;Synchronized是时间换空间,也就是a访问完b再来
ReentrantLock
- 手动上锁,手动解锁
- 可以根据tryLock的结果分别执行
- lockInterruptibly();去申请锁,但运行主线程来打断
- ReentrantLock(true);true表示公平锁,谁等的长谁拿lock
- NewCondition() await与signalAll可以精确指定