8鎖現象
八鎖->就是關於鎖的八個問題
鎖是什麼,如何判斷鎖的是誰
物件、class模板
深刻理解鎖
鎖的東西無外乎就兩樣:1、同步方法的呼叫者,2、Class模板。
同一個鎖中,只有當前執行緒資源釋放後才會被下一個執行緒所接手。
同步方法的呼叫者是兩個不同的例項時,互不相關。
靜態同步方法(static)鎖的是整個Class模板,和同步方法的呼叫者也不是同一個鎖;切Class模板在Java程式中唯一。
程式碼示例
1、淺淺理解鎖的作用
同一把鎖中根據執行先後釋放資源,保證一個資源的使用順序
package org.example.phone;
import java.util.concurrent.TimeUnit;
public class Test1 {
public static void main(String[] args) {
// 標準情況下,列印順序為 1、發簡訊,2、打電話
// 給sendMsg內部延遲四秒執行,執行順序依舊是 1、發簡訊,2、打電話
// 可知,並非是我們所想的,A執行緒在前面就先執行,而是鎖的機制導致了這種情況
// phone1只建立了一個物件,所以這個物件的鎖只有一把,誰先拿到就是誰先執行
// 鎖的物件是該方法的呼叫者,即phone1
Phone1 phone1 = new Phone1();
new Thread(()->{
phone1.sendMsg();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(()->{
phone1.call();
},"B").start();
}
}
class Phone1{
// synchronized鎖的物件是方法的呼叫者,Phone1只new了一個物件,所以鎖的是new出來的整個物件
public synchronized void sendMsg(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("發簡訊");
}
public synchronized void call(){
System.out.println("打電話");
}
}
2、區分鎖的物件
不同的例項使用的鎖並非同一把,所以也無法同時鎖定某個固定的資源、無法對同一資源進行有順序的操作
package org.example.phone;
import java.util.concurrent.TimeUnit;
public class Test3 {
public static void main(String[] args) {
// 標準情況下,列印順序為 1、發簡訊,2、打電話
// 給sendMsg內部延遲四秒執行,執行順序依舊是 1、發簡訊,2、打電話
// 可知,並非是我們所想的,A執行緒在前面就先執行,而是鎖的機制導致了這種情況
// phone1只建立了一個物件,所以這個物件的鎖只有一把,誰先拿到就是誰先執行
// 鎖的物件是該方法的呼叫者,即phone1
// 呼叫兩個不同物件的方法,鎖的是兩個不同的物件,此時先出現打電話,說明不同物件之間的鎖互不影響
Phone3 phone3_1 = new Phone3();
Phone3 phone3_2 = new Phone3();
new Thread(()->{
phone3_1.sendMsg();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(()->{
phone3_2.call();
},"B").start();
}
}
class Phone3{
// synchronized鎖的物件是方法的呼叫者,Phone1只new了一個物件,所以鎖的是new出來的整個物件
public synchronized void sendMsg(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("發簡訊");
}
public synchronized void call(){
System.out.println("打電話");
}
// 當在資源類中新增了一個普通方法後,先輸出hello
// 沒有鎖,不是同步方法,不受鎖的影響
public void hello(){
System.out.println("Hello");
}
}
3、瞭解鎖的參與者
只有同步方法參與鎖,普通方法依舊按照java執行順序執行
package org.example.phone;
import java.util.concurrent.TimeUnit;
public class Test2 {
public static void main(String[] args) {
// 標準情況下,列印順序為 1、發簡訊,2、打電話
// 給sendMsg內部延遲四秒執行,執行順序依舊是 1、發簡訊,2、打電話
// 可知,並非是我們所想的,A執行緒在前面就先執行,而是鎖的機制導致了這種情況
// phone1只建立了一個物件,所以這個物件的鎖只有一把,誰先拿到就是誰先執行
// 鎖的物件是該方法的呼叫者,即phone1
Phone2 phone2 = new Phone2();
new Thread(()->{
phone2.sendMsg();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(()->{
phone2.hello();
},"B").start();
}
}
class Phone2{
// synchronized鎖的物件是方法的呼叫者,Phone1只new了一個物件,所以鎖的是new出來的整個物件
public synchronized void sendMsg(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("發簡訊");
}
public synchronized void call(){
System.out.println("打電話");
}
// 當在資源類中新增了一個普通方法後,先輸出hello
// 沒有鎖,不是同步方法,不受鎖的影響
public void hello(){
System.out.println("Hello");
}
}
4、明白鎖能鎖誰
鎖只能鎖兩個東西,一個是同步方法的呼叫者,一個是整個Class模板(全域性唯一),一旦使用static建立靜態同步方法,那麼該方法的鎖鎖的就是全域性唯一的Class模板,並且在反射時就已經被建立了
package org.example.phone;
import java.util.concurrent.TimeUnit;
public class Test4 {
public static void main(String[] args) {
// 兩個物件的Class類別範本只有一個;static,鎖的是Class
Phone4 phone4_1 = new Phone4();
Phone4 phone4_2 = new Phone4();
new Thread(()->{
phone4_1.sendMsg();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(()->{
phone4_2.call();
},"B").start();
}
}
class Phone4{
// synchronized鎖的物件是方法的呼叫者
// 注:增加了static靜態方法 此時呼叫該方法的就變成了Phone4的反射物件,全域性唯一
// 此時鎖的就是Class模板了,即不管你有幾個呼叫者,都在同一個鎖
// static方法類一載入就有了!鎖的是Class
public static synchronized void sendMsg(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("發簡訊");
}
public static synchronized void call(){
System.out.println("打電話");
}
}
5、深入理解鎖的是誰
靜態同步方法和普通同步方法在一起使用時,鎖的並非同一物件,所以列印順序也時按java的執行順序來,並不存在鎖定資源的情況
package org.example.phone;
import java.util.concurrent.TimeUnit;
/*
* 1、一個靜態同步方法,一個普通同步方法,先列印發簡訊還是打電話
* 兩個方法一個鎖的是Class模板,一個鎖的是呼叫者,鎖的不是同一物件,所以延遲四秒的靜態同步方法後列印,延遲一秒的普通同步方法先列印
*
* */
public class Test5 {
public static void main(String[] args) {
Phone5 phone5_1 = new Phone5();
// Phone5 phone5_2 = new Phone5();
new Thread(()->{
phone5_1.sendMsg();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(()->{
phone5_1.call();
},"B").start();
}
}
class Phone5{
// 鎖的是Class模板
public static synchronized void sendMsg(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("發簡訊");
}
// 鎖的是呼叫者
public synchronized void call(){
System.out.println("打電話");
}
}