8锁问题(对象锁、类锁)
1.初始场景
- Phone资源类有
void sendMessage() 、void call()两个方法 - main方法开启 多线程场景 进行测试,8种情况可以提出8个问题,即8锁问题
- JUC下线程sleep方法:
imeUnit.SECONDS.sleep(1);
public class Demo1 {
public static void main(String[] args) {
}
}
class Phone {
public synchronized void sendMessage(){
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
##### 1.创建一个Phone实例多线程调用两个方法,问哪一个先执行?
package eight_locks;
import java.util.concurrent.TimeUnit;
public class Demo1 {
public static void main(String[] args) {
Phone p = new Phone();
// 发短信
new Thread(()->{
p.sendMessage();
}).start();
// JUC下的线程延时方法
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 打电话
new Thread(()->{
p.call();
}).start();
}
}
class Phone {
public synchronized void sendMessage(){
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
输出:
发短信
打电话
- 因为synchronized关键字 是对 该资源类的对象 上锁,因此哪个线程先拿到对象锁,就先执行
- 因此上面的线程先拿到锁,先执行,如果还不理解接着看问题2
2.创建一个Phone实例多线程调用两个方法,其中第一个线程调用的方法中加延迟,问哪一个先执行?
import java.util.concurrent.TimeUnit;
public class Demo1 {
public static void main(String[] args) {
Phone p = new Phone();
// 发短信
new Thread(()->{
p.sendMessage();
}).start();
// JUC下的线程延时方法
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 打电话
new Thread(()->{
p.call();
}).start();
}
}
class Phone {
public synchronized void sendMessage(){
// JUC下的线程延时方法
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
输出:
发短信
打电话
结果分析
- 原理同上。还是上面的线程先拿到 资源类 锁对象
- 同一资源类下,synchronized修饰的方法 使用的是当前资源类 对象锁。谁先拿到谁执行。
3.创建一个Phone实例多线程调用两个方法,其中一个是普通方法,而且该线程位置靠后,问哪一个先执行?
import java.util.concurrent.TimeUnit;
public class Demo3 {
public static void main(String[] args) {
Phone3 p = new Phone3();
// 发短信
new Thread(()->{
p.sendMessage();
}).start();
// JUC下的线程延时方法
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 打电话
new Thread(()->{
p.watchMovie();
}).start();
}
}
class Phone3 {
public synchronized void sendMessage(){
// JUC下的线程延时方法
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
public void watchMovie(){
System.out.println("看电影");
}
}
输出:
看电影
发短信
结果分析
watchMovie()为普通方法,不受同步方法的影响,也就是不受 锁 的影响- 因为
sendMessage()方法体 中有延迟语句,因此会后输出,其实 抛去延迟来说,两个输出结果为不一定
4.创建两个Phone实例多线程调用两个方法,问哪一个先执行?
import java.util.concurrent.TimeUnit;
public class Demo4 {
public static void main(String[] args) {
Phone4 one = new Phone4();
Phone4 two = new Phone4();
// 发短信
new Thread(()->{
one.sendMessage();
}).start();
// JUC下的线程延时方法
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 打电话
new Thread(()->{
two.call();
}).start();
}
}
class Phone4 {
public synchronized void sendMessage(){
// JUC下的线程延时方法
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
输出:
打电话
发短信
结果分析
- 区别于问题1,该情况是 两个资源类对象分别开启两个线程,因此锁对象 并无互相干扰,因为线程延时的原因,打电话 先输出
- 由此可在次证明synchronized加载方法前面锁住的是 调用该方法的 实例对象,多个实例之间的锁 不干扰
5.创建一个Phone实例多线程调用两个方法,两个方法都有static修饰,问哪一个先执行?
import java.util.concurrent.TimeUnit;
public class Demo5 {
public static void main(String[] args) {
Phone5 one = new Phone5();
// 发短信
new Thread(()->{
one.sendMessage();
}).start();
// JUC下的线程延时方法
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 打电话
new Thread(()->{
one.call();
}).start();
}
}
class Phone5 {
public synchronized static void sendMessage(){
// JUC下的线程延时方法
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized static void call(){
System.out.println("打电话");
}
}
输出:
发短信
打电话
问题分析
- 加上static关键字之后,两个方法都变为静态方法。
- 发短信 在前面的原因是 synchronized 加 静态方法 锁的是 Class ,
Phone5.Class只有单个。因此第二个线程需要 等待第一个线程释放Class锁才能执行。
6.创建两个Phone实例多线程调用两个方法,两个方法都有static修饰,问哪一个先执行?
import java.util.concurrent.TimeUnit;
public class Demo6 {
public static void main(String[] args) {
Phone6 one = new Phone6();
Phone6 two = new Phone6();
// 发短信
new Thread(()->{
one.sendMessage();
}).start();
// JUC下的线程延时方法
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 打电话
new Thread(()->{
two.call();
}).start();
}
}
class Phone6 {
public synchronized static void sendMessage(){
// JUC下的线程延时方法
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized static void call(){
System.out.println("打电话");
}
}
输出:
发短信
打电话
结果分析
- 两个对象的Class只有一个Phone6.Class
- 原理同5,synchronized 加 静态方法 锁的是 Class
7.创建一个Phone实例多线程调用两个方法,其中一个有static修饰,而且调用线程在前面,问哪一个先执行?
import java.util.concurrent.TimeUnit;
public class Demo7 {
public static void main(String[] args) {
Phone7 one = new Phone7();
// 发短信
new Thread(()->{
one.sendMessage();
}).start();
// JUC下的线程延时方法
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 打电话
new Thread(()->{
one.call();
}).start();
}
}
class Phone7 {
public synchronized static void sendMessage(){
// JUC下的线程延时方法
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
输出:
打电话
发短信
结果分析
- 打电话 先输出的原因是 Phone7 实例 和 Phone7.Class 分别被锁,两个线程之间并无影响,因为线程延迟的原因。
- 再次 证明 synchronized 锁的是 类实例即对象 、synchronized 加 静态方法 锁的是 Class
8.创建两个Phone实例多线程调用两个方法,其中一个有static修饰,而且调用线程在前面,问哪一个先执行?
import java.util.concurrent.TimeUnit;
public class Demo7 {
public static void main(String[] args) {
Phone8 one = new Phone8();
Phone8 two = new Phone8();
// 发短信
new Thread(()->{
one.sendMessage();
}).start();
// JUC下的线程延时方法
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 打电话
new Thread(()->{
two.call();
}).start();
}
}
class Phone7 {
public synchronized static void sendMessage(){
// JUC下的线程延时方法
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
输出:
打电话
发短信
结果分析
- 原理同7。两个线程分别锁的是 Phone8.Class 和 Phone8实例,因为线程延迟,打电话 才会优先输出。