使用 System.out.println(Thread.currentThread().getName() + " " +count);导致同步

Using System.out.println(Thread.currentThread().getName() + " " +count); leads to synchronization

我在 java 学习多线程,在教程中,它说删除 synchronized 应该会使程序出现错误并且它确实如此,所以我只是在试验并写了一个打印行 System.out.println(Thread.currentThread().getName() + " " +count); 并删除了同步词,即使这样程序也能正常工作。但是,如果仅删除同步字并且未添加打印行(System.out.println(Thread.currentThread().getName() + " " +count);),则程序存在错误广告。

我无法理解添加打印行如何使其同步。

public class intro implements Runnable {

    int n=10000; 
    private int count = 0; 

    public int getCount() { return count; }

    public synchronized void incrSync() { count++;  }

    public void run () {
        for (int i=0; i<n; i++) {
            incrSync();
            //System.out.println(Thread.currentThread().getName() + "  " +count);       
        } 
        } 


    public static void main(String [] args) {

        intro mtc = new intro();
        Thread t1 = new Thread(mtc);
        Thread t2 = new Thread(mtc);
        t1.start();
        t2.start();
        try {
            t1.join();

            t2.join();

        } catch (InterruptedException ie) {
        System.out.println(ie);
        ie.printStackTrace();
    }
    System.out.println("count = "+ mtc.getCount());
}
}

当多个线程试图同时访问同一字段时,线程之间会发生同步问题

如果不进行打印,run 方法将处于一个几乎连续访问计数器的紧密循环中。让多个线程在没有同步的情况下做那个,很容易出错。

通过添加打印,您正在改变循环,使其大部分(几乎所有)时间都用于打印,只是偶尔增加计数。这不太可能引起争用。

代码在打印方面仍然存在问题,唯一的区别是争用发生的频率要低得多,而且您仅对 1000 循环进行的测试不足以证明该问题。您可能必须 运行 几年后线程才会发生冲突。

这是一个经典的演示,说明为什么线程问题如此难以查找和修复。该循环(带有它的 print 语句)可以 运行 在多个线程上运行多年而不会发生争用,但如果线程之间只发生一次冲突,那么代码就会中断。想象一下,这发生在心脏起搏器、卫星或核电站中!

println 方法调用 newline,这是一个带有 synchronized 块的方法。它给出了正确的结果,尽管它不是线程安全的。

考虑 T1 读取计数 5 和 T2 读取计数 5 同时发生,然后发生竞争条件。它给出了正确的结果,因为您使用的是阻塞的 System.out.println(Thread.currentThread().getName() + " " +count); 。增加线程数。

 private void newLine() {
    try {
        synchronized (this) {
            ensureOpen();
            textOut.newLine();
            textOut.flushBuffer();
            charOut.flushBuffer();
            if (autoFlush)
                out.flush();
        }
    }
    catch (InterruptedIOException x) {
        Thread.currentThread().interrupt();
    }
    catch (IOException x) {
        trouble = true;
    }
}