Synchronized關鍵字
靜態的synchronized方法以class對象作爲鎖,又稱Intrinsic Lock或Monitor鎖;
Synchronized (lock) {
//訪問或修改由鎖保護的共享狀態
}
同步代碼塊:對象的Monitor鎖底層有monitorenter和monitorexit指令,monitorenter指令將計數器值加1,monitorexit將計數器值減1;當計數器值爲0時monitor鎖才能由線程獲取,且計數器值只能由持有Monitor鎖的線程修改。並且,無論方法正常結束還是異常終止,monitorexit指令都會執行,以確保線程釋放monitor鎖。
同步方法:使用的是運行時常量池中方法的 ACC_SYNCHRONIZED 標誌,原理與以上類似。
示例代碼:
public class CountSync implements Runnable {
private int i = 0;
public synchronized void increase(){
i++;
}
@Override
public void run() {
for (int j = 0; j < 1000000; j++) {
increase();
}
}
public synchronized int getI() {
return i;
}
public static void main(String[] args) throws InterruptedException {
CountSync sync = new CountSync();
Thread t1 = new Thread(sync);
Thread t2 = new Thread(sync);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(sync.getI());
}
}
注:synchronized關鍵字用於靜態方法時使用的是類對象的鎖
Volatile變量
變量的更新操作會被及時通知到其他線程,只確保可見性不保證原子性,但volatile 關鍵宇能夠保障對 long/double 型變量的寫操作具有原子性。
加入volatile會禁止指令重排,強制對緩存的修改操作立即寫入主內存;其他線程的讀操作直接從主內存讀取最新值。
使用場景:1. 對變量的寫操作不依賴當前值;
2. 該變量沒有包含在具有其他變量的不變式中。
示例代碼:
public class VolaThread implements Runnable {
public static volatile int n = 0;
@Override
public void run() {
try {
for (int i = 0; i < 100; i++) {
increment();
Thread.sleep(10);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void increment() {
n++;
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new VolaThread());
Thread t2 = new Thread(new VolaThread());
Thread t3 = new Thread(new VolaThread());
Thread t4 = new Thread(new VolaThread());
t1.start();
t2.start();
t3.start();
t4.start();
t1.join();
t2.join();
t3.join();
t4.join();
System.out.println("n is :" + n);
}
}
ThreadLocal類
ThreadLocal用於保存某個線程共享變量:對於同一個static ThreadLocal,不同線程只能從中get,set,remove自己的變量,而不會影響其他線程的變量。使線程中的某個值與保存值的對象關聯起來,ThreadLocal對象通常用於防止對可變的單實例變量或全局變量進行共享
ThreadLocal使用ThreadLocalMap內部靜態類,實際調用ThreadLocalMap的set和get方法設置和獲取值,key爲當前ThreadLocal對象,value爲變量副本。
示例代碼:
import java.text.SimpleDateFormat;
import java.util.Date;
public class MyThreadLocal {
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("HH:mm:ss SSS");
private static final ThreadLocal<Object> threadLocal = new ThreadLocal<Object>() {
@Override
protected Object initialValue() {
System.out.println("[" + DATE_FORMAT.format(new Date()) +"] " + Thread.currentThread().getName() + " invokes method initialValue, return Default value");
return null;
}
};
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new MyIntegerTask("IntegerTask1"));
Thread t2 = new Thread(new MyStringTask("StringTask1"));
Thread t3 = new Thread(new MyIntegerTask("IntegerTask2"));
Thread t4 = new Thread(new MyStringTask("StringTask2"));
t1.start();
t2.start();
t3.start();
t4.start();
//等待t1,t2,t3,t4死掉
t1.join();
t2.join();
t3.join();
t4.join();
System.out.println("******************All done*****************");
System.out.println("線程" + Thread.currentThread().getName() + " get variable, result " + MyThreadLocal.threadLocal.get());
MyThreadLocal.threadLocal.remove();
}
public static class MyIntegerTask implements Runnable {
private String name;
MyIntegerTask(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
if (null == MyThreadLocal.threadLocal.get()) {
MyThreadLocal.threadLocal.set(0);
System.out.println("[" + DATE_FORMAT.format(new Date()) + "]" + " 線程" + name + ": 0");
} else {
int num = (int) MyThreadLocal.threadLocal.get(); //沒有拋ClassCastException
MyThreadLocal.threadLocal.set(num + 1);
System.out.println("[" + DATE_FORMAT.format(new Date()) + "]" + " 線程" + name + ":" + MyThreadLocal.threadLocal.get());
if (i == 3) {
MyThreadLocal.threadLocal.remove();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static class MyStringTask implements Runnable {
private String name;
MyStringTask(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
if (null == MyThreadLocal.threadLocal.get()) {
MyThreadLocal.threadLocal.set("a");
System.out.println("[" + DATE_FORMAT.format(new Date()) + "]" + " 線程" + name + ":a");
} else {
String string = (String) MyThreadLocal.threadLocal.get();
MyThreadLocal.threadLocal.set(string + "a");
System.out.println("[" + DATE_FORMAT.format(new Date()) + "]" + " 線程" + name + ":" + MyThreadLocal.threadLocal.get());
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
注:1. 使用完線程共享變量後,顯示調用ThreadLocalMap.remove方法清除線程共享變量;
2. JDK建議ThreadLocal定義爲private static,這樣ThreadLocal弱引用的問題則不存在了
Final域
用於構造不可變對象,能確保初始化過程的安全性
示例代碼:略
參考:《Java併發編程實戰》、深入理解Java併發之synchronized實現原理、正確使用 Volatile 變量、ThreadLocal用法詳解和原理