Почему синхронизированный метод не блокируется, если я не использую .join ()? - PullRequest
0 голосов
/ 18 июня 2019

В Java маркировка метода synchronized должна отключить условие гонки , которое может возникнуть из-за того, что два потока вызывают метод, который обращается к полю и изменяет его в одном и том же объекте.

Но по какой-то причине synchronized не работает должным образом в следующем примере, если я не вызову .join() в обоих потоках основной программы. Почему это так?

package example;

public class Account {
    private double balance;

    public Account(double balance) {
        super();
        this.balance = balance;
    }

    public synchronized void deposit(double amount) {
        balance += amount;
    }

    public double getBalance() {
        return balance;
    }
}
package example;

public class AccountTester extends Thread {
    private Account account;
    private double amount;

    public AccountTester(Account account, double amount) {
        this.account = account;
        this.amount = amount;
    }

    public static void main(String[] args) {
        Account account = new Account(0);
        AccountTester tester1 = new AccountTester(account, 1.0);
        AccountTester tester2 = new AccountTester(account, 2.0);
        tester1.start();
        tester2.start();
        // Why do I need the main thread to join threads
        //      tester1 and tester2 for synchronized to work?
        try {
            tester1.join();
            tester2.join();
        } catch (InterruptedException e) {
            System.err.println(e);
        }
        System.out.println("End balance: " + account.getBalance());
    }

    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            account.deposit(amount);
        }
    }
}

1 Ответ

0 голосов
/ 18 июня 2019

Как пояснил @flakes, код нуждался в основном потоке для join() обоих потоков, чтобы гарантировать завершение обоих потоков, т. Е. Сделать с их изменением баланса, прежде чем распечатать конец balance.

Более понятный способ реализовать это - использовать интерфейс java.util.concurrent.ExecutorService. Здесь его метод shutdown() гарантирует, что оба потока в пуле потоков будут завершены до вывода конца balance. Вот моя реализация:

package example;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class AccountTester implements Runnable {
    private Account account;
    private double amount;

    public AccountTester(Account account, double amount) {
        this.account = account;
        this.amount = amount;
    }

    public static void main(String[] args) {
        Account account = new Account(0);
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        executorService.execute(new AccountTester(account, 1.0));
        executorService.execute(new AccountTester(account, 2.0));
        executorService.shutdown();
        System.out.println("End balance: " + account.getBalance());
    }

    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            account.deposit(amount);
        }
    }
}
...