кодесурса

Синхронизация кода Java

script1adsense2code
script1adsense3code

Вступление

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

В приведенном ниже примере у нас есть класс Account, который представляет банковский счет. Для краткости кода эта учетная запись начинается с баланса 50 и может использоваться только для снятия средств. Вывод средств будет принят, даже если на счете недостаточно средств для его покрытия. Аккаунт просто уменьшает остаток на сумму, которую вы хотите снять:

Java-код:

package synchronization;
public class Account {
	private int balance = 50;
	public int getBalance() {
	return balance;
	}
	public void withdraw(int amount) {
	balance = balance - amount;
	}
}

Представьте себе пару, Ранджита и Реему, которые оба имеют доступ к учетной записи и хотят снять деньги. Но они не хотят, чтобы аккаунт был отменен. Ниже класса AccountTesting.java запускаются два потока, и оба потока пытаются вывести деньги из одного и того же объекта счета в цикле. Вывод средств состоит из двух этапов:

1. Проверьте баланс.

2. Если на счету достаточно (снять10), сделайте вывод.

Java-код: перейти к редактору

public class AccountTesting implements Runnable {
	private Account acct = new Account();
	public static void main(String[] args) {
		AccountTesting r = new AccountTesting();
		Thread one = new Thread(r);
		Thread two = new Thread(r);
		one.setName("Ranjeet");
		two.setName("Reema");
		one.start();
		two.start();
	}
	@Override
	public void run() {
		for (int x = 0; x < 5; x++) {
			makeWithdrawal(10);
			if (acct.getBalance() < 0) {
				System.out.println("account is overdrawn!");
			}
		}
	}
	private void makeWithdrawal(int amt) {
		if (acct.getBalance() >= amt) {
			System.out.println(Thread.currentThread().getName() + " is going to withdraw");
			try {
				Thread.sleep(100);
			} catch (InterruptedException ex) {
			}
			acct.withdraw(amt);
			System.out.println(Thread.currentThread().getName() + " completes the withdrawal");
		} else {
			System.out.println("Not enough in account for " + Thread.currentThread().getName() + " to withdraw " + acct.getBalance());
		}
	}
}
class Account {
	private int balance = 50;
	public int getBalance() {
	return balance;
	}
	public void withdraw(int amount) {
	balance = balance - amount;
	}
}

Выход:


Хотя каждый раз, когда вы запускаете этот код, выходные данные могут немного отличаться, давайте рассмотрим этот конкретный пример, используя пронумерованные строки вывода. Для первых четырех попыток все в порядке. Эта проблема известна как «состояние гонки», когда несколько потоков могут обращаться к одному и тому же ресурсу (обычно к переменным экземпляра объекта) и могут выдавать поврежденные данные, если один поток «запускается» слишком быстро до завершения операции, которая должна быть «атомарной».

синхронизация

Синхронизация является решением этой проблемы. Специальное ключевое слово, синхронизированное, предотвращает возникновение условий гонки. Это ключевое слово помещает блокировку (монитор) на важный объект или фрагмент кода, чтобы гарантировать, что только один поток за раз будет иметь доступ.

Как вы защищаете данные? Вы должны сделать две вещи:

  • Отметьте переменные как приватные.
  • Синхронизируйте код, который изменяет переменные.

Мы можем решить все проблемы Ranjeet и Reema, добавив одно слово в код. Мы помечаем метод makeWithdrawal () синхронизированным следующим образом:

вот модифицированный код Java

public class SynchronizedAccountTesting implements Runnable {
	private Account acct = new Account();
	public static void main(String[] args) {
		SynchronizedAccountTesting r = new SynchronizedAccountTesting();
		Thread one = new Thread(r);
		Thread two = new Thread(r);
		one.setName("Ranjeet");
		two.setName("Reema");
		one.start();
		two.start();
	}
	@Override
	public void run() {
		for (int x = 0; x < 5; x++) {
			makeWithdrawal(10);
			if (acct.getBalance() < 0) {
				System.out.println("account is overdrawn!");
			}
		}
	}
	private synchronized void makeWithdrawal(int amt) {
		if (acct.getBalance() >= amt) {
			System.out.println(Thread.currentThread().getName() + " is going to withdraw");
			try {
				Thread.sleep(100);
			} catch (InterruptedException ex) {
			}
			acct.withdraw(amt);
			System.out.println(Thread.currentThread().getName() + " completes the withdrawal");
		} else {
			System.out.println("Not enough in account for " + Thread.currentThread().getName() + " to withdraw " + acct.getBalance());
		}
	}
}
class Account {
	private int balance = 50;
	public int getBalance() {
	return balance;
	}
	public void withdraw(int amount) {
	balance = balance - amount;
	}
}

Выход:


Блокирует весь методdrawCash (), поэтому никакие другие потоки не получат доступ к указанной части кода, пока текущий (блокирующий) поток не завершит выполнениеdrawCash (). Блокировки должны быть установлены в кратчайшие возможные сроки, чтобы избежать замедления программа: именно поэтому синхронизация коротких блоков кода предпочтительнее, чем синхронизация целых методов.

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

Резюме:

  • Синхронизированные методы предотвращают одновременный доступ более чем одного потока к коду критического метода объекта.
  • Вы можете использовать синхронизированное ключевое слово в качестве модификатора метода или для запуска синхронизированного блока кода.
  • Чтобы синхронизировать блок кода (другими словами, область действия меньше, чем весь метод), вы должны указать аргумент, который является объектом, блокировку которого вы хотите синхронизировать.
  • В то время как только один поток может получить доступ к синхронизированному коду конкретного экземпляра, несколько потоков могут получить доступ к несинхронизированному коду одного и того же объекта.

Редактор кода Java:

Предыдущая: Взаимодействие с Java-потоками
Далее: Util Package

Новый контент: Composer: менеджер зависимостей для PHP , R программирования


script1adsense4code
script1adsense5code
disqus2code
script1adsense6code
script1adsense7code
script1adsense8code
buysellads2code