Version: Next
八锁现象彻底理解锁——到底锁了啥
锁只会锁两种东西
- 对象 (可能有多个)
- Class模板对象 (唯一)
八锁,就是关于锁的八个问题
1、synchronized修饰方法,锁的是方法的调用者
下面这段例子中:
sendMsg()
和call()
使用了synchronized
修饰- 创建一个唯一的
phone
对象,调用这两个方法,那么锁的就是phone
对象- 两个方法使用的是同一把锁,谁先拿到,谁先执行
public class Test1 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sendMsg();
},"A").start();
try { // 用JUC的TimeUnit使线程睡1秒
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
},"B").start();
}
}
class Phone{
public synchronized void sendMsg(){
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
发短信
打电话
执行结果总是,先“发短信”,再“打电话“
2、让一个同步方法沉睡
直观的感受是,因为发短信这个方法先被调用,所以结果会这样,那么在发短信方法中再睡一会儿,观察一下,结果还是不变的
public class Test1 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sendMsg();
},"A").start();
try { // 用JUC的TimeUnit使线程睡1秒
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
},"B").start();
}
}
class Phone{
public synchronized void sendMsg(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
发短信
打电话
出现这样的结果,是因为synchronized的存在,锁住了调用方法的对象,phone
3、设置一个非synchronized方法
定义一个不用synchronized修饰的方法,在第二个线程中,不再调用打电话方法,而是调用这个没有用synchronized修饰的方法
public class Test2 {
public static void main(String[] args) {
Phone2 phone = new Phone2();
new Thread(() -> {
phone.sendMsg();
}, "A").start();
try { // 用JUC的TimeUnit使线程睡1秒
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone.hello();
}, "B").start();
}
}
class Phone2 {
public synchronized void sendMsg() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call() {
System.out.println("打电话");
}
public void hello() {
System.out.println("hello");
}
}
hello
发短信
主线程睡了一秒,然后打出了"hello";发短信方法睡了4秒,打出了"发短信"
4、设置两个资源对象
如果有两个phone对象
public class Test3 {
public static void main(String[] args) {
Phone3 phone1 = new Phone3();
Phone3 phone2 = new Phone3();
new Thread(() -> {
phone1.sendMsg();
}, "A").start();
try { // 用JUC的TimeUnit使线程睡1秒
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone2.call();
}, "B").start();
}
}
class Phone3 {
public synchronized void sendMsg() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call() {
System.out.println("打电话");
}
}
- 执行之后,首先进入发短信方法,开始睡4秒;主线程睡了1秒后进入打电话方法,打出“打电话”,此时发短信已经睡了1秒,于是又过了3秒,打出“发短信”
- 两个synchronized,锁都不是同一个phone对象
打电话
发短信
5、设置两个静态同步方法
静态同步方法,锁的不再是资源对象,而是锁Class反射对象,Phone.class对象,全局唯一
public class Test4 {
public static void main(String[] args) {
Phone4 phone = new Phone4();
new Thread(() -> {
phone.sendMsg();
}, "A").start();
try { // 用JUC的TimeUnit使线程睡1秒
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone.call();
}, "B").start();
}
}
class Phone4 {
public static synchronized void sendMsg() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public static synchronized void call() {
System.out.println("打电话");
}
}
睡4秒,然后打出"发短信、打电话"
发短信
打电话
6、两个静态同步方法 + 两个资源对象
如果在这种情况下,设置两个资源对象
public class Test4 {
public static void main(String[] args) {
Phone4 phone1 = new Phone4();
Phone4 phone2 = new Phone4();
new Thread(() -> {
phone1.sendMsg();
}, "A").start();
try { // 用JUC的TimeUnit使线程睡1秒
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone2.call();
}, "B").start();
}
}
class Phone4 {
public static synchronized void sendMsg() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public static synchronized void call() {
System.out.println("打电话");
}
}
依然是睡4秒,然后打印出“发短信、打电话”,因为静态同步方法,就不再锁资源对象,而是锁资源类的Class对象
发短信
打电话
7、一个静态同步方法 + 一个普通同步方法
public class Test5 {
public static void main(String[] args) {
Phone5 phone = new Phone5();
new Thread(() -> {
phone.sendMsg();
}, "A").start();
try { // 用JUC的TimeUnit使线程睡1秒
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone.call();
}, "B").start();
}
}
class Phone5 {
public static synchronized void sendMsg() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call() {
System.out.println("打电话");
}
}
一个锁的是Class对象,一个锁的是phone实例化对象,不是同一把锁
- 主线程睡1秒,打出“打电话”
- 再睡3秒后,打出“发短信”
打电话
发短信
8、一个静态同步方法 + 一个普通同步方法 + 两个资源对象
设置两个资源对象
public class Test5 {
public static void main(String[] args) {
Phone5 phone1= new Phone5();
Phone5 phone2= new Phone5();
new Thread(() -> {
phone1.sendMsg();
}, "A").start();
try { // 用JUC的TimeUnit使线程睡1秒
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone2.call();
}, "B").start();
}
}
class Phone5 {
public static synchronized void sendMsg() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call() {
System.out.println("打电话");
}
}
一个锁的是类的Class对象,另一个锁的是调用者实例化对象phone2,不是同一把锁
- 主线程睡1秒,打出“打电话”
- 发短信方法已经睡了1秒,再睡3秒,打出“发短信”
打电话
发短信