Java多线程编程核心技术(第3版)
上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所示。