【问题标题】:Java Synchronized : Wired behavior under two threads [duplicate]Java同步:两个线程下的有线行为[重复]
【发布时间】:2017-10-16 18:39:03
【问题描述】:

我创建了一个简单的 Worker:

public class Worker {

    public synchronized void writeData() {
        try {
            System.out.println("write Data , thread id = " + Thread.currentThread().getId());
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void readData() {
        try {
            System.out.println("readData , thread id = " + Thread.currentThread().getId());
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

AFAIK,如果多个线程访问同一个 Worker 实例,synchronized 只会阻塞访问同一方法的线程。 AKA 如果线程 A 调用 writeData 而 B 使用 readData,它们不会相互影响(如果我错了,请纠正我)。

但是,当我尝试通过以下代码进行演示时:

private static void testWithThreads() {
        final Worker worker = new Worker();

        new Thread(() -> {
            System.out.println("start read thread");
            for (int i = 0; i < 20; i++) {
                worker.readData();
            }

        }).start();

        new Thread(() -> {
            System.out.println("start write thread");
            for (int i = 0; i < 20; i++) {
                worker.writeData();
            }

        }).start();
    }

我得到了这样的输出(注意我们这里有Thread.sleep 2 秒):

start read thread
readData , thread id = 10
start write thread
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10

谁能给我解释一下?他们似乎以某种方式相互屏蔽了。

【问题讨论】:

标签: java multithreading


【解决方案1】:

同步只阻塞访问相同方法的线程

错了。它会阻止尝试在同一对象上同步的线程。

【讨论】:

  • 谢谢,上周有件事让我头疼。 ://
【解决方案2】:

它的工作原理是,如果 A 将 writeData 用于 Worker 实例,那么 B 不能使用来自同一个 Worker 的 readData 或 writeData,直到有机会。

如果你希望你的输出是:

阅读

阅读

等等……

那么我建议使用函数 wait();和通知所有(); 这样,您可以在线程 A 完成后让线程 B 转一圈,反之亦然。

您可以阅读有关 wait() 和 notifyAll() 的更多信息 here.

【讨论】:

    【解决方案3】:

    synchronized 在方法级别上同步访问该方法所属的对象的所有同步方法,只有一个线程可以在该对象的任何同步方法中执行。即使其他线程尝试访问除第一个线程之外的其他synchronized 方法,它们也会等待。

    其他线程将阻塞,直到第一个线程退出同步块。

    在您的代码中,在for 循环中调用同步方法之间,有一个很小的时隙,其他线程可以在第一次再次进入 readData() 之前进入 writeData() - 一个典型的 for 循环不是原子操作 - 但这个时隙是如此之小,以至于它很少发生 - 所以你的输出看起来像是在以某种方式相互阻塞 - 并且在某一时刻,风变了,其他线程带头。

    更具体地说,cmets 指向每个 for 循环中“不同步”时隙的开始位置:

    private static void testWithThreads() {
            final Worker worker = new Worker();
    
            new Thread(() -> {
                System.out.println("start read thread");
                for (int i = 0; i < 20; i++) {
                    worker.readData();
                    // any thread can now invoke writeData() if current thread is before next invocation of worker.readData();
                }
    
                try {
                    Thread.currentThread().join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
    
            new Thread(() -> {
                System.out.println("start write thread");
                for (int i = 0; i < 20; i++) {
                    worker.writeData();
                    // any thread can now invoke readData() if current thread is before next invocation of worker.writeData();
                }
    
                try {
                    Thread.currentThread().join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    

    如果您想要更好的交错,您可以执行以下操作之一:

    • 使用 wait() 和 notify()
    • 不要在该方法上使用同步 - 同步 数据。
    • 将睡眠操作移到同步的读写方法之外, 他们会给线程更多的机会进入 同步块。

    【讨论】:

    • 请解释一下否决票?
    猜你喜欢
    • 2013-11-12
    • 1970-01-01
    • 1970-01-01
    • 2013-03-01
    • 1970-01-01
    • 2012-04-28
    • 1970-01-01
    • 2015-07-25
    • 1970-01-01
    相关资源
    最近更新 更多