对象和Java多线程
1、缺省对象都是继承java.lang.Object
2、也可以特别继承java.lang.Thread ;
3、或实现java.lang.Runnable接口
4、对象的方法以线程方式执行。
线程的主内存和工作内存
问题?
如何保证内存计算一致性
1、缓存一致性
当一个线程更新了自己工作内存中的数据后,没有写到主内存,其他线程是不知道的。 (1)顺序一致性模型: 要求对改变的值立即进行传播, 并确保该值被所有其他线程接受后, 才能继续执行其他指令. (2) 释放一致性模型: 允许线程将改变的值延迟到锁释放时才进行传播.
a、获取对象监视器的锁(lock)
b、清空工作内存数据, 从主存复制变量到当前工作内存, 即同步数据 (read and load)
c、 执行代码,改变共享变量值 (use and assign)
d、 将工作内存数据刷回主存 (store and write)
e、 释放对象监视器的锁 (unlock)
happens-before ordering实现
线程安全模式
线程安全模式
1、尽量不使用synchronized锁,锁是耗费资源和性能的。
2、首先 编写那些不用任何特别处理的线程安全代码,比如不变性代码。
3、使用producer-observer模式。
4、其次:使用Visibility 使资料对所有线程可见。
5、最后:使用JVM优化的锁。
单值更新
1、使用Atmoic原子特性API:
2、Atomic{Integer|Long}.compareAndSet().
3、使用CAS实现机制的API。
4、AtomicReference.compareAndSet()实现不变性对象内部的组合更新。
immutable 不可变模式
Publishing发布公开对象
public static Set<Secret> knownSecrets;
public void initialize() {
knownSecrets = new HashSet<Secret>();
}
由于外界可以访问knownSecrets 并且修改,那么knownSecrets 相当于脱离当前对象的scope生命周期,变成escaped 逃脱了。
安全的发布公开对象模式
天然的线程安全
1、Hashtable, synchronizedMap, or Concurrent-Map
2、Vector, CopyOnWriteArrayList, CopyOnWrite-ArraySet, synchronizedList, or synchronizedSet
3、BlockingQueue or a ConcurrentLinkedQueue
Visibility/NoVisibility模式
线程更新的是自己私有空间变量,需要更新到主内存空间,一个线程修改的结果对于另外一个线程是NoVisibility :
class RealTimeClock {
private int clkID;
public int clockID() { return clkID; }
public void setClockID(int id) { clkID = id; }
}
Thread 1 calls the setClockID method, passing a value of 5.
Thread 2 calls the setClockID method, passing a value of 10.
Thread 1 calls the clockID method, which returns the value 5.
出现明明修改某个字段值,但是刷新还是旧值。
多线程访问同一资源
volatile
@NotThreadSafe
public class NumberRange {
private int lower, upper;
public int getLower() { return lower; }
public int getUpper() { return upper; }
public void setLower(int value) {
if (value > upper) throw new IllegalArgumentException(...);
lower = value; }
public void setUpper(int value) {
if (value < lower) throw new IllegalArgumentException(...);
upper = value;
}
}
}
Volatile缺点
使用final替代Volatile
原子操作模式
锁模式
重进入(Reentrancy)
当一个线程请求其他线程已经占有的锁时,请求线程将被阻塞 。线程在试图获得它自己占有的锁时,请求会成功 .
public class Widget {
public synchronized void doSomething() {
}}
public class LoggingWidget extends Widget {
public synchronized void doSomething() {
System.out.println(toString() + ": calling doSomething");
super.doSomething();
}}
Reentrancy好处
子类覆写了父类synchronized类型的方法,并调用父类中的方法。如果没有可重入的锁,这段看上去很自然的代码就会产生死锁。
cheap read-write lock
public class CheesyCounter {
// Employs the cheap read-write lock trick // All mutative operations MUST be done with the 'this' lock held @GuardedBy("this")
private volatile int value;
public int getValue() { return value; }
public synchronized int increment() {
return value++;
}
}
ReentrantReadWriteLock
public void set(String key, String value) {
write.lock();
try {dictionary.put(key, value);}
finally {write.unlock();}
}
public String get(String key) {
read.lock();
try {return dictionary.get(key);}
finally {read.unlock();}
}
何时用
1、如果需要timed, polled, 或可中断 lock, fair queueing, non-block-structured locking.就是要ReentrantReadWriteLock
2、否则使用 synchronized.
案例:如何实现集合的边读边改
联系人名单集合,发送Email
public void sendMessages(Map contactMap) {
sendEmail(contactMap.values());
}
设计新的不可变集合
使用新不可变集合类
状态和值对象
ThreadLocal
数据库连接放入ThreadLocal
private static ThreadLocal<Connection> connectionHolder =
new ThreadLocal<Connection>() {
public Connection initialValue() {
return DriverManager.getConnection(DB_URL); }
} ;
public static Connection getConnection() {
return connectionHolder.get();
}