synchronized与单例的线程安全问题
如果对synchronized
了解不够深入,建议先理解透彻下面这张图
遇到的问题
单例中多个方法操作同一变量,出现数据混乱/线程不安全问题。
原先的代码是synchronized加错了地方,或者理解不透测。
synchronized
线程同步问题大都使用synchronized解决,有同步代码块和同步方法的两种方式。
代码示例
错误示例一
Single.java
public class Single {
private final String TAG = "Single";
private static Single instance = null;
private int num = 0;
public static synchronized Single getInstance() {
synchronized (Single.class) {
if (instance == null) {
instance = new Single();
}
}
return instance;
}
public void addNum() {
num++;
Log.e(TAG, "num:" + num);
}
public void reduceNum() {
num--;
Log.e(TAG, "num:" + num);
}
}
复制代码
MainActivity.java
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void ClickButton(View view) {
for (int i = 0; i < 1000; i++) {
ThreadPool.getInstance().execute(new Runnable() {
@Override
public void run() {
Single.getInstance().addNum();
Single.getInstance().reduceNum();
}
});
}
}
}
复制代码
结果:打印num,发现出现很多次num:2的情况。线程不安全。
错误示例二
在addNum
与reduceNum
方法前加上synchronized
结果:打印num,发现还是会出现num:2的情况。线程不安全。
问题原因及解决
造成上面问题的原因是:即使有线程安全的单例,也仅仅只能保证单例有一个,并不能保证单例中的方法不会出现线程安全问题。因为当多个线程获取到该单例对象后,其中的方法就有可能不同步。
错误示例二中的方法仅仅只对方法做了同步,并不同保证加减统一。
解决办法有两种:
第一种,在线程中加上同步代码块。
第二种,在单例代码中增加一个同步方法,将加减操作放在这个同步方法中。
正确示例一
添加同步代码块
Single.java
public class Single {
private final String TAG = "Single";
private static Single instance = null;
private int num = 0;
public static synchronized Single getInstance() {
synchronized (Single.class) {
if (instance == null) {
instance = new Single();
}
}
return instance;
}
public void addNum() {
num++;
Log.e(TAG, "num:" + num);
}
public void reduceNum() {
num--;
Log.e(TAG, "num:" + num);
}
}
复制代码
MainActivity.java
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void ClickButton(View view) {
for (int i = 0; i < 1000; i++) {
ThreadPool.getInstance().execute(new Runnable() {
@Override
public void run() {
synchronized ("") { //添加同步代码块
Single.getInstance().addNum();
Single.getInstance().reduceNum();
}
}
});
}
}
}
复制代码
正确示例二
增加新的同步方法,将加减方法放在该方法中调用
Single.java
public class Single {
private final String TAG = "Single";
private static Single instance = null;
private int num = 0;
public static synchronized Single getInstance() {
synchronized (Single.class) {
if (instance == null) {
instance = new Single();
}
}
return instance;
}
public synchronized void cal() {
addNum();
reduceNum();
}
private void addNum() {
num++;
Log.e(TAG, "num:" + num);
}
private void reduceNum() {
num--;
Log.e(TAG, "num:" + num);
}
}
复制代码
MainActivity.java
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void ClickButton(View view) {
for (int i = 0; i < 1000; i++) {
ThreadPool.getInstance().execute(new Runnable() {
@Override
public void run() {
Single.getInstance().cal(); //调用同步方法
}
});
}
}
}
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END