synchronized的作用范围
- synchronized作用于成员变量和非静态方法时,锁住的是对象的实例,即this对象
- synchronized作用于静态方法时,锁住的是Class实例,因为静态方法属于Class而不属于对象。
- synchronized作用于一个代码块时,锁住的是所有代码块中配置的对象
synchronized作用于成员变量和非静态方法时,锁住的是对象的实例,即this对象
public class SynchronizedDemo {
public static void main(String[] args) {
final SynchronizedDemo synchronizedDemo=new SynchronizedDemo();
new Thread(new Runnable() {
@Override
public void run() {
synchronizedDemo.generalMethod1();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
synchronizedDemo.generalMethod2();
}
}).start();
}
public synchronized void generalMethod1(){
for (int i = 0; i < 3; i++) {
System.out.println("generalMethod1 excute"+i+" time");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void generalMethod2(){
for (int i = 0; i < 3; i++) {
System.out.println("generalMethod2 excute"+i+" time");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
上面的程序定义了两个使用synchronized修饰的普通方法,然后在main函数中定义对象的实例并发执行各个方法。我们看到,线程 1会等待线程 2执行完成才能执行,这是因为synchronized锁住了当前的对象实例synchronizedDemo导致的。具体的执行结果如下执行结果:
generalMethod1 excute0 time
generalMethod1 excute1 time
generalMethod1 excute2 time
generalMethod2 excute0 time
generalMethod2 excute1 time
generalMethod2 excute2 time
稍微把程序修改一下,定义两个实例分别调用两个方法,程序就能并发执行起来了:
final SynchronizedDemo synchronizedDemo1=new SynchronizedDemo();
final SynchronizedDemo synchronizedDemo2=new SynchronizedDemo();
new Thread(new Runnable() {
@Override
public void run() {
synchronizedDemo1.generalMethod1();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
synchronizedDemo2.generalMethod2();
}
}).start();
}
执行结果:
generalMethod1 excute0 time
generalMethod2 excute0 time
generalMethod1 excute1 time
generalMethod2 excute1 time
generalMethod1 excute2 time
generalMethod2 excute2 time
synchronized作用于静态方法时,锁住的是Class实例,因为静态方法属于Class而不属于对象。
具体的使用代码如下,我们只需在以上方法上加上static关键字即可:
public synchronized static void generalMethod1(){
for (int i = 0; i < 3; i++) {
System.out.println("generalMethod1 excute"+i+" time");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized static void generalMethod2(){
for (int i = 0; i < 3; i++) {
System.out.println("generalMethod2 excute"+i+" time");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
执行结果:
generalMethod1 excute0 time
generalMethod1 excute1 time
generalMethod1 excute2 time
generalMethod2 excute0 time
generalMethod2 excute1 time
generalMethod2 excute2 time
我们通过日志能清晰地看到,因为static方法是属于Class的,并且Class的相关数据在JVM中是全局共享的,因此静态方法锁相当于类的一个全局锁,会锁住所有调用该方法的线程。
synchronized作用于一个代码块时,锁住的是所有代码块中配置的对象
具体的实现代码如下:
public class SynchronizedDemo {
Object lockA=new Object();
public static void main(String[] args) {
final SynchronizedDemo synchronizedDemo=new SynchronizedDemo();
new Thread(new Runnable() {
@Override
public void run() {
synchronizedDemo.blockMethod1();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
synchronizedDemo.blockMethod2();
}
}).start();
}
public void blockMethod1(){
synchronized (lockA){
for (int i = 0; i < 3; i++) {
System.out.println("generalMethod1 excute"+i+" time");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public void blockMethod2(){
synchronized (lockA) {
for (int i = 0; i < 3; i++) {
System.out.println("generalMethod2 excute" + i + " time");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
以上代码的执行结果很简单,由于两个方法都需要获取名为lockA的锁,所以线程 1会等待线程2执行完成后才能获取该锁并执行:
generalMethod1 excute0 time
generalMethod1 excute1 time
generalMethod1 excute2 time
generalMethod2 excute0 time
generalMethod2 excute1 time
generalMethod2 excute2 time
我们在写多线程程序时可能会出现A线程依赖B线程中的资源,而B线程又依赖于A线程中的资源的情况,这时就可能出现死锁。我们在开发时要杜绝资源相互调用的情况。