8锁现象彻底理解锁

Test1

package lock8;  
  
import java.util.concurrent.TimeUnit;  
  
public class Test1 {  
    public static void main(String[] args) throws InterruptedException {  
        Data data = new Data();  
        new Thread(data::SendSMS).start();  
        TimeUnit.SECONDS.sleep(1);  
        new Thread(data::SendEmail).start();  
    }  
}  
  
  
class Data {  
    public synchronized void SendSMS() {  
        try {  
            TimeUnit.SECONDS.sleep(4);  
        } catch (InterruptedException e) {  
            throw new RuntimeException(e);  
        }  
        System.out.println("发短信");  
    }  
  
    public synchronized void SendEmail() {  
        System.out.println("发邮件");  
    }  
}

会先打印哪个?

image.png

先打印发短信,是因为我们中间停了一秒吗,那我们在方法体里停四秒试试:

public synchronized void SendSMS() {  
    try {  
        TimeUnit.SECONDS.sleep(4);  
    } catch (InterruptedException e) {  
        throw new RuntimeException(e);  
    }  
    System.out.println("发短信");  
}

image.png 结果依然是先发短信。

synchronized锁的是对象的调用者

而我们两个方法都是一个对象对象调用的,所以只要一个拿到了锁,另一个只能排队。

Test2

我们加一个普通方法试试呢?

package lock8;  
  
import java.util.concurrent.TimeUnit;  
  
public class Test2 {  
    public static void main(String[] args) throws InterruptedException {  
        Data2 data = new Data2();  
        new Thread(data::SendSMS).start();  
        TimeUnit.SECONDS.sleep(1);  
        new Thread(data::Hello).start();  
    }  
}  
  
class Data2 {  
    public synchronized void SendSMS() {  
        try {  
            TimeUnit.SECONDS.sleep(4);  
        } catch (InterruptedException e) {  
            throw new RuntimeException(e);  
        }  
        System.out.println("发短信");  
    }  
    public synchronized void SendEmail() {  
        System.out.println("发邮件");  
    }  
    public void Hello() {  
        System.out.println("Hello");  
    }  
}

image.png

普通方法不受锁的影响!

我们再创建一个对象呢?

package lock8;  
  
import java.util.concurrent.TimeUnit;  
  
public class Test2 {  
    public static void main(String[] args) throws InterruptedException {  
        Data2 data = new Data2();  
        Data2 data2 = new Data2();  
        new Thread(data::SendSMS).start();  
        TimeUnit.SECONDS.sleep(1);  
        new Thread(data2::SendEmail).start();  
    }  
}  
  
class Data2 {  
    public synchronized void SendSMS() {  
        try {  
            TimeUnit.SECONDS.sleep(4);  
        } catch (InterruptedException e) {  
            throw new RuntimeException(e);  
        }  
        System.out.println("发短信");  
    }  
    public synchronized void SendEmail() {  
        System.out.println("发邮件");  
    }  
}

image.png

两个对象两把锁,在第一个线程拿到锁sleep之后,第二个线程不需要等待。

Test3

那么其余不变,我们把方法改为static之后呢?

package lock8;  
  
import java.util.concurrent.TimeUnit;  
  
public class Test3 {  
    public static void main(String[] args) throws InterruptedException {  
        Data3 data = new Data3();  
        Data3 data2 = new Data3();  
        new Thread(() -> data.SendSMS()).start();  
        TimeUnit.SECONDS.sleep(1);  
        new Thread(() -> data2.SendEmail()).start();  
    }  
}  
  
class Data3 {  
    public static synchronized void SendSMS() {  
        try {  
            TimeUnit.SECONDS.sleep(4);  
        } catch (InterruptedException e) {  
            throw new RuntimeException(e);  
        }  
        System.out.println("发短信");  
    }  
    public static synchronized void SendEmail() {  
        System.out.println("发邮件");  
    }  
}

image.png

static方法在类一加载就有了,所以不论创建多少个实例,锁只有一把!就是.Class

Test4

一个静态同步方法,一个非静态同步方法

package lock8;  
  
import java.util.concurrent.TimeUnit;  
  
public class Test4 {  
    public static void main(String[] args) throws InterruptedException {  
        Data4 data = new Data4();  
        Data4 data2 = new Data4();  
        new Thread(() -> data.SendSMS()).start();  
        TimeUnit.SECONDS.sleep(1);  
        new Thread(data2::SendEmail).start();  
    }  
}  
  
class Data4 {  
    public static synchronized void SendSMS() {  
        try {  
            TimeUnit.SECONDS.sleep(4);  
        } catch (InterruptedException e) {  
            throw new RuntimeException(e);  
        }  
        System.out.println("发短信");  
    }  
    public synchronized void SendEmail() {  
        System.out.println("发邮件");  
    }  
}

image.png

因为这是两把锁!互不干扰。

总结

锁的对象无非就两种:

一个是具体的实例对象,可以有多把;

一个是static,类模板对象,只有一把。