"8锁问题"是一个经典的多线程问题,主要测试对Java中synchronized
关键字的理解,以及线程同步和锁机制的掌握。这个问题通常涉及到8种不同的情况,通过不同的锁和调用顺序,来观察哪个线程会先执行。以下是对8种情况的详细解释和代码示例。
题目描述
假设有两个方法sendEmail
和sendSMS
,以及一个普通方法hello
,我们会在不同的条件下调用它们,观察它们的执行顺序。
情况1:两个方法都加锁,两个线程分别调用这两个方法
结果:
sendEmail
先执行,然后sendSMS
执行。因为两个方法都加了synchronized
,且是同一个对象锁。
class Phone {
public synchronized void sendEmail() {
System.out.println("sendEmail");
}
public synchronized void sendSMS() {
System.out.println("sendSMS");
}
public void hello() {
System.out.println("hello");
}
}
public class EightLocks {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
phone.sendEmail();
}, "Thread1").start();
new Thread(() -> {
phone.sendSMS();
}, "Thread2").start();
}
}
情况2:一个方法加锁,另一个方法不加锁,两个线程分别调用这两个方法
结果:
sendSMS
和sendEmail
的执行顺序不确定。因为sendSMS
没有锁,不受sendEmail
锁的影响。
class Phone {
public synchronized void sendEmail() {
System.out.println("sendEmail");
}
public void sendSMS() {
System.out.println("sendSMS");
}
}
public class EightLocks {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
phone.sendEmail();
}, "Thread1").start();
new Thread(() -> {
phone.sendSMS();
}, "Thread2").start();
}
}
情况3:两个方法都加锁,且是静态锁,两个线程分别调用这两个方法
结果:
sendEmail
先执行,然后sendSMS
执行。静态同步方法使用的是类锁(Phone.class),两个线程互斥。
class Phone {
public static synchronized void sendEmail() {
System.out.println("sendEmail");
}
public static synchronized void sendSMS() {
System.out.println("sendSMS");
}
}
public class EightLocks {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
Phone.sendEmail();
}, "Thread1").start();
new Thread(() -> {
Phone.sendSMS();
}, "Thread2").start();
}
}
情况4:一个静态同步方法,一个非静态同步方法,两个线程分别调用这两个方法
结果:
sendEmail
和sendSMS
的执行顺序不确定。一个是类锁,一个是对象锁,不互斥。
class Phone {
public static synchronized void sendEmail() {
System.out.println("sendEmail");
}
public static synchronized void sendSMS() {
System.out.println("sendSMS");
}
}
public class EightLocks {
public static void main(String[] args) {
Phone phone1 = new Phone();
Phone phone2 = new Phone();
new Thread(() -> {
phone1.sendEmail();
}, "Thread1").start();
new Thread(() -> {
phone2.sendSMS();
}, "Thread2").start();
}
}
情况5:两个静态同步方法,两个对象,两个线程分别调用这两个方法
结果:
sendEmail
先执行,然后sendSMS
执行。静态同步方法使用的是类锁,不管多少个对象,锁都是同一个。
class Phone {
public static synchronized void sendEmail() {
System.out.println("sendEmail");
}
public static synchronized void sendSMS() {
System.out.println("sendSMS");
}
}
public class EightLocks {
public static void main(String[] args) {
Phone phone1 = new Phone();
Phone phone2 = new Phone();
new Thread(() -> {
phone1.sendEmail();
}, "Thread1").start();
new Thread(() -> {
phone2.sendSMS();
}, "Thread2").start();
}
}
情况6:两个非静态同步方法,两个对象,两个线程分别调用这两个方法
结果:
sendEmail
和sendSMS
的执行顺序不确定。两个对象有各自的锁,不互斥。
class Phone {
public synchronized void sendEmail() {
System.out.println("sendEmail");
}
public synchronized void sendSMS() {
System.out.println("sendSMS");
}
}
public class EightLocks {
public static void main(String[] args) {
Phone phone1 = new Phone();
Phone phone2 = new Phone();
new Thread(() -> {
phone1.sendEmail();
}, "Thread1").start();
new Thread(() -> {
phone2.sendSMS();
}, "Thread2").start();
}
}
情况7:一个非静态同步方法,一个非同步方法,两个对象,两个线程分别调用这两个方法
结果:
sendSMS
和sendEmail
的执行顺序不确定。sendSMS
没有锁,不受sendEmail
锁的影响。
class Phone {
public synchronized void sendEmail() {
System.out.println("sendEmail");
}
public void sendSMS() {
System.out.println("sendSMS");
}
}
public class EightLocks {
public static void main(String[] args) {
Phone phone1 = new Phone();
Phone phone2 = new Phone();
new Thread(() -> {
phone1.sendEmail();
}, "Thread1").start();
new Thread(() -> {
phone2.sendSMS();
}, "Thread2").start();
}
}
情况8:一个静态同步方法,一个非同步方法,两个对象,两个线程分别调用这两个方法
结果:
sendSMS
和sendEmail
的执行顺序不确定。一个是类锁,一个是对象锁,不互斥。
class Phone {
public static synchronized void sendEmail() {
System.out.println("sendEmail");
}
public void sendSMS() {
System.out.println("sendSMS");
}
}
public class EightLocks {
public static void main(String[] args) {
Phone phone1 = new Phone();
Phone phone2 = new Phone();
new Thread(() -> {
phone1.sendEmail();
}, "Thread1").start();
new Thread(() -> {
phone2.sendSMS();
}, "Thread2").start();
}
}
总结
synchronized锁的是方法的调用者(拥有者)
通过"8锁问题"可以深入理解Java中的锁机制和
synchronized
关键字的使用。不同的锁(类锁和对象锁),不同的调用顺序和条件(静态方法和非静态方法),会导致不同的执行结果。通过这些案例,可以更好地理解并发编程中的锁机制。