线程八锁:
1、标准访问,请问先执行发邮件还是先发短信?
2、如果让发邮件sleep()三秒钟是先发邮件还是先发短信?
3、新增一个普通方法hello(),先发邮件还是先执行hello()?
4、两部手机(两个资源类对象),先发邮件还是先发送短信?
5、两个静态方法,同一部手机,先发邮件还是先发短信?
6、两个静态方法,两部手机,先发邮件还是先发短信?
7、一个普通方法,一个静态方法,一部手机,先发邮件还是先发短信?
8、一个普通方法,一个静态方法,两部手机,先发邮件还是先发短信?
这里我认为需要加一些前提条件:
在没有sleep()等情况下,线程A在线程B上面,默认线程A先抢到执行权
一、问题一和问题二
1、标准访问,请问先执行发邮件还是先发短信?
2、如果让发邮件sleep()三秒钟是先发邮件还是先发短信?
当一个资源类类对象中有多个synchronized方法时,在某一时刻内,只要有一个线程去调用其中的一个synchronized方法,其他线程必须等待。换句话说,在某一时刻内,只能有唯一 一个线程去访问这些synchronized方法,锁住的是当前对象this,被锁定后,其他线程不能调用到当前对象的任何synchronized方法。(前提:多个线程共用同一个对象,共同操作同一个资源类)
如果大家细心并多次的话可能会发现一个问题,多线程不是谁先抢到谁执行吗?为什么在这里永远都是先执行线程A再执行线程B?
我是这样理解的,由于线程A在线程B的前面,线程A在没有意外的情况下先start()准备就绪,极大可能先执行。如果我们在线程A创建但没有准备就绪前先让其sleep()两秒钟,这时我们就会发现是线程B先抢到资源了。所以并不是线程A在上面就先执行,是要看谁先抢到资源,只不过线程A在上面抢到的概率更大。如果有不同意见请在评论区指出,我们共同学习,共同进步!!!
class Phone {
public synchronized void sendEmail() throws InterruptedException {
TimeUnit.SECONDS.sleep(3);
System.out.println("--------send email----------");
}
public synchronized void sendSMS() {
System.out.println("--------send SMS----------");
}
}
public class Lock8 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
try {
// TimeUnit.SECONDS.sleep(2);
phone.sendEmail();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
new Thread(phone::sendSMS, "B").start();
}
}
问题三:
3、新增一个普通方法hello(),先发邮件还是先执行hello()?
加了普通方法后,这个普通方法和同步锁无关。即一个普通方法hello()并没有加synchronized,所以不会和被synchronized的发邮件方法冲突,而发邮件sleep()了三秒,所以先执行hello()
class Phone {
public synchronized void sendEmail() throws InterruptedException {
TimeUnit.SECONDS.sleep(3);
System.out.println("--------send email----------");
}
public void hello() {
System.out.println("--------say hello----------");
}
}
public class Lock8 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(phone::sendEmail, "A").start();
new Thread(phone::hello, "B").start();
}
}
问题四:
4、两部手机(两个资源类对象),先发邮件还是先发送短信?
这个问题都不是同一个对象(不是同一把锁)了,所以并不存在抢资源的问题,谁先调用谁先打印
class Phone {
public synchronized void sendEmail() throws InterruptedException {
TimeUnit.SECONDS.sleep(3);
System.out.println("--------send email----------");
}
public synchronized void sendSMS() {
System.out.println("--------send SMS----------");
}
}
public class Lock8 {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(phone::sendEmail, "A").start();
new Thread(phone2::sendSMS, "B").start();
}
}
问题五和问题六
5、两个静态方法,同一部手机,先发邮件还是先发短信?
6、两个静态方法,两部手机,先发邮件还是先发短信?
由于这两个问题都是static和synchronized修饰的同步锁,锁住的是当前类,即当前class充当锁,同一把锁,谁先抢到谁先执行
package com.monster.ticket.communication;
import java.util.concurrent.TimeUnit;
/**
* @author Monster
* @version v1.0
* @time 03-29-2021 20:59:43
* @description:
*/
class Phone {
public static synchronized void sendEmail() throws InterruptedException {
TimeUnit.SECONDS.sleep(3);
System.out.println("--------send email----------");
}
public static synchronized void sendSMS() {
System.out.println("--------send SMS----------");
}
}
public class Lock8 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(2);
phone.sendEmail();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
new Thread(phone::hello, "B").start();
}
}
问题七和问题八
7、一个普通方法,一个静态方法,一部手机,先发邮件还是先发短信?
8、一个普通方法,一个静态方法,两部手机,先发邮件还是先发短信?
同理,普通的synchronized方法是当前对象phone充当锁,而静态方法是当前类充当锁,他们之间没有竞争关系。
class Phone {
public static synchronized void sendEmail() throws InterruptedException {
TimeUnit.SECONDS.sleep(3);
System.out.println("--------send email----------");
}
public synchronized void sendSMS() {
System.out.println("--------send SMS----------");
}
}
public class Lock8 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(2);
phone.sendEmail();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
new Thread(phone::hello, "B").start();
}
}
总结:
synchronized实现同步的基础: Java中的每一个对象都可以作为锁。具体表现为以下3种形式。
对于普通同步方法,锁是当前实例对象。
对于静态同步方法,锁是当前类的cLass对象。
对于同步方法块,锁是synchonized括号里配置的对象。
所有的非静态同步方法用的都是同一把锁—实例对象本身。当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁,所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。所有的静态同步方法用的也是同一把锁—类对象本身,这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,而不管是同一个实例对象的静态同步方法之间,还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象!