修飾靜態方法默認獲取當前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(); } }