上QQ阅读APP看书,第一时间看更新
2.2.11 不同步导致的逻辑错误与解决方案
如果方法不被同步化,会导致的逻辑错误。
创建t9项目,创建一个只能放一个元素的自定义集合工具类MyOneList.java,代码如下:
package mylist; import java.util.ArrayList; import java.util.List; public class MyOneList { private List list = new ArrayList(); synchronized public void add(String data) { list.add(data); }; synchronized public int getSize() { return list.size(); }; }
创建业务类MyService.java,代码如下:
package service; import mylist.MyOneList; public class MyService { public MyOneList addServiceMethod(MyOneList list, String data) { try { if (list.getSize() < 1) { Thread.sleep(2000); // 模拟从远程花费2秒取回数据 list.add(data); } } catch (InterruptedException e) { e.printStackTrace(); } return list; } }
创建线程类MyThread1.java,代码如下:
package mythread; import mylist.MyOneList; import service.MyService; public class MyThread1 extends Thread { private MyOneList list; public MyThread1(MyOneList list) { super(); this.list = list; } @Override public void run() { MyService msRef = new MyService(); msRef.addServiceMethod(list, "A"); } }
创建线程类MyThread2.java,代码如下:
package mythread; import service.MyService; import mylist.MyOneList; public class MyThread2 extends Thread { private MyOneList list; public MyThread2(MyOneList list) { super(); this.list = list; } @Override public void run() { MyService msRef = new MyService(); msRef.addServiceMethod(list, "B"); } }
创建Run.java,代码如下:
package test; import mylist.MyOneList; import mythread.MyThread1; import mythread.MyThread2; public class Run { public static void main(String[] args) throws InterruptedException { MyOneList list = new MyOneList(); MyThread1 thread1 = new MyThread1(list); thread1.setName("A"); thread1.start(); MyThread2 thread2 = new MyThread2(list); thread2.setName("B"); thread2.start(); Thread.sleep(6000); System.out.println("listSize=" + list.getSize()); } }
程序运行结果如图2-35所示。
图2-35 无序性带来的错误结果
出现错误的原因是两个线程同时执行if判断语句时返回list的size()大小都是0,最后向list中添加了两个数据,解决办法就是对if语句进行同步化。
在此示例中的synchronized public int getSize()方法是同步的,但运行结果并不正确,所以看到synchronized时并不代表代码不出现意外,要把synchronized关键字放在正确的位置才能发挥它应有的作用,位置放错了,达不到应有的效果。
更改MyService.java类文件代码如下:
package service; import mylist.MyOneList; public class MyService { public MyOneList addServiceMethod(MyOneList list, String data) { try { synchronized (list) { if (list.getSize() < 1) { Thread.sleep(2000); list.add(data); } } } catch (InterruptedException e) { e.printStackTrace(); } return list; } }
图2-36 正确的运行结果
由于list参数对象在项目中是一份实例,是单例的,而且需要对list参数的getSize()方法做同步的调用,所以使用list参数作为锁。
程序运行结果如图2-36所示。