Java: 线程能够调用某个其他线程的同步块中的方法

Java: thread able to call a method that is in synchronised block of some other thread

线程 t1 正在调用 test1() Test class 对象对象的 test1() 方法。
线程 t2 正在同步块中调用 Test class 对象 ob 的 test1() 方法。
t1 能够调用 ob 的 test1() 方法,即使 ob 的 test1() 方法调用在线程 t2.

的同步块中

代码如下:

class Test {
    void test1() {
        while(1 == 1) {
            System.out.println(Thread.currentThread().getName() + " test1!");
        }
    }

    void test2() {
        while(1 == 1) {
            System.out.println(Thread.currentThread().getName() + " test2!");
        }
    }
}

class NewThread1 implements Runnable {
    Thread t;
    String name;
    Test target;

    NewThread1(Test ob, String threadname) {
        target = ob;
        name = threadname;
        t = new Thread(this, name);
    }

    public void run() {
        target.test1();
    }
}

class NewThread2 implements Runnable {
    Thread t;
    String name;
    Test target;

    NewThread2(Test ob, String threadname) {
        target = ob;
        name = threadname;
        t = new Thread(this, name);
    }

    public void run() {
        synchronized(target) {
            target.test1();
        }
    }
}

class Test1 {
    public static void main(String args[]) {
        Test ob = new Test();
        NewThread1 t1 = new NewThread1(ob, "t1");
        NewThread2 t2 = new NewThread2(ob, "t2");

        t2.t.start();
        t1.t.start();

        try {
            t1.t.join();
            t2.t.join();
        } catch(InterruptedException e) {
            System.out.println("Main thread interrupted");
        }

        System.out.println("Main thread exiting");
    }
}

由于 NewThread1#run() 不是同步的,它不会尝试获取目标上的监视器,因此它不会被阻塞,即使另一个线程持有它的监视器,它也可以调用目标上的方法.

Synchronized 只有在所有线程都与具有同步部分的同一个监视器竞争时才能排他性地锁定其他线程。 (调用 test1 或 test2 都没有关系,检查发生在基于目标的同步中)。您可以做的是使 test1 和 test2 同步方法,然后它们将尝试在所有情况下保留实例的监视器)。这不仅适用于独占执行,而且适用于您可能希望退出同步块的任何内存访问保证(发生在之后)。

顺便说一句,你不需要不同的线程 类,如果你只使用一个线程(带同步的线程),它会像预期的那样工作。

Thread t1 = new NewThread2(ob, "t1");
Thread t2 = new NewThread2(ob, "t2");

但是,如果您的锁定范围很窄,则最好将锁定定位在目标 Test 的(所有)实例方法内,因为这样您就永远无法在缺少同步(和您可以切换到其他锁定原语而调用者不必知道)。

void synchronized test1() {
    while(1 == 1) {
        System.out.println(Thread.currentThread().getName() + " test1!");
    }
}

void test1() {
    synchronized(this) {
        while(1 == 1) {
            System.out.println(Thread.currentThread().getName() + " test1!");
        }
    }
}