【JUC剖析】八锁现象 探析
欢迎来到 (Java版)“八年抗战” 现场!
经历了本篇博文,相信同学们对于 锁 的理解就很彻底了!
那么,现在就开始内容的讲解吧:
两个synchronized方法:
synchronized 锁的对象 是 方法调用者
两个方法共用一把锁,谁先拿到锁,谁先执行
例如:
package edu.youzg.core;
import java.util.concurrent.TimeUnit;
/**
* 8锁现象
* @Author: Youzg
* @CreateTime: 2020-07-30 08:20
* @Description: 带你深究Java的本质!
*/
public class EightLock {
private static Desktop desktop = new Desktop();
public static void main(String[] args) {
new Thread(() -> {
desktop.chat();
}, "线程1").start();
new Thread(() -> {
desktop.game();
}, "未加锁方法").start();
}
}
class Desktop {
// synchronized 锁的对象 是 方法调用者
// 两个方法共用一把锁,谁先拿到锁,谁先执行
// 锁资源 就是 主线程中new的 desktop对象
public synchronized void chat() {
System.out.println("聊天功能");
}
public synchronized void game() {
System.out.println("游戏功能");
}
}
如上面的代码,锁资源
就是 主线程中new的desktop对象
这样的结果,相信很多同学都能预料到
那么接下来,就慢慢加大难度了
synchronized同步方法 + 前一个方法内部延迟:
synchronized 锁的对象 是 方法调用者
两个方法共用一把锁,谁先拿到锁,谁先执行
例如:
package edu.youzg.core;
import java.util.concurrent.TimeUnit;
/**
* 8锁现象
* @Author: Youzg
* @CreateTime: 2020-07-30 08:20
* @Description: 带你深究Java的本质!
*/
public class EightLock {
private static Desktop desktop = new Desktop();
public static void main(String[] args) {
new Thread(() -> {
desktop.chat();
}, "线程1").start();
new Thread(() -> {
desktop.game();
}, "未加锁方法").start();
}
}
class Desktop {
// synchronized 锁的对象 是 方法调用者
// 两个方法共用一把锁,涉嫌拿到锁,谁先执行
public synchronized void chat() {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("聊天功能");
}
public synchronized void game() {
System.out.println("游戏功能");
}
}
可以看到:和我们的分析一致!
普通方法 + synchronized同步方法:
synchronized 锁方法 与 普通方法
不竞争锁资源
两个线程竞争cpu时间片段 执行
例如:
package edu.youzg.core;
import java.util.concurrent.TimeUnit;
/**
* 8锁现象
* @Author: Youzg
* @CreateTime: 2020-07-30 08:20
* @Description: 带你深究Java的本质!
*/
public class EightLock {
private static Desktop desktop = new Desktop();
public static void main(String[] args) {
new Thread(() -> {
desktop.chat();
}, "线程1").start();
new Thread(() -> {
desktop.video();
}, "未加锁方法").start();
}
}
class Desktop {
// synchronized 锁方法 与 普通方法
// 不竞争锁资源,谁先执行完谁先输出
public synchronized void chat() {
System.out.println("聊天功能");
}
public void video() {
System.out.println("视频功能");
}
}
可以看到:和我们的分析一致!
两个锁对象:
两个锁对象 => 两个调用对象 => 两把锁
两个线程竞争cpu时间片段 执行
例如:
package edu.youzg.core;
import java.util.concurrent.TimeUnit;
/**
* 8锁现象
* @Author: Youzg
* @CreateTime: 2020-07-30 08:20
* @Description: 带你深究Java的本质!
*/
public class EightLock {
// 两个对象,两把锁
private static Desktop desktop1 = new Desktop();
private static Desktop desktop2 = new Desktop();
public static void main(String[] args) {
new Thread(() -> {
desktop1.chat();
}, "线程1").start();
new Thread(() -> {
desktop2.game();
}, "线程2").start();
}
}
class Desktop {
public synchronized void chat() {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("聊天功能");
}
public synchronized void game() {
System.out.println("游戏功能");
}
}
可以看到:
由于我们设置的第一个线程所执行的方法延时太大
因此在临界资源的竞争中,第二个线程竞争到了资源
两个 静态+同步 方法:
static静态方法,在类一旦加载,就初始化了此方法
Desktop 只存在唯一一个 Class对象
因此 两个静态方法 锁 都是 此类的Class对象
因此,按照调用顺序执行
例如:
package edu.youzg.core;
import java.util.concurrent.TimeUnit;
/**
* 8锁现象
* @Author: Youzg
* @CreateTime: 2020-07-30 08:20
* @Description: 带你深究Java的本质!
*/
public class EightLock {
public static void main(String[] args) {
new Thread(() -> {
Desktop.chat();
}, "线程1").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
Desktop.game();
}, "线程2").start();
}
}
// Desktop 只存在唯一一个 Class对象
// 因此 静态方法 锁的是此类的Class对象
class Desktop {
// static 方法
// 类一旦加载,就初始化了此方法
public static synchronized void chat() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("聊天功能");
}
// static 方法
// 类一旦加载,就初始化了此方法
public static synchronized void game() {
System.out.println("游戏功能");
}
}
可以看到:
和我们预料的是一样的!
两个对象 + 两个 静态+同步 方法:
static静态方法,在类一旦加载,就初始化了此方法
Desktop 只存在唯一一个 Class对象
因此 静态方法 锁 都是 此类的Class对象
因此,按照调用顺序执行
例如:
package edu.youzg.core;
import java.util.concurrent.TimeUnit;
/**
* 8锁现象
* @Author: Youzg
* @CreateTime: 2020-07-30 08:20
* @Description: 带你深究Java的本质!
*/
public class EightLock {
// 两个对象的Class对象只有一个
private static Desktop desktop1 = new Desktop();
private static Desktop desktop2 = new Desktop();
public static void main(String[] args) {
new Thread(() -> {
desktop1.chat();
}, "线程1").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
desktop2.game();
}, "线程2").start();
}
}
// Desktop 只存在唯一一个 Class对象
// 因此 静态方法 锁的是此类的Class对象
class Desktop {
// static 方法
// 类一旦加载,就初始化了此方法
public static synchronized void chat() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("聊天功能");
}
// static 方法
// 类一旦加载,就初始化了此方法
public static synchronized void game() {
System.out.println("游戏功能");
}
}
普通同步方法 + 静态同步方法:
静态方法 的 锁 是 此类的Class对象
普通同步方法 的 锁 不是本类Class对象
两个方法的锁不一样,互不影响
package edu.youzg.core;
import java.util.concurrent.TimeUnit;
/**
* 8锁现象
* @Author: Youzg
* @CreateTime: 2020-07-30 08:20
* @Description: 带你深究Java的本质!
*/
public class EightLock {
private static Desktop desktop = new Desktop();
public static void main(String[] args) {
new Thread(() -> {
desktop.chat();
}, "线程1").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
desktop.game();
}, "线程2").start();
}
}
// Desktop 只存在唯一一个 Class对象
// 因此 静态方法 锁的是此类的Class对象
class Desktop {
// static 方法
// 类一旦加载,就初始化了此方法
public static synchronized void chat() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("聊天功能");
}
// 普通同步方法
public synchronized void game() {
System.out.println("游戏功能");
}
}
两个对象 + 静态同步方法 + 普通同步方法:
两个对象的Class对象只有一个
但是 锁不相同(一个是Class对象,一个是this)
两个方法的锁不一样,互不影响
package edu.youzg.core;
import java.util.concurrent.TimeUnit;
/**
* 8锁现象
* @Author: Youzg
* @CreateTime: 2020-07-30 08:20
* @Description: 带你深究Java的本质!
*/
public class EightLock {
// 两个对象的Class对象只有一个
// 但是 锁不相同
private static Desktop desktop1 = new Desktop();
private static Desktop desktop2 = new Desktop();
public static void main(String[] args) {
new Thread(() -> {
desktop1.chat();
}, "线程1").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
desktop2.game();
}, "线程2").start();
}
}
// Desktop 只存在唯一一个 Class对象
// 因此 静态方法 锁的是此类的Class对象
class Desktop {
// static 方法
// 类一旦加载,就初始化了此方法
public static synchronized void chat() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("聊天功能");
}
// 普通同步方法
public synchronized void game() {
System.out.println("游戏功能");
}
}
技术总结:
- 当一个线程 试图 访问同步代码块时,
它首先必须得到锁,退出或抛出异常时必须释放锁- Java 中的每一个对象都可以作为锁;
普通同步方法锁 this,
静态同步方法锁 Class,
同步方法块锁 括号- 锁的对象不是同一个,就直接按照线程执行的快慢来决定;
锁的对象是同一个,就按照线程进入的先后顺序决定
到此为止,八锁问题全部解决!