修飾靜态方法預設擷取目前class的鎖,同步方法沒有釋放的鎖,不影響class其他非同步方法的調用,也不影響不同鎖的同步方法,更不影響使用class的其他屬性.
package thread.base.sync;
import java.util.concurrent.TimeUnit;
/**
*
* @author ZhenWeiLai
*
*/
public class A {
synchronized public static void saySomething() throws InterruptedException{
//前後可以寫非同步代碼
synchronized(A.class){
System.out.println("a");
TimeUnit.SECONDS.sleep(5);
}
//前後可以寫非同步代碼
}
// /**
// * 與上面相等(但是方法内沒有非同步區域):可以這麼了解,把synchronized當修飾符用總不能寫成synchronized(A.claa) public static void saySomething()
// * 這是靜态方法啊,不需要執行個體就可以調用,那麼我擷取什麼鎖啊?隻能擷取本class的鎖吖,你又不能給我傳參
// */
// synchronized public static void saySomething() throws InterruptedException{
// System.out.println("a");
// TimeUnit.SECONDS.sleep(5);
// }
}
修飾非靜态方法,預設擷取調用此方法的執行個體對象鎖 Spring容器管理的bean預設都是單例的(當然可以注解為prototype),是以加上 synchronized 的方法,注解自動裝配擷取的執行個體,調用都會同步了
package thread.base.sync;
import java.util.concurrent.TimeUnit;
/**
*
* @author ZhenWeiLai
*
*/
public class A {
public void saySomething() throws InterruptedException {
//前面可以寫非同步代碼
synchronized (this) {
System.out.println("a");
TimeUnit.SECONDS.sleep(5);
}
//後面可以寫非同步代碼
}
// /**
// * 與上面相等,看了靜态的,再看對象方法更好了解.
// * 必須要有執行個體才能調用吧?太好辦了那麼我就擷取目前執行個體對象的鎖
// */
// synchronized public void saySomething() throws InterruptedException{
// System.out.println("a");
// TimeUnit.SECONDS.sleep(5);
// }
}
synchronized (object)與synchronized (this)一樣,擷取執行個體對象的鎖.因為synchronized (this)隻能擷取目前執行個體鎖,那麼synchronized (object)就是可以擷取其他執行個體鎖的意思
對于synchronized 最簡單粗暴的了解就是,你要哪些線程方法同步,就跟他們擷取一樣的鎖好了,A.class,就擷取A.class, objectA 就擷取 objectA(我說的不是對象名相同,而是真真切切在java堆中的同一個對象),
據說ConcurrentHashMap 就是用16個對象鎖實作的
我也模拟一下
package thread.base.sync.hashmap;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
/**
*
* @author ZhenWeiLai 模拟16個并發的線程安全put
* @param <K>
* @param <V>
*/
public class SyncHashMap<K, V> extends HashMap<K, V> {
/**
*
*/
private static final long serialVersionUID = 4071859310703431745L;
// 初始化16個對象鎖
Object[] lock = new Object[16];
public SyncHashMap() {
for (int i = 0; i < 16; i++) {
lock[i] = new Object();
}
}
/**
* 線程安全方法
*
* @param key
* @param value
* @return
* @throws InterruptedException
*/
public V safePut(K key, V value) throws InterruptedException {
/**
* 不一樣的key有可能得到一樣的餘數,據說hashcode算法問題.我這裡是取巧,不深究
* 萬一兩個不同的key得到一樣的餘數,那麼慢的一個就要等待5秒咯
* 随機擷取16個對象鎖中的一個
*/
synchronized (lock[key.hashCode() % 16]) {
System.out.println("aaaa");
TimeUnit.SECONDS.sleep(5);
// 同步調用原生put方法
return super.put(key, value);
}
}
}
測試方法,兩個key相同,其中擷取鎖慢的那個線程會等5秒再輸出控制台
package thread.base.sync.hashmap;
/**
*
* @author ZhenWeiLai
*
*/
public class TestClass {
public static void main(String[] args) {
SyncHashMap<Integer,Integer> shm = new SyncHashMap<>();
new Thread(()->{
try {
//兩個線程操作同一個key,
shm.safePut(1, 1);
} catch (Exception e) {
e.printStackTrace();
}
}).start();
new Thread(()->{
try {
//兩個線程操作同一個key
shm.safePut(1,1);
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}