一、场景一
两个同步方法,一部手机,请问先打印邮件还是短信?
class Phone{
public synchronized void sendEmail() throws Exception{
System.out.println("*******sendEmail");
}
public synchronized void sendMs() throws Exception{
System.out.println("*******sendMs");
}
}
public class TestDemo {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(()->{
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
},"A").start();
Thread.sleep(100);
new Thread(()->{
try {
phone.sendMs();
} catch (Exception e) {
e.printStackTrace();
}
},"B").start();
}
}
结果:
sendEmail
sendMs
分析:
因为
Thread.sleep(100);
这一行代码保证了一定会让 A 线程先启动,所以线程 A 先获取到锁对象(this对象),而线程 B 无法获取到锁对象,所以线程 A 先执行,然后才是线程 B 执行。
二、场景二
发送邮件的方法睡眠4秒钟,请问先打印邮件还是短信?
class Phone{
public synchronized void sendEmail() throws Exception{
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("*******sendEmail");
}
public synchronized void sendMs() throws Exception{
System.out.println("*******sendMs");
}
}
public class TestDemo {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(()->{
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
},"A").start();
Thread.sleep(100);
new Thread(()->{
try {
phone.sendMs();
} catch (Exception e) {
e.printStackTrace();
}
},"B").start();
}
}
结果:
sendEmail
sendMs
分析:
因为
Thread.sleep(100);
这一行代码保证了一定会让 A 线程先启动,所以线程 A 先获取到锁对象(this对象),而线程 B 无法获取到锁对象,所以线程 A 先执行,A 执行完毕后才是线程 B 执行。
三、场景三
新增一个普通方法 hello(),请问先打印邮件还是 hello?
class Phone{
public synchronized void sendEmail() throws Exception{
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("*******sendEmail");
}
public synchronized void sendMs() throws Exception{
System.out.println("*******sendMs");
}
public void sayHello() throws Exception{
System.out.println("*****sayHello");
}
}
public class TestDemo {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(()->{
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
},"A").start();
Thread.sleep(100);
new Thread(()->{
try {
phone.sayHello();
} catch (Exception e) {
e.printStackTrace();
}
},"B").start();
}
}
结果:
sayHello
endEmail
分析:
因为
Thread.sleep(100);
这一行代码保证了一定会让 A 线程先启动,所以线程 A 先获取到锁对象(this对象),而线程 B 调用的 sayHello 方法不需要 获取锁,所以在线程 A 睡眠的时候,线程 B 可以直接运行。
四、场景四
两部手机,请问先打印邮件还是短信?
class Phone{
public synchronized void sendEmail() throws Exception{
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("*******sendEmail");
}
public synchronized void sendMs() throws Exception{
System.out.println("*******sendMs");
}
}
public class TestDemo {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(()->{
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
},"A").start();
Thread.sleep(100);
new Thread(()->{
try {
phone2.sendMs();
} catch (Exception e) {
e.printStackTrace();
}
},"B").start();
}
}
结果:
sendMs
sendEmail
分析:
因为有两个手机实例,所以存在两个 this 对象,因此两个线程获取的锁对象不是同一个,所以线程 A、B 不存在锁的争夺情况,从而在线程 A 睡眠期间,线程 B 可以运行。
五、场景五
两个静态同步方法,同一部手机,请问先打印邮件还是短信?
class Phone{
public static synchronized void sendEmail() throws Exception{
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("*******sendEmail");
}
public static synchronized void sendMs() throws Exception{
System.out.println("*******sendMs");
}
}
public class TestDemo {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(()->{
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
},"A").start();
Thread.sleep(100);
new Thread(()->{
try {
phone.sendMs();
} catch (Exception e) {
e.printStackTrace();
}
},"B").start();
}
}
结果:
sendEmail
sendMs
分析:
因为
Thread.sleep(100);
这一行代码保证了一定会让 A 线程先启动,所以线程 A 先获取到锁对象(class对象),而线程 B 无法获取到锁对象,所以线程 A 先执行,A 执行完毕后才是线程 B 执行。
六、场景六
两个静态同步方法,两部手机,请问先打印邮件还是短信?
class Phone{
public static synchronized void sendEmail() throws Exception{
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("*******sendEmail");
}
public static synchronized void sendMs() throws Exception{
System.out.println("*******sendMs");
}
}
public class TestDemo {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(()->{
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
},"A").start();
Thread.sleep(100);
new Thread(()->{
try {
phone2.sendMs();
} catch (Exception e) {
e.printStackTrace();
}
},"B").start();
}
}
结果:
sendEmail
sendMs
分析:
因为
Thread.sleep(100);
这一行代码保证了一定会让 A 线程先启动,所以线程 A 先获取到锁对象(class对象),而线程 B 无法获取到锁对象,所以线程 A 先执行,A 执行完毕后才是线程 B 执行。补充:两部手机只能说明存在两个 this 对象,但是 class 对象还是只有一个。
七、场景七
一个普通同步方法,一个静态同步方法,一部手机,请问先打印邮件还是短信?
class Phone{
public static synchronized void sendEmail() throws Exception{
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("*******sendEmail");
}
public synchronized void sendMs() throws Exception{
System.out.println("*******sendMs");
}
}
public class TestDemo {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(()->{
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
},"A").start();
Thread.sleep(100);
new Thread(()->{
try {
phone.sendMs();
} catch (Exception e) {
e.printStackTrace();
}
},"B").start();
}
}
结果:
sendMs
sendEmail
分析:
尽管
Thread.sleep(100);
这一行代码保证了一定会让 A 线程先启动,所以线程 A 先获取到锁对象(class对象);但是因为线程 B 是调用的同步方法,锁对象为 this 对象,所以两个线程的锁对象不是同一个,不存在争用的情况,所以在线程 A 睡眠的时候,线程 B 可以继续执行。
八、场景八
一个普通同步方法,一个静态同步方法,两部手机,请问先打印邮件还是短信?
class Phone{
public static synchronized void sendEmail() throws Exception{
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("*******sendEmail");
}
public synchronized void sendMs() throws Exception{
System.out.println("*******sendMs");
}
}
public class TestDemo {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(()->{
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
},"A").start();
Thread.sleep(100);
new Thread(()->{
try {
phone2.sendMs();
} catch (Exception e) {
e.printStackTrace();
}
},"B").start();
}
}
结果:
sendMs
sendEmail
分析:
尽管
Thread.sleep(100);
这一行代码保证了一定会让 A 线程先启动,所以线程 A 先获取到锁对象(class对象);但是因为线程 B 是调用的同步方法,锁对象为 this 对象,所以两个线程的锁对象不是同一个,不存在争用的情况,所以在线程 A 睡眠的时候,线程 B 可以继续执行。
补充:两部手机只能说明存在两个 this 对象,但因为两个线程中一个用的 class,一个用的 this,所以还是两个不同的锁对象。
九、总结
其实这些都是关于锁对象的问题,只需要正确的分析出锁对象,即可解决问题。
若有错误,欢迎指正!