synchronize可以锁住一个方法?

2020年7月8日
synchronize可以锁住一个方法?插图

本文出自明月工作室:https://www.freebytes.net/it/java/synchronize-use.html

记得有一次在面试一个实习生的时候,闻到了一个synchronize的相关问题,他提到“synchronize通过锁住一个方法或者一段代码实现同步效果”。这句话从表面上看其实也没有什么问题,毕竟从代码上看就是这么回事——

    public synchronized void run() {
        for (int i = 0; i < 1000; i++) {
            TestSynchronize4.count++;
        }
    }

    synchronized (TestSynchronize1.class) {
         for (int i = 0; i < 1000; i++) {
              TestSynchronize1.count++;
         }
    }

但如果是对synchronize稍微有研究的人,是不会这样说的。因为synchronize锁住的其实不是一个方法、一段代码,而是一个对象或者一个class类。

我们可以从synchronize的几种应用场景中,加深对其的理解。

案例1

public class TestSynchronize1 {
    public static int count = 0;

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            Thread1 thread = new Thread1();
            thread.start();
        }
        while (Thread.activeCount() > 1) {
        }
        System.out.println(count);
    }
}

class Thread1 extends Thread {
    @Override
    public void run() {
//        synchronized (Object.class) {
        synchronized (TestSynchronize1.class) {
            //锁住一个class类,理论上任何class都可以
            for (int i = 0; i < 1000; i++) {
                TestSynchronize1.count++;
            }
        }
    }
}

这里锁住的是一个class类,理论上任何class都可以作为锁对象。

案例2

public class TestSynchronize2 {
    public static int count = 0;
    public static TestSynchronize2 test=new TestSynchronize2();
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            Thread2 thread = new Thread2();
            thread.start();
        }
        while (Thread.activeCount() > 1) {
        }
        System.out.println(count);
    }
}

class Thread2 extends Thread {
    @Override
    public void run() {
        synchronized (TestSynchronize2.test) {
            //锁住了一个线程共享的对象
            for (int i = 0; i < 1000; i++) {
                TestSynchronize2.count++;
            }
        }
    }
}

这里锁住了一个线程共享的对象(线程共享static变量),理论上任何对象都可以,但如果该对象不是线程共享的,就会产生并发问题。

案例3

public class TestSynchronize3 {
    public static int count = 0;

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            Thread3 thread = new Thread3();
            thread.start();
        }
        while (Thread.activeCount() > 1) {
        }
        System.out.println(count);
    }
}

class Thread3 extends Thread {
    @Override
    public void run() {
        synchronized (this) {
            //锁住了一个对象,等同于锁住了Thread3的对象本身
            for (int i = 0; i < 1000; i++) {
                TestSynchronize3.count++;
            }
        }
    }
}

这里锁住了一个this,等同于锁住了Thread3的对象本身,但这个对象不是线程共享的,所以会出现并发问题。

案例4

public class TestSynchronize4 {
    public static int count = 0;

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            Thread4 thread = new Thread4();
            thread.start();
        }
        while (Thread.activeCount() > 1) {
        }
        System.out.println(count);
    }
}

class Thread4 extends Thread {
    @Override
    public synchronized void run() {
        //看起来是锁住了一个方法,但其实等同于synchronized (this),锁住了当前对象
        //如果该方法是静态的,那么等同于synchronized (Thread4.class),锁住当前类的class类
        for (int i = 0; i < 1000; i++) {
            TestSynchronize4.count++;
        }
    }
}

看起来是锁住了一个方法,但其实等同于synchronized (this),锁住了当前对象。 如果该方法是静态的,那么等同于synchronized (Thread4.class),锁住当前类的class类。

案例5

public class TestSynchronize5 {

    public static void main(String[] args) {
        Service1 service1 = new Service1();
        service1.start();
        Service2 service2 = new Service2();
        service2.start();
    }
}

class Service1 extends Thread {
    @Override
    public void run() {
        //即使两个不同的线程,由于用了同一个锁,也会实现同步的效果
        synchronized (TestSynchronize5.class) {
            System.out.println("线程---"+Thread.currentThread().getName()+"---进入");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程---"+Thread.currentThread().getName()+"---退出");
        }
    }
}

class Service2 extends Thread {
    @Override
    public void run() {
        //即使两个不同的线程,由于用了同一个锁,也会实现同步的效果
        synchronized (TestSynchronize5.class) {
            System.out.println("线程---"+Thread.currentThread().getName()+"---进入");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程---"+Thread.currentThread().getName()+"---退出");
        }
    }
}
控制台输出:
线程---Thread-0---进入
线程---Thread-0---退出
线程---Thread-1---进入
线程---Thread-1---退出

这里是正常的,没有出现并发问题。说明即使两个不同的线程,由于用了同一个对象锁,也能够实现同步的效果。