线程同步关键字 synchronized
在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
从以上两种情况可以发现使用了同步代码块后两个不同锁的代码块可以异步执行