多线程8锁问题
在多线程环境中,访问资源类的顺序到底是如何?
最近,在网络上看到一道题目,通过多线程的8种锁情况了解访问资源类的顺序。
- 标准访问,先打印邮件方法还是短信方法?
class Phone{
public synchronized void sendEmail(){
System.out.println("----sendEmail----");
}
public synchronized void sendSMS(){
System.out.println("----sendSMS----");
}
}
public class lock8 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(() -> {
phone.sendEmail();
}, "A").start();
//睡眠4毫秒
TimeUnit.MILLISECONDS.sleep(4);
new Thread(() -> {
phone.sendSMS();
}, "B").start();
}
}
运行结果:
synchronized关键字锁的不是当前方法,而是该方法所在的整个资源类,也就是说,同一时间下,只能有一个线程进入当前的资源类访问同步方法,所以运行结果跟方法调用的顺序相同。
- 邮件方法暂停4秒,先打印邮件方法还是短信方法?
class Phone {
public synchronized void sendEmail() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("----sendEmail----");
}
public synchronized void sendSMS(){
System.out.println("----sendSMS----");
}
}
运行结果:
第二锁的原理跟第一锁相同,只不过是在发邮件方法中线程睡眠了4秒,当线程A访问发邮件方法睡眠时,线程B无法进入该资源类,所以运行结果还是相同。
- 新增一个普通方法hello,先打印邮件方法还是短信方法?
class Phone {
public synchronized void sendEmail() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("----sendEmail----");
}
public void hello(){
System.out.println("----hello----");
}
}
public class lock8 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(() -> {
phone.sendEmail();
}, "A").start();
TimeUnit.MILLISECONDS.sleep(4);
new Thread(() -> {
phone.hello();
}, "B").start();
}
}
运行结果:
新增的hello方法并没有被synchronized关键字修饰,不是同步方法,所以在线程A访问发邮件方法时,线程B可以访问到hello方法,而线程A需要睡眠4秒,所以运行结果是hello先。
- 两个资源类,先打印邮件方法还是短信方法?
class Phone {
public synchronized void sendEmail() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("----sendEmail----");
}
public synchronized void sendSMS(){
System.out.println("----sendSMS----");
}
}
public class lock8 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(() -> {
phone.sendEmail();
}, "A").start();
TimeUnit.MILLISECONDS.sleep(4);
new Thread(() -> {
phone2.sendSMS();
}, "B").start();
}
}
运行结果:
第四锁中创建了两个资源类,main方法中访问的是两个不同资源类的方法,相互并不干扰,而发邮件的方法睡眠4秒,所以发短信的方法会先打印。
- 两个静态同步方法,同一个资源类,先打印邮件方法还是短信方法?
class Phone {
public static synchronized void sendEmail() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("----sendEmail----");
}
public static synchronized void sendSMS(){
System.out.println("----sendSMS----");
}
}
public class lock8 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(() -> {
phone.sendEmail();
}, "A").start();
TimeUnit.MILLISECONDS.sleep(4);
new Thread(() -> {
phone.sendSMS();
}, "B").start();
}
}
运行结果:
这题涉及到static静态,锁的不再是这个资源类对象,锁的是这个对象在类加载器中的类模板(.class),但由于还是同一个资源类,所以的顺序原理还是跟第一锁差不多。
- 两个静态同步方法,两个资源类,先打印邮件方法还是短信方法?
class Phone {
public static synchronized void sendEmail() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("----sendEmail----");
}
public static synchronized void sendSMS(){
System.out.println("----sendSMS----");
}
}
public class lock8 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(() -> {
phone.sendEmail();
}, "A").start();
TimeUnit.MILLISECONDS.sleep(4);
new Thread(() -> {
phone2.sendSMS();
}, "B").start();
}
}
运行结果:
第六锁变换成两个资源类,但由于static锁的是整个类的类模板(.class),所以不论实例化多少个资源类访问的结果还是相同的。
- 一个同步方法,一个静态同步方法,一个资源类,先打印邮件方法还是短信方法?
class Phone {
public static synchronized void sendEmail() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("----sendEmail----");
}
public synchronized void sendSMS(){
System.out.println("----sendSMS----");
}
}
public class lock8 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(() -> {
phone.sendEmail();
}, "A").start();
TimeUnit.MILLISECONDS.sleep(4);
new Thread(() -> {
phone.sendSMS();
}, "B").start();
}
}
运行结果:
第七锁,一个静态同步方法,一个普通同步方法,一个锁的是整个类模板,一个锁的是当前对象,两者相互不冲突,所以发短信先显示,发邮件的需要睡眠4秒才显示。
- 一个同步方法,一个静态同步方法,两个资源类,先打印邮件方法还是短信方法?
class Phone {
public static synchronized void sendEmail() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("----sendEmail----");
}
public synchronized void sendSMS(){
System.out.println("----sendSMS----");
}
}
public class lock8 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(() -> {
phone.sendEmail();
}, "A").start();
TimeUnit.MILLISECONDS.sleep(4);
new Thread(() -> {
phone2.sendSMS();
}, "B").start();
}
}
运行结果:
第八锁与第七锁的原理类似。
总结
synchronized实现同步的基础:Java中的每个对象都可以作为锁。
具体表现为以下三种形式:
- 对于普通同步方法,锁的是当前实例对象
- 对于静态同步方法,锁的是当前类的Class对象
- 对于同步方法快,锁的是synchronized括号里配置的对象