Java Synchronized 塊
Synchronized塊是用來解決race condition屿附。Synchronized塊是基于對象碗殷,作用在同一對象的同步快保證在同一時間只有一個線程處理。Synchronized關(guān)鍵字可以作用于一下四種情況:
- 實(shí)例方法
- 靜態(tài)方法
- 實(shí)例方法內(nèi)的代碼塊
- 靜態(tài)方法內(nèi)的代碼塊
Synchronized Instance Methods
public synchronized void add(int value){
this.count += value;
}
實(shí)例方法加synchronized是同步的其擁有該方法的對象祟霍。如果有多個實(shí)例對象押搪,那么每個對象在同一時間都只能有一個線程執(zhí)行同步方法。
Synchronized Static Methods
public static synchronized void add(int value){
this.count += value;
}
靜態(tài)方法加synchronized是同步其擁有該方法的類浅碾。由于一個JVM里面只有一個類大州,所以在同一個類中只能有一個線程執(zhí)行同步方法。
Synchronized Blocks in Instance Methods
public void add(int value){
synchronized(this){
this.count += value;
}
}
實(shí)例方法內(nèi)的代碼塊通過指定加鎖對象(monitor object)來進(jìn)行同步垂谢。有時候不需要對整個方法加鎖厦画,只需要對方法內(nèi)的一部分加鎖,可以使用該方法滥朱。
Synchronized Blocks in Static Methods
public class MyClass {
public static synchronized void log1(String msg1, String msg2){
log.writeln(msg1);
log.writeln(msg2);
}
public static void log2(String msg1, String msg2){
synchronized(MyClass.class){
log.writeln(msg1);
log.writeln(msg2);
}
}
}
靜態(tài)方法內(nèi)的代碼塊同步的是擁有該方法的類根暑。在上述例子中,一個線程在同一時刻只能執(zhí)行一個方法徙邻。如果log2同步快內(nèi)加鎖的是不同的類排嫌,那么一個線程可以同時執(zhí)行這兩個方法。
Java Volatile關(guān)鍵字
volatile關(guān)鍵字保證變量的可見性
多線程應(yīng)用在處理非volatile變量的時候缰犁,先把變量的值拷貝一份放到cpu cache里面淳地,以此提高效率怖糊。
volatile變量保證每次從main memory里面讀取變量最新的值,每次修改以后將變量的值更新到main memory里面颇象。Java5以后的volatile關(guān)鍵字提供了Happens Before Guarantee
- 如果線程A寫了一個volatile變量伍伤,接著線程B讀取了同一個volatile變量。那么線程A更新volatile變量之前的所有變量遣钳,在線程B讀取volatile變量之后扰魂,都是可見的。
- 讀寫volatile變量的操作不能被JVM重新排序蕴茴,但是volatile變量之前和之后的操作可以被重新排序劝评。
public class Exchanger {
private Object object = null;
private volatile hasNewObject = false;
public void put(Object newObject) {
while(hasNewObject) {
//wait - do not overwrite existing new object
}
object = newObject;
hasNewObject = true; //volatile write
}
public Object take(){
while(!hasNewObject){ //volatile read
//wait - don't take old object (or null)
}
Object obj = object;
hasNewObject = false; //volatile write
return obj;
}
}
可以利用這個特性,不用每個變量都加volatile倦淀。上面的例子中付翁,如果只有一個線程執(zhí)行put(), 另外一個線程執(zhí)行take()晃听,那么不需要同步塊也能保證線程安全。
sharedObject.nonVolatile1 = 123;
sharedObject.nonVolatile2 = 456;
sharedObject.nonVolatile3 = 789;
sharedObject.volatile = true; //a volatile variable
int someValue1 = sharedObject.nonVolatile4;
int someValue2 = sharedObject.nonVolatile5;
int someValue3 = sharedObject.nonVolatile6;
volatile保證了sharreObject.volatile的順序不被重排砰识,但是前3個變量和后面3個變量的執(zhí)行順序可能被JVM重排能扒。
僅僅volatile是不夠的
只要一個線程需要先讀一個volatile變量,然后在需要在這個變量值的基礎(chǔ)之上生成一個新的值辫狼,那么volatile變量不再試線程安全的初斑。
當(dāng)有多個線程對volatile變量進(jìn)行寫操作的時候,就有可能造成race condition膨处。
什么時候volatile變量是可靠的
當(dāng)一個線程進(jìn)行volatile變量的讀寫见秤,另外一個變量只進(jìn)行volatile變量的讀的時候,變量是線程安全的額真椿。
volatile變量的性能
由于volatile變量需要從main memory里面讀取數(shù)據(jù)鹃答,多以性能比直接從cpu 緩存中讀取數(shù)據(jù)要高。多以在必要的時候再使用volatile關(guān)鍵字突硝。
ThreadLocal
ThreadLocal類允許生成一個只能被一個線程進(jìn)行讀寫的變量测摔。即使兩個線程執(zhí)行同一段代碼,兩個線程也不能看到對方的ThreadLocal變量
public class ThreadLocalExample {
public static class MyRunnable implements Runnable {
private ThreadLocal<Integer> threadLocal =
new ThreadLocal<Integer>();
@Override
public void run() {
threadLocal.set( (int) (Math.random() * 100D) );
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println(threadLocal.get());
}
}
public static void main(String[] args) {
MyRunnable sharedRunnableInstance = new MyRunnable();
Thread thread1 = new Thread(sharedRunnableInstance);
Thread thread2 = new Thread(sharedRunnableInstance);
thread1.start();
thread2.start();
thread1.join(); //wait for thread 1 to terminate
thread2.join(); //wait for thread 2 to terminate
}
}
在上面的例子中解恰,雖然是同一個ThreadLocal成員變量锋八,但是兩個的值取出來是不相同的。