在java的多線程編程中多個線程操作同一數據時會出現數據錯誤,主要原因是多個線程同時處理數據時同時獲取了數據,但是有些線程沒有來得及操作數據,然后另一個線程獲取到了之前的值,然后引起數據異常
具體代碼如下:
public class SynchronizedMethod { public static void main(String[] args) { MoneyMethod moneyMethod = new MoneyMethod(); for (int i = 0; i < 5; i++) { // 使用lamdba表達式實現runable接口 Thread t1 = new Thread(()-> {moneyMethod.addMoney();}, "t1"+i); t1.start(); } for (int i = 0; i < 5; i++) { Thread t2 = new Thread(new MyThread(moneyMethod), "t2"+i); t2.start(); } } } class MyThread implements Runnable { MoneyMethod moneyMethod ; /** * */ public MyThread(MoneyMethod moneyMethod) { // TODO Auto-generated constructor stub this.moneyMethod = moneyMethod; } public void run() { moneyMethod.subMoney(); } } class MoneyMethod { int money =20; public void addMoney() { getMoney(); money++; } public void subMoney() { getMoney(); money--; } public void getMoney() { System.out.println(Thread.currentThread().getName()+":::getMoney:::"+money); } }
結果值如下:
t10:::getMoney:::20 t11:::getMoney:::21 t13:::getMoney:::21 t14:::getMoney:::22 t12:::getMoney:::24 t23:::getMoney:::24 t22:::getMoney:::24 t24:::getMoney:::23 t21:::getMoney:::22 t20:::getMoney:::21
為了避免這種錯誤,在java中使用關鍵字 synchronized 來處理操作
包括兩種操作:在方法前修飾、在代碼塊前修飾
方法前修飾如下:
public class SynchronizedMethod { public static void main(String[] args) { MoneyMethod moneyMethod = new MoneyMethod(); for (int i = 0; i < 5; i++) { // 使用lamdba表達式實現runable接口 Thread t1 = new Thread(()-> {moneyMethod.addMoney();}, "t1"+i); t1.start(); } for (int i = 0; i < 5; i++) { Thread t2 = new Thread(new MyThread(moneyMethod), "t2"+i); t2.start(); } } } class MyThread implements Runnable { MoneyMethod moneyMethod ; /** * */ public MyThread(MoneyMethod moneyMethod) { // TODO Auto-generated constructor stub this.moneyMethod = moneyMethod; } public void run() { moneyMethod.subMoney(); } } class MoneyMethod { int money =20; public synchronized void addMoney() { getMoney(); money++; } public synchronized void subMoney() { getMoney(); money--; } public synchronized void getMoney() { System.out.println(Thread.currentThread().getName()+":::getMoney:::"+money); } }
結果如下:
t10:::getMoney:::20 t13:::getMoney:::21 t12:::getMoney:::22 t11:::getMoney:::23 t14:::getMoney:::24 t20:::getMoney:::25 t21:::getMoney:::24 t22:::getMoney:::23 t23:::getMoney:::22 t24:::getMoney:::21
請注意,構造函數無法同步 - 使用synchronized
帶有構造函數的關鍵字是語法錯誤,而且各個實例對象的同步方法之間不會相互影響,在靜態方法上添加synchronized會影響所有的實例對象
但是在同步方法中有缺陷,就是如果對象的所有方法都是同步方法,則鎖都是當前對象,所有如果不是操作同一數據,會影響效率。在java中可以使用同步代碼塊來處理這樣的問題
同步方法的代碼如下:
public class SynchronizedCodeBlock { public static void main(String[] args) { MoneyMethod2 moneyMethod = new MoneyMethod2(); for (int i = 0; i < 5; i++) { // 使用lamdba表達式實現runable接口 Thread t1 = new Thread(() -> { moneyMethod.addMoney(); }, "t1" + i); t1.start(); } for (int i = 0; i < 5; i++) { Thread t2 = new Thread(new MyThread2(moneyMethod), "t2" + i); t2.start(); } for (int i = 0; i < 5; i++) { // 使用lamdba表達式實現runable接口 Thread t1 = new Thread(() -> { moneyMethod.addMoney2(); }, "t3" + i); t1.start(); } for (int i = 0; i < 5; i++) { Thread t2 = new Thread(new MyThread3(moneyMethod), "t4" + i); t2.start(); } } } class MyThread2 implements Runnable { MoneyMethod2 moneyMethod; /** * */ public MyThread2(MoneyMethod2 moneyMethod) { // TODO Auto-generated constructor stub this.moneyMethod = moneyMethod; } public void run() { moneyMethod.subMoney(); } } class MyThread3 implements Runnable { MoneyMethod2 moneyMethod; /** * */ public MyThread3(MoneyMethod2 moneyMethod) { // TODO Auto-generated constructor stub this.moneyMethod = moneyMethod; } public void run() { moneyMethod.subMoney2(); } } class MoneyMethod2 { int money = 100; int money2 = 600; Object lock1 = new Object(); Object lock2 = new Object(); public synchronized void addMoney() { // (lock1) { try { Thread.sleep(4000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } getMoney(); money++; // } } public synchronized void subMoney() { // synchronized (lock1) { try { Thread.sleep(4000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } getMoney(); money--; // } } public synchronized void addMoney2() { // synchronized (lock2) { try { Thread.sleep(4000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } getMoney2(); money2++; // } } public synchronized void subMoney2() { // synchronized (lock2) { try { Thread.sleep(4000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } getMoney2(); money2--; // } } public void getMoney() { System.out.println(Thread.currentThread().getName() + ":::getMoney:::" + money+":::"+new Date().getTime()); } public void getMoney2() { System.out.println(Thread.currentThread().getName() + ":::getMoney2:::" + money2+":::"+new Date().getTime()); } }
執行結果:
t10:::getMoney:::100:::1540435235076 t44:::getMoney2:::600:::1540435239076 t43:::getMoney2:::599:::1540435243076 t42:::getMoney2:::598:::1540435247076 t41:::getMoney2:::597:::1540435251076 t40:::getMoney2:::596:::1540435255077 t34:::getMoney2:::595:::1540435259077 t33:::getMoney2:::596:::1540435263077 t32:::getMoney2:::597:::1540435267078 t31:::getMoney2:::598:::1540435271078 t30:::getMoney2:::599:::1540435275079 t24:::getMoney:::101:::1540435279080 t23:::getMoney:::100:::1540435283080 t22:::getMoney:::99:::1540435287080 t21:::getMoney:::98:::1540435291080 t20:::getMoney:::97:::1540435295080 t13:::getMoney:::96:::1540435299080 t14:::getMoney:::97:::1540435303080 t11:::getMoney:::98:::1540435307081 t12:::getMoney:::99:::1540435311082
使用了同步代碼塊的代碼:
public class SynchronizedCodeBlock { public static void main(String[] args) { MoneyMethod2 moneyMethod = new MoneyMethod2(); for (int i = 0; i < 5; i++) { // 使用lamdba表達式實現runable接口 Thread t1 = new Thread(() -> { moneyMethod.addMoney(); }, "t1" + i); t1.start(); } for (int i = 0; i < 5; i++) { Thread t2 = new Thread(new MyThread2(moneyMethod), "t2" + i); t2.start(); } for (int i = 0; i < 5; i++) { // 使用lamdba表達式實現runable接口 Thread t1 = new Thread(() -> { moneyMethod.addMoney2(); }, "t3" + i); t1.start(); } for (int i = 0; i < 5; i++) { Thread t2 = new Thread(new MyThread3(moneyMethod), "t4" + i); t2.start(); } } } class MyThread2 implements Runnable { MoneyMethod2 moneyMethod; /** * */ public MyThread2(MoneyMethod2 moneyMethod) { // TODO Auto-generated constructor stub this.moneyMethod = moneyMethod; } public void run() { moneyMethod.subMoney(); } } class MyThread3 implements Runnable { MoneyMethod2 moneyMethod; /** * */ public MyThread3(MoneyMethod2 moneyMethod) { // TODO Auto-generated constructor stub this.moneyMethod = moneyMethod; } public void run() { moneyMethod.subMoney2(); } } class MoneyMethod2 { int money = 100; int money2 = 600; Object lock1 = new Object(); Object lock2 = new Object(); public void addMoney() { synchronized(lock1) { try { Thread.sleep(4000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } getMoney(); money++; } } public void subMoney() { synchronized (lock1) { try { Thread.sleep(4000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } getMoney(); money--; } } public void addMoney2() { synchronized (lock2) { try { Thread.sleep(4000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } getMoney2(); money2++; } } public void subMoney2() { synchronized (lock2) { try { Thread.sleep(4000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } getMoney2(); money2--; } } public void getMoney() { System.out.println(Thread.currentThread().getName() + ":::getMoney:::" + money+":::"+new Date().getTime()); } public void getMoney2() { System.out.println(Thread.currentThread().getName() + ":::getMoney2:::" + money2+":::"+new Date().getTime()); } }
結果:
t10:::getMoney:::100:::1540435584843 t30:::getMoney2:::600:::1540435584844 t23:::getMoney:::101:::1540435588844 t44:::getMoney2:::601:::1540435588845 t24:::getMoney:::100:::1540435592844 t43:::getMoney2:::600:::1540435592845 t22:::getMoney:::99:::1540435596844 t42:::getMoney2:::599:::1540435596846 t20:::getMoney:::98:::1540435600845 t41:::getMoney2:::598:::1540435600847 t21:::getMoney:::97:::1540435604845 t40:::getMoney2:::597:::1540435604847 t14:::getMoney:::96:::1540435608846 t34:::getMoney2:::596:::1540435608848 t13:::getMoney:::97:::1540435612846 t33:::getMoney2:::597:::1540435612848 t11:::getMoney:::98:::1540435616847 t31:::getMoney2:::598:::1540435616849 t12:::getMoney:::99:::1540435620847 t32:::getMoney2:::599:::1540435620849
從以上兩種情況可以發現使用了同步代碼塊后兩個不同鎖的代碼塊可以異步執行