synchronized总结
synchronized
使用方法
-
synchronized修饰普通方法,此时锁住的是当前实例对象,进入到同步方法时,需要获取当前实例对象的锁。因此,当多线程作用于两个实例对象时,并不能保证线程安全。
-
synchronized修饰静态方法,此时锁住的是当前类,进入同步方法时,需要获取当前类的锁。
-
synchronized修饰代码块,此时锁住的括号中的对象,进入同步代码块时,需要获取给定对象的锁。
代码实现
- 作用于同一实例的普通方法
public class SynchronizedMethod {
private int num = 0;
synchronized public void add(String type) {
try {
if (type.equals("a")) {
num = 100;
System.out.println("set A num!");
Thread.sleep(1000);
} else {
num = 200;
System.out.println("set B num!");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("set number = " + num);
}
}
@NoArgsConstructor
public class ThreadDemo extends Thread {
private SynchronizedMethod method;
private String type;
private ThisSynchronized thisSynchronized;
private String thisType;
public ThreadDemo(SynchronizedMethod method, String type) {
super();
this.method = method;
this.type = type;
}
public ThreadDemo(ThisSynchronized thisSynchronized, String thisType) {
this.thisSynchronized = thisSynchronized;
this.thisType = thisType;
}
@Override
public void run() {
super.run();
if (type != null) {
method.add(type);
} else if (thisType == "yes"){
thisSynchronized.serviceMethod();
} else if (thisType == "no") {
thisSynchronized.serviceNumber();
}
}
}
/**
* synchronized 修饰方法,调用一个对象
*/
public static void runSynchronizedOneMethod() {
SynchronizedMethod method = new SynchronizedMethod();
ThreadDemo threadA = new ThreadDemo(method,"a");
ThreadDemo threadB = new ThreadDemo(method, "b");
threadA.start();
threadB.start();
}
public class SynchronizedTest {
public static void main(String[] args) {
/**
* 当synchronized修饰了方法时,同步锁作用于对象,
* 调用同一个对象,
* 线程想要执行同步代码需要获取锁,多个线程之间存在竞争关系
* 所以执行顺序是同步的
*/
runSynchronizedOneMethod();
}
}
运行结果:
由运行结果可知,threadA与threadB是串行运行,多个线程之间竞争的是同一个实例method的锁
- 作用于两个不同实例的普通方法
public static void runSychronizedMayMethod() {
SynchronizedMethod methodA = new SynchronizedMethod();
SynchronizedMethod methodB = new SynchronizedMethod();
ThreadDemo threadA = new ThreadDemo(methodA,"a");
ThreadDemo threadB = new ThreadDemo(methodB, "b");
threadA.start();
threadB.start();
}
运行结果:
由运行结果可知,应为两个线程之间竞争的是不同实例的锁,所以两个线程是并行的,不存在竞争关系。
- 作用于静态方法
public static void runSychronizedStaticMethod() {
ThreadStaticDemo threadA = new ThreadStaticDemo("A");
threadA.setName("threadA");
ThreadStaticDemo threadB = new ThreadStaticDemo("B");
threadB.setName("threadB");
threadA.start();
threadB.start();
}
public class ThreadStaticDemo extends Thread {
private String type;
public ThreadStaticDemo(String type) {
this.type = type;
}
@Override
public void run() {
super.run();
if (type == "A") {
ThisSynchronized.staticServiceMethodA();
}
if (type == "B"){
ThisSynchronized.staticServiceMethodB();
}
if (type == "classA") {
ThisSynchronized.classServiceMethodA();
}
if (type == "classB") {
ThisSynchronized.classServiceMethodB();
}
}
}
synchronized public static void staticServiceMethodA() {
try {
System.out.println(" staticServiceMethodA 当前线程名称:" + Thread.currentThread().getName() + "进入同步代码块时间:" + System.currentTimeMillis());
Thread.sleep(1000);
System.out.println("staticServiceMethodA 当前线程名称:" + Thread.currentThread().getName() + "离开同步代码块时间:" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized public static void staticServiceMethodB() {
try {
System.out.println(" staticServiceMethodB 当前线程名称:" + Thread.currentThread().getName() + "进入同步代码块时间:" + System.currentTimeMillis());
Thread.sleep(1000);
System.out.println("staticServiceMethodB 当前线程名称:" + Thread.currentThread().getName() + "离开同步代码块时间:" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
运行结果:
由运行结果可知,由于staticServiceMethodA和staticServiceMethodB两个静态方法被synchronized修饰,两个线程竞争同一个类的锁,所以两个为串行状态。
- 作用于代码块
public static void runSychronizedThisMethod() {
ThisSynchronized thisSynchronized = new ThisSynchronized();
ThreadDemo threadA = new ThreadDemo(thisSynchronized, "yes");
ThreadDemo threadB = new ThreadDemo(thisSynchronized, "yes");
threadA.start();
threadB.start();
}
@NoArgsConstructor
public class ThreadDemo extends Thread {
private SynchronizedMethod method;
private String type;
private ThisSynchronized thisSynchronized;
private String thisType;
public ThreadDemo(SynchronizedMethod method, String type) {
super();
this.method = method;
this.type = type;
}
public ThreadDemo(ThisSynchronized thisSynchronized, String thisType) {
this.thisSynchronized = thisSynchronized;
this.thisType = thisType;
}
@Override
public void run() {
super.run();
if (type != null) {
method.add(type);
} else if (thisType == "yes"){
thisSynchronized.serviceMethod();
} else if (thisType == "no") {
thisSynchronized.serviceNumber();
}
}
}
public void serviceMethod() {
try {
synchronized (this) {
System.out.println("start = " + System.currentTimeMillis());
Thread.sleep(1000);
System.out.println("end = " + System.currentTimeMillis());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
运行结果:
因为两个线程竞争的为同一个对象的锁,所以线程的运行结果为串行状态
原理
jvm基于进入和退出Monitor对象来实现方法同步和代码块的同步。代码块的同步是使用monitorenter和monitorexit指令实现的,同步方法的也是通过这两个指令实现的同步,只是在修饰方法是使用ACC_SYNCHRONIZED来代替了。
monitorenter指令是在编译后插入到同步代码开始的位置,而monitorexit是插入在方法结束活异常的位置,JVM保证这两个命令必须是配对的。每一个对象都有一个monitor与之关联,当monitor被持有后,该对象就处于锁定状态。
参考java 偏向锁、轻量级锁及重量级锁synchronized原理
Synchronize 关键字原理
Synchronize 反编译码分析