多线程8锁问题详解
1.案例准备
首先我们准备一个资源类,在资源类定义两个同步方法,来测试在不同的访问方式下,
会产生什么样的结果。代码如下:
class MyPhone {
public synchronized void sendEmail() {
// 发送邮件
System.out.println("----sendEmail");
}
// 发送短信
public synchronized void sendMS() {
System.out.println("----sendMS");
}
}
2.第一锁,标准访问是先打印邮件还是短信
我们编写如下代码进行测试
public class Lock8Demo {
public static void main(String[] args) throws InterruptedException {
MyPhone myPhone = new MyPhone();
new Thread(() -> {
myPhone.sendMS();
}, "线程二").start();
new Thread(() -> {
myPhone.sendEmail();
}, "线程一").start();
}
}
我们发现在标准访问下,线程一和线程二都有可能先执行
第二锁,邮件方法暂停2秒,是先打印邮件还是短信
资源类修改如下:
class MyPhone {
public synchronized void sendEmail() {
// 发送邮件
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("----sendEmail");
}
// 发送短信
public synchronized void sendMS() {
System.out.println("----sendMS");
}
}
测试代码如下:
public class Lock8Demo {
public static void main(String[] args) throws InterruptedException {
MyPhone myPhone = new MyPhone();
new Thread(() -> {
myPhone.sendMS();
}, "线程二").start();
Thread.sleep(200);
new Thread(() -> {
myPhone.sendEmail();
}, "线程一").start();
}
}
为了方便测试,我们在线程一一和线程二之间休眠2秒,保证线程一先调用,此后同样。
在这样的访问方式下,会先发送邮件,因为在同一个对象里如果有多个synchronized方法或synchronized代码块,某一个时刻内,只要有一个线程去调用了其中一个synchronized方法,其它线程只能等待,换句话说,某一个时刻内,只能有一个线程去访问这些synchronized方法,在本例中即使是线程一休眠了2秒,因为它先调用资源类,所以线程二会等待线程一执行完才会执行。
第三锁,新增一个普通方法hello,会先打印邮件还是hello
class MyPhone {
public synchronized void sendEmail() {
// 发送邮件
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("----sendEmail");
}
// 发送短信
public synchronized void sendMS() {
System.out.println("----sendMS");
}
// 普通方法hello
public void hello() {
System.out.println("----hello");
}
}
测试代码:
public class Lock8Demo {
public static void main(String[] args) throws InterruptedException {
MyPhone myPhone = new MyPhone();
new Thread(() -> {
myPhone.sendEmail();
}, "线程一").start();
new Thread(() -> {
myPhone.hello();
}, "线程二").start();
}
}
在本例中,会先执行hello,没有加synchronized的普通方法,不会加锁。
5.第四锁,两步手机会先打印邮件还是短信
public class Lock8Demo {
public static void main(String[] args) throws InterruptedException {
MyPhone myPhone = new MyPhone();
MyPhone myPhone2 = new MyPhone();
new Thread(() -> {
myPhone.sendEmail();
}, "线程一").start();
new Thread(() -> {
myPhone2.sendMS();
}, "线程二").start();
}
}
在本例中,会先打印短信。因为两个线程调用的是你两个不同实例的方法,在线程一的方法暂停两秒钟的情况下,先打印短信
6.第五锁,两个静态同步方法,一部手机,请问是先打印邮件还是短信
修改资源类,改变为静态同步方法
class MyPhone {
public static synchronized void sendEmail() {
// 发送邮件
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("----sendEmail");
}
// 发送短信
public static synchronized void sendMS() {
System.out.println("----sendMS");
}
// 普通方法hello
public void hello() {
System.out.println("----hello");
}
}
测试代码:
public class Lock8Demo {
public static void main(String[] args) throws InterruptedException {
MyPhone myPhone = new MyPhone();
MyPhone myPhone2 = new MyPhone();
new Thread(() -> {
myPhone.sendEmail();
}, "线程一").start();
new Thread(() -> {
myPhone.sendMS();
}, "线程二").start();
}
}
在本例中,会先打印邮件。具体原因,我们先看第六锁。
7.第六锁,两个静态同步方法,两步手机,请问是先打印邮件还是短信
public class Lock8Demo {
public static void main(String[] args) throws InterruptedException {
MyPhone myPhone = new MyPhone();
MyPhone myPhone2 = new MyPhone();
new Thread(() -> {
myPhone.sendEmail();
}, "线程一").start();
new Thread(() -> {
myPhone2.sendMS();
}, "线程二").start();
}
}
在本例中同样会先打印邮件。因为static修饰的方法在JVM中只会有一份,不管实例多少个对象,调用的都是一个方法,一次两个线程即使是两个对象,但其实相当于是一个类,会是允许一个线程进入,所以会先打印邮件,因此第五锁和第六锁同样的效果。
8.第七锁,一个普通同步方法,一个静态同步方法,一部手机,请问是先打印邮件还是短信
修改资源类
class MyPhone {
public static synchronized void sendEmail() {
// 发送邮件
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("----sendEmail");
}
// 发送短信
public synchronized void sendMS() {
System.out.println("----sendMS");
}
// 普通方法hello
public void hello() {
System.out.println("----hello");
}
}
测试代码:
public class Lock8Demo {
public static void main(String[] args) throws InterruptedException {
MyPhone myPhone = new MyPhone();
MyPhone myPhone2 = new MyPhone();
new Thread(() -> {
myPhone.sendEmail();
}, "线程一").start();
new Thread(() -> {
myPhone.sendMS();
}, "线程二").start();
}
}
在当前例子中,会先打印短信。因为线程一的静态方法锁的是当前类,而线程二锁的是当前实例对象,就相当于是两个不同的类,因此线程二会先执行。
9.一个普通同步方法,一个静态同步方法,两步手机,请问是先打印邮件还是短信
测试代码:
public class Lock8Demo {
public static void main(String[] args) throws InterruptedException {
MyPhone myPhone = new MyPhone();
MyPhone myPhone2 = new MyPhone();
new Thread(() -> {
myPhone.sendEmail();
}, "线程一").start();
new Thread(() -> {
myPhone2.sendMS();
}, "线程二").start();
}
}
在本例中,先打印短信即线程二会先执行,原理同第七锁。