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线程中的资源的情况,这时就可能出现死锁。我们在开发时要杜绝资源相互调用的情况。