简体   繁体   English

方法何时应声明为同步?

[英]When should a method be declared synchronized?

Following is a, simple, working program which uses two threads to print a counter: 以下是一个简单的工作程序,该程序使用两个线程来打印计数器:

public class SynchronizedCounter implements Runnable {

    private static int i = 0;

    public void increment() { i++; }
    public int getValue() { return i; }  

    @Override
    public void run() {
        for( int i = 0; i < 5; i++ ) {
            synchronized( this ) {
                increment();
                System.out.println( Thread.currentThread().getName() + " counter: " + this.getValue() );
            }
        }
    }

    public static void main( String[] args ) {

        ExecutorService executorService = Executors.newFixedThreadPool( 2 );
        SynchronizedCounter synchronizedCounter = new SynchronizedCounter();
        executorService.submit( synchronizedCounter );
        executorService.submit( synchronizedCounter );  
        executorService.shutdown();

    }

}

The output is as expected - the two threads display counter in order. 输出是预期的-两个线程按顺序显示计数器。

If increment() and getValue() are declared as synchronized and synchronized block code is commented, the output manifests visibility and race problems. 如果将increment()getValue()声明为已synchronized并且已注释synchronized块代码,则输出将显示可见性和竞争问题。

As increment() and getValue() are not declared as synchronized and by, just, using a synchronized block(passing a monitor object) if synchronization could be achieved, in what circumstances should they be declared as synchronized ? 由于未实现increment()getValue()的声明为已synchronized并且仅通过使用synchronized块(通过监视对象)才能实现同步,因此在什么情况下应将它们声明为已synchronized

Declare a method synchronized when you need the semantics of a synchronized block around the body of the method. 当需要在方法主体周围使用同步块的语义时,声明一个方法已同步。

synchronized void increment() {
  ...
}

is exactly the same as: 与以下内容完全相同:

void increment() {
  synchronized (this) { ... }
}

The thing about doing that in the code above is that you no longer execute the increment() and getValue() methods atomically: another thread can nip in and execute the methods in between the calls of your thread: 在上面的代码中这样做的事情是,您不再原子地执行increment()getValue()方法:另一个线程可以插入并在线程的调用之间执行这些方法:

Thread 1             Thread 2

increment()
                     increment()
                     getValue()
getValue()

This is possible, but two threads can't execute increment() (or getValue() ) at the same time, because they are synchronized (*). 这是可能的,但是两个线程不能同时执行increment() (或getValue() ),因为它们是同步的(*)。

On the other hand, synchronizing around the calls as in the code in the question means that the two calls are executed atomically, so the two threads can't interleave: 另一方面,像问题代码中那样围绕调用进行同步意味着这两个调用是原子执行的,因此这两个线程不能交织:

Thread 1             Thread 2

increment()
getValue()
                     increment()
                     getValue()

(*) Actually, they can both be executing the method at the same time. (*)实际上,他们都可以同时执行该方法。 It's just that all but one of the threads will be waiting at the synchronized (this) . 只是,除了一个线程外,所有线程都将在synchronized (this)等待。

In your code, the class itself is not thread safe and it is up to the client (your run method) to ensure thread safety. 在您的代码中,类本身不是线程安全的,它取决于客户端(您的run方法)以确保线程安全。

If you want to make the class thread safe such that clients don't need to do anything particular to use it, it should have proper synchronization mechanisms and propose "higher level" method that can run atomic operation on a combination of operations if necessary. 如果要使类线程安全,以使客户端不需要执行任何特定操作即可使用该类线程,则它应具有适当的同步机制,并提出“更高级别”的方法,必要时可以在组合操作上运行原子操作。

Think of AtomicInteger , for example, which has an incrementAndGet() method that would provide a superior approach. 例如,考虑一下AtomicInteger ,它具有一个incrementAndGet()方法,该方法将提供更好的方法。 Your run method would then become: 您的run方法将变为:

for( int i = 0; i < 5; i++ ) {
  int newValue = counter.incrementAndGet();
  System.out.println(Thread.currentThread().getName() + " counter: " + newValue );
}

Your increment() method uses i++ which is in fact substituted by 3 instructions. 您的increment()方法使用的是i++ ,实际上它已被3条指令代替。 If that method is not declared as synchronized then several threads may execute it at the same time which will lead to race conditions and incorrect results. 如果该方法未声明为已synchronized则多个线程可能会同时执行该方法,这将导致争用条件和错误结果。

If increment() and getValue() are declared as synchronized and synchronized block code is commented, the output manifests visibility and race problems. 如果将gain()和getValue()声明为已同步,并且已注释同步块代码,则输出将显示可见性和竞争问题。

There are no race conditions in this case. 在这种情况下,没有比赛条件。 You see different results because now increment() and getValue() are not executed atomically (by atomically I mean two methods as one synch block) and while one thread called increment() and is about to call getValue() another thread also called increment() . 您会看到不同的结果,因为现在不是原子地执行crement increment()getValue() (就原子而言,我是指两个方法作为一个同步块),而一个线程称为increment()并即将调用getValue()另一个线程也称为increment()

So you need to make increment() and getValue() synchronized if you are planning to call them outside of synchronized block in your run method (and because they are declared as public there is a possibility to make such calls). 因此,如果您打算在run方法的synchronized块之外调用它们,则必须使increment()getValue()同步(并且由于它们被声明为public ,因此有可能进行此类调用)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM