一、线程,是如此的不安全
首先我们先做一个小实验,运行以下程序
private static int count=0;
private final static int threadNum=100;
private final static int times=1000000;
private static void inc(){
count++;
}
public static void main(String[] args) {
List<Thread> ts=new ArrayList<Thread>();
for (int i = 0; i < threadNum; i++) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
for (int j = 0; j < times; j++) {
ThreadTest.inc();
}
}
});
ts.add(t);
}
long time=System.currentTimeMillis();
for (Thread t:ts) {
t.start();
}
while (true) {
int num = 0;
for (Thread t : ts) {
if(t.isAlive()) num++;
}
if(num==0) break;
System.out.println("live thread :" +num);
sleep(1000);
}
System.out.println("costTime:"+(System.currentTimeMillis()-time));
System.out.println("you want num is " +(times*threadNum));
System.out.println("the real num is " +(count));
}
private static void sleep(long ms){
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
运行结果如下
可以发现,每次结果都不一样。根据经验,我们遇到了线程安全的问题,小小的i++都不安全,还让不让活了。。。
二、i++的深刻检讨
显而易见,我们只有一个count共享变量,那么不安全的原因便是i++是不是原子操作的问题了。
i++有以下步骤:
1、内存到寄存器
2、寄存器自增
3、写回内存
任何一部都可能中断分离开,所以不是原子操作,从而发生了线程安全问题
三、解决办法之synchronized
我想多数人碰到这个问题就会想到这个词synchronized,那我们就试用一下
代码:
private static synchronized void inc(){
count++;
}
结果:
可以看出,确实解决了这个问题,但是也影响了程序的性能(可能因为我机器原因,单确实影响了性能,而且会随着锁内运行时间的增大或循环次数增加而递增)
四、解决办法之Volatile
用synchronized解决了这个问题,但是较大的降低了执行速度,那么我们能不能使用量级较轻的synchronized:Volatile呢?
锁的特征是互斥和可见,而Volatile满足了可见性