多线程下的并发安全问题
如果有一个静态变量 static int count = 0;
这时候两个线程同时循环 +10000;
也就是for循环10000次 {
i++;
}
那么结果是什么呢?? 我们期望得到到
20000
但是你会发现 每次得到的是一个随机数
12314 16232 17652
为什么会这样?
因为 i++ 这个操作不是原子性操作。
它可能会出现两个线程同时操作了
i++
等于它只加了一次。
所以这时候 我们用 synchronized 加锁 让这个操作具备原子性
public class SyncTestThread extends Thread{
public static int count = 0;
static SyncTestThread instance = new SyncTestThread();
@Override
public void run(){
for (int i = 0; i < 10000; i++) {
synchronized (instance) {
count++;
}
}
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
SyncTestThread syncTestThread1 = new SyncTestThread();
SyncTestThread syncTestThread2 = new SyncTestThread();
syncTestThread1.start();
syncTestThread2.start();
// 这里记得加上join方法 不然线程还没执行完 主线程就把count打出来了
syncTestThread1.join();
syncTestThread2.join();
System.out.println("the back is "+SyncTestThread.count);
}
}
这种情况下就是线程安全的。
可以看到我们创建了一个静态对象实例,用它来当锁。
你也可以 synchronized 修饰方法 比如搞一个add()方法 把i++封装。
再用synchronized 修饰。
错误的加锁案例
public class SyncTestThread extends Thread{
public static Integer count = 0;
static SyncTestThread instance = new SyncTestThread();
@Override
public void run(){
for (int i = 0; i < 10000; i++) {
synchronized (count) {
count++;
}
}
}
}
这里就会有问题 你虽然给count加锁
但是这个count是integer类型 他是一个对象 而不是一个基础类型
每次+的时候 都会创建一个新对象 那么锁就失效了
联想到单例模式
还记得经典的单例模式是怎么写的吗?
public class Singleton2 {
//构造方法设为私有
private Singleton2(){}
//创建一个本类的对象
private static Singleton2 singletonInstance;
//提供一个外界可以访问的方式
public static synchronized Singleton2 getInstance(){
if (singletonInstance==null) {
singletonInstance = new Singleton2();
}
return singletonInstance;
}
}
这里直接对静态方法加锁
相当于对当前类加锁
synchronized(Singleton2.class){
...............
}