前言
synchronized 八锁现象,就是 synchronized 使用中的八个问题,下面我们一一了解下。
一.同一个对象,两个线程分别调用对象的两个加锁方法
package com.example.demo.module.synchronized8Test;
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(phone::sendSms,"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(phone::call,"B").start();
}
}
class Phone{
synchronized void sendSms(){
System.out.println("----发短信----");
}
synchronized void call(){
System.out.println("----打电话----");
}
}
运行结果
结论:
synchronized
在普通方法上修饰时,锁的对象是方法调用者,该示例中都是同一对象 phone
调用两个用 synchronized
修饰的普通方法 sendSms
call
,所以哪个线程先拿到锁哪个线程就先执行。
二.同一个对象,两个线程分别调用对象的两个加锁方法,sendSms 方法加上延迟3秒
package com.example.demo.module.synchronized8Test;
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(phone::sendSms,"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(phone::call,"B").start();
}
}
class Phone{
synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("----发短信----");
}
synchronized void call(){
System.out.println("----打电话----");
}
}
运行结果
结论:
synchronized
在普通方法上修饰时,锁的对象是方法调用者,该示例中都是同一对象 phone
调用两个用 synchronized
修饰的普通方法 sendSms
call
,所以哪个线程先拿到锁哪个线程就先执行。
三.同一个对象,两个线程分别调用对象的加锁方法和无锁方法,sendSms 方法加上延迟3秒
package com.example.demo.module.synchronized8Test;
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(phone::sendSms,"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(phone::hello,"B").start();
}
}
class Phone{
synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("----发短信----");
}
synchronized void call(){
System.out.println("----打电话----");
}
void hello(){
System.out.println("----hello----");
}
}
运行结果
结论:
hello
方法未加锁,所以不受锁的限制,线程 B 延迟1秒后将直接执行 hello
方法,无需等待线程 A。
四.不同对象,调用各自的加锁方法,sendSms 方法加上延迟3秒
package com.example.demo.module.synchronized8Test;
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String[] args) {
Phone phone1 = new Phone();
Phone phone2 = new Phone();
new Thread(phone1::sendSms,"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(phone2::call,"B").start();
}
}
class Phone{
synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("----发短信----");
}
synchronized void call(){
System.out.println("----打电话----");
}
}
运行结果
结论:
synchronized
在普通方法上修饰时,锁的对象是方法调用者,该示例中对象 phone1
调用 sendSms
方法,对象 phone2
调用 call
方法,sendSms
方法锁的是 对象 phone1
, call
方法锁的是对象 phone2
,是两把不同的锁,互不影响。
五.同一个对象,两个线程分别调用对象的两个静态加锁方法,sendSms 方法加上延迟3秒
package com.example.demo.module.synchronized8Test;
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
phone.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone.call();
},"B").start();
}
}
class Phone{
static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("----发短信----");
}
static synchronized void call(){
System.out.println("----打电话----");
}
}
运行结果
结论:
synchronized
在静态方法上修饰时,锁的对象是 Class,该示例中同一对象 phone
调用两个用 synchronized
修饰的静态方法 sendSms
call
,为同一把锁,所以从上到下执行。
六.不同对象,调用各自的静态加锁方法,sendSms 方法加上延迟3秒
package com.example.demo.module.synchronized8Test;
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String[] args) {
Phone phone1 = new Phone();
Phone phone2 = new Phone();
new Thread(() -> {
phone1.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone2.call();
},"B").start();
}
}
class Phone{
static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("----发短信----");
}
static synchronized void call(){
System.out.println("----打电话----");
}
}
运行结果
结论:
synchronized
在静态方法上修饰时,锁的对象是 Class,该示例中对象 phone1
调用静态锁方法 sendSms
,对象 phone2
调用静态锁方法 call
,对象 phone1
phone2
的类模板都是 Phone.class
,所以为同一把锁,所以从上到下执行。
七.同一个对象,两个线程分别调用对象的静态加锁方法和普通加锁方法,sendSms 方法加上延迟3秒
package com.example.demo.module.synchronized8Test;
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
phone.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone.call();
},"B").start();
}
}
class Phone{
static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("----发短信----");
}
synchronized void call(){
System.out.println("----打电话----");
}
}
运行结果
结论:
线程 A 调用静态加锁方法 sendSms
,锁的是 Phone.class
,线程 B 调用普通加锁方法 call
,锁的是对象 phone
,是两把不同的锁,互不影响。
八.不同对象,分别调用静态加锁方法和普通加锁方法,sendSms 方法加上延迟3秒
package com.example.demo.module.synchronized8Test;
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String[] args) {
Phone phone1 = new Phone();
Phone phone2 = new Phone();
new Thread(() -> {
phone1.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone2.call();
},"B").start();
}
}
class Phone{
static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("----发短信----");
}
synchronized void call(){
System.out.println("----打电话----");
}
}
运行结果
结论:
对象 phone1
调用静态加锁方法 sendSms
,锁的是 Phone.class
,对象 phone2
调用普通加锁方法 call
,锁的是对象 phone2
,是两把不同的锁,互不影响。