在实际业务常见中,很多的时候我们都需要访问一些共同的资源,比如一个序列号,比如某一个文件。如果多个线程一起访问这个序列或者文件,而我们没有做足够多的处理,就很容易造成脏数据或者数据丢失等各种问题。
这种场景特别常见,写一个简单的例子。以免自己的团队在实际开发中,犯这种小错误。
当然,这种错误知道了,要预先处理还是相当简单;但是如果要真的出现了错误,在一大堆代码中找这个坑,那是相当的要命。
我们的业务需要获取一个序列号。下面是一个取得序列号的单例模式的例子,但调用get()时,可能会产生两个相同的序列号:
当代码(1)和(2)都试图调用get()取得一个唯一的序列。当代码(1)执行完代码(a),正要执行代码(b)时,它被中断了并开始执行代码(2)。一旦当代码(2)执行完(a)而代码(1)还未执行代码(b),那么代码(1)和代码(2)就将得到相同的值。
package com.thread;
/**
* 简单用多线程获取序列号,理论上能出现序列号重复,但是要观察到,可能需要多次试验和仔细观察
* 小技巧:我们这里有30次循环,如果打印出的结果中,没有30,那么就可能出现了重复
* @author 范芳铭
*/
public class EasyGetSeq extends Thread {
private static int number = 0;
private static EasyGetSeq seq = new EasyGetSeq();
public EasyGetSeq(String name){
super(name);
}
private EasyGetSeq() {
}
public EasyGetSeq getInstance() {
return seq;
}
public int get() {
number++; // (a)
return number;// (b)
}
public void run(){
for (int i = 0 ; i < 10; i ++){
int a = getInstance().get();// (1)
System.out.println(Thread.currentThread().getName() + ":" + a);
//模拟访问停顿
try {
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
//简单模拟多线程调用
EasyGetSeq sepA = new EasyGetSeq("A");
sepA.start();
EasyGetSeq sepB = new EasyGetSeq("---B");
sepB.start();
EasyGetSeq sepC = new EasyGetSeq("======C");
sepC.start();
}
}
运行结果
A:1
======C:3
—B:2
—B:5
======C:4
A:4
======C:7
—B:6
A:6
—B:9
A:8
======C:10
======C:12
—B:13
A:11
======C:14
A:16
—B:15
—B:18
======C:19
A:17
======C:20
—B:22
A:21
—B:23
======C:25
A:24
—B:26
A:27
======C:26
观察的小技巧,因为我们运行的次数是 3*10,因此最终的数据应该是30,但是这里没有出现30和29,那么肯定有一些数字发生了重复,比如
—B:26
A:27
======C:26
原文地址:http://blog.csdn.net/ffm83/article/details/44782205