在访问自己的字段时,对象是否需要使用synchronized方法?

时间:2018-03-28 20:51:33

标签: java multithreading

我在这里阅读Java教程中的同步方法/语句:https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html

这是有道理的,但是一旦我开始编码,我就陷入了两难境地:我如何处理对象访问自己的字段?

考虑这个例子:

public class MyClass extends Thread {

    private PrintWriter printWriter;

    public synchronized void setPrintWriter(PrintWriter pw){
        printWriter = pw;
    }

    public synchronized PrintWriter getPrintWriter(){
        return printWriter;
    }

    @Override
    public void run() {
        PrintWriter pw = getPrintWriter();
        while (!this.isInterrupted()) {

            try {
                pw.println("some stuff");
                pw.flush();
                Thread.sleep(1000);
            }
            catch (InterruptedException e){
                //pass the interrupt along (will terminate the loop)
                Thread.currentThread().interrupt();
            }
        }
        //finish by printing this when we've received an interrupt
        pw.println("some more stuff");
    }
}

这会有效,但是当我可以直接使用pw时,使用getter获取副本printWriter似乎有点荒谬。但我不确定在这种情况下是否有必要,或者在其他情况下是否与此相似。也许它可以保护我免受腐败,如果其他一些线程在我使用printWriter的同时调用了setPrintWriter?

1 个答案:

答案 0 :(得分:1)

问题不在于“对象访问自己的字段”。调用方法的任何其他类也(间接)访问字段。

问题是,“如果很多线程访问该类的给定实例,那么它是否安全?”

在您的班级中,如果主题A和B使用您班级的给定实例,则他们都可以访问printWriter字段。如果在没有正确同步的情况下完成此操作,可能会发生错误(值缓存,数据损坏等)

确保正确的线程安全的最简单方法是遵循以下配方:

  • 列出可以从外部访问的所有字段,例如通过调用方法
  • 对于每个字段或相关字段组(如“必须同时修改的字段”,例如age和birthDate),定义保护策略:监视器(通过synchronized关键字),锁定(在java.util.concurrent.locks),原子类(AtomicInteger ...)等。你有很多选择。
  • 对于指向步骤1中列出的变量的每条路径,请确保应用同步策略。通过“每条路径”,我的意思是您必须在仅以只读模式访问这些变量的路径上应用同步。这对于避免读取陈旧值很重要(想想:svn / git中的“update / pull”)。

在这方面,您在此处正确实现了同步:所有printWriter路径均由synchronized覆盖(在写入读取模式下),因此您需要好。