В Java ключевое слово синхронизированное можно использовать для управления синхронизацией потоков для достижения последовательного доступа к ключевым ресурсам и избежать несоответствия данных, вызванных многопоточным одновременным выполнением. Принцип синхронизации - это монитор объекта (блокировка). Только поток, который приобретает монитор, может продолжать выполнять, в противном случае поток будет ждать, чтобы приобрести монитор. Каждый объект или класс в Java связан с замок, связанный с ним. Для объекта он контролирует переменную экземпляра этого объекта. Для класса он контролирует переменную класса (сам класс является объектом класса класса, поэтому блокировка, связанная с классом, также является блокировкой объекта). Существует два способа использования синхронизированных ключевых слов: синхронизированный метод и синхронизированный блок. Обе области мониторинга связаны с введенным объектом. Когда он достигнет этой области мониторинга, JVM будет блокировать эталонный объект, и когда он уйдет, замок на эталонном объекте будет выпущена (JVM отпустит блокировку при выходе из исключений). Блокировки объектов являются внутренними механизмами JVM. Вам нужно только написать методы синхронизации или блоки синхронизации. При использовании областей мониторинга JVM автоматически приобретет или отпустит блокировку.
Пример 1
Давайте сначала посмотрим на первый пример. В Java существует только одна критическая область того же объекта, к которой можно получить доступ одновременно (все нестатические синхронизированные методы):
Пакет параллелизм; открытый класс main8 {public static void main (string [] args) {account account = new account (); Account.SetBalance (1000); Компания компании = новая компания (счет); Thread CompanyThread = новая поток (компания); Bank Bank = новый банк (счет); Thread Bankthread = New Thread (Bank); System.out.printf («Учетная запись: начальный баланс: %f/n», account.getBalance ()); CompanyThread.Start (); bankthread.start (); try {// join () Метод ждет, пока эти два потока завершат companyThread.join (); bankthread.join (); System.out.printf ("Учетная запись: Окончательный баланс: %f/n", account.getBalance ()); } catch (прерванное искусство e) {e.printstacktrace (); }}} /*Учетная запись*/учетная запись класса {private Double Balance; /*Добавить входящие данные в баланс баланса*/ public void addamount (двойная сумма) {double tmp = balance; try {thread.sleep (10); } catch (прерванное искусство e) {e.printstacktrace (); } tmp += сумма; баланс = tmp; } /*Вычесть входящие данные из баланса баланса* / public void void subtractAmount (двойная сумма) {double tmp = balance; try {thread.sleep (10); } catch (прерванное искусство e) {e.printstacktrace (); } tmp -= сумма; баланс = tmp; } public double getBalance () {return Balance; } public void setBalance (двойной баланс) {this.balance = balance; }} /*Bank*/Class Bank реализует Runnable {Private Account Scount; Общественный банк (счет счета) {this.account = account; } @Override public void run () {for (int i = 0; i <100; i ++) {account.subtractamount (1000); }}} /*Компания*/Class Company реализует Runnable {частная учетная запись; Общественная компания (учетная запись) {this.account = account; } @Override public void run () {for (int i = 0; i <100; i ++) {account.addamount (1000); }}}Вы разработали заявку на моделирование для банковских счетов, которые могут пополнять и вычесть остатки. Эта программа перезаряжает учетную запись, вызывая метод addamount () 100 раз, внося 1000 каждый раз; Затем вычитает баланс счета, вызывая метод suptractamount () 100 раз, вычитая 1000 каждый раз; Мы ожидаем, что окончательный баланс счета будет равен первоначальному балансу, и мы реализуем его через синхронизированное ключевое слово.
Если вы хотите просмотреть проблему параллельного доступа общих данных, вам нужно только удалить синхронизированные ключевые слова в объявлениях метода addamount () и suptractamount (). Без синхронизированного ключевого слова значение печатного баланса не является согласованным. Если вы запустите эту программу несколько раз, вы получите разные результаты. Поскольку JVM не гарантирует порядок выполнения потоков, каждый раз, когда он работает, потоки будут читать и изменять баланс учетной записи в разных заказах, что приводит к различным конечным результатам.
Метод объекта объявляется с использованием синхронизированного ключевого слова и может быть доступен только одним потоком. Если поток A выполняет метод синхронизации Syncmethoda (), поток B хочет выполнить другие методы синхронизации Syncmethodb () этого объекта, поток B будет заблокирован до тех пор, пока поток A не завершит доступ. Но если поток B обращается к разным объектам одного и того же класса, ни один поток не будет заблокирован.
Пример 2
Продемонстрируйте проблему, что статические синхронизированные методы и нестатические синхронизированные методы на одном и том же объекте могут быть доступны с помощью нескольких потоков одновременно. Проверьте это.
Пакет параллелизм; открытый класс main8 {public static void main (string [] args) {account account = new account (); Account.SetBalance (1000); Компания компании = новая компания (счет); Thread CompanyThread = новая поток (компания); Bank Bank = новый банк (счет); Thread Bankthread = New Thread (Bank); System.out.printf («Учетная запись: начальный баланс: %f/n», account.getBalance ()); CompanyThread.Start (); bankthread.start (); try {// join () Метод ждет, пока эти два потока завершат companyThread.join (); bankthread.join (); System.out.printf ("Учетная запись: Окончательный баланс: %f/n", account.getBalance ()); } catch (прерванное искусство e) {e.printstacktrace (); }}} /*Учетная запись*/учетная запись класса {/*изменить ее на статическую переменную здесь*/private Static Double Balance = 0; /*Добавить входящие данные в баланс баланса, обратите внимание, что они изменяются со статическим*/ общедоступным статическим синхронизированным void addamount (двойная сумма) {double tmp = balance; try {thread.sleep (10); } catch (прерванное искусство e) {e.printstacktrace (); } tmp += сумма; баланс = tmp; } /*Вычтите входящие данные из баланса баланса* / public void void subtractamount (двойная сумма) {double tmp = balance; try {thread.sleep (10); } catch (прерванное искусство e) {e.printstacktrace (); } tmp -= сумма; баланс = tmp; } public double getBalance () {return Balance; } public void setBalance (двойной баланс) {this.balance = balance; }} /*Bank*/Class Bank реализует Runnable {Private Account Scount; Общественный банк (счет счета) {this.account = account; } @Override public void run () {for (int i = 0; i <100; i ++) {account.subtractamount (1000); }}} /*Компания*/Class Company реализует Runnable {частная учетная запись; Общественная компания (учетная запись) {this.account = account; } @Override public void run () {for (int i = 0; i <100; i ++) {account.addamount (1000); }}}Я только что добавил статическое ключевое слово для изменения баланса в предыдущем примере, и метод addamount () также может изменить статическое ключевое слово. Вы можете проверить результаты выполнения самостоятельно, и каждое выполнение будет иметь другой результат!
Некоторое резюме: