单例作为最传统的设计模式,我相信会被最多小伙伴使用
问起单例的特性,小伙伴们肯定会说,保证内存中只有一个单例对象,可以共享。
但是,实际上这只是在单线程中是安全的,如果在多线程中,单例的使用就要格外小心,很多情况会因为数据的操作导致系统崩溃。
话不多说,直接上代码
单例代码:
public class SingleTon {
private static SingleTon instance;
private static CommonBean commonBean;
public SingleTon() {
commonBean = new CommonBean();
commonBean.setVar1("1");
commonBean.setVar2("2");
commonBean.setVar3("3");
}
public static SingleTon getInstance(){
if (instance == null){
instance = new SingleTon();
}
return instance;
}
public void doSingleWork(){
commonBean.doTest();
}
public void doSingleWorkWithDataChaneg(){
commonBean.setVar3("4");
commonBean.doTest();
}
}
模拟数据操作对象
public class CommonBean {
private static final String TAG = "CommonBean";
private String var1;
private String var2;
private String var3;
private Map<String, String> mMap =null;
public String getVar1() {
return var1;
}
public void setVar1(String var1) {
this.var1 = var1;
}
public String getVar2() {
return var2;
}
public void setVar2(String var2) {
this.var2 = var2;
}
public String getVar3() {
return var3;
}
public void setVar3(String var3) {
this.var3 = var3;
}
public Map<String, String> getMap() {
if (mMap == null){
mMap = new HashMap<>();
}
mMap.clear();
mMap.put("var1",var1);
mMap.put("var2",var2);
mMap.put("var3",var3);
return mMap;
}
public void doTest(){
for (String s:getMap().values()
) {
Log.i(TAG, "doTest: "+s);
try {
if (s.equals("3")) {
Thread.sleep(5 * 1000);
Log.i(TAG, Thread.currentThread().getName()+"开始等待...");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public String toString() {
return "CommonBean{" +
"var1='" + var1 + '\'' +
", var2='" + var2 + '\'' +
", var3='" + var3 + '\'' +
'}';
}
调用方式很简单,新建两个线程,记住不要起匿名线程,这样不利于追踪
TestThread thread1 = new TestThread(test1,"test1");
TestThread thread2 = new TestThread(test2,"test2");
thread1.start();
thread2.start();
private class TestThread extends Thread{
public TestThread(Runnable target, String name) {
super(target, name);
}
@Override
public void run() {
super.run();
Log.i(TAG, "run: " + currentThread().getName());
}
}
Runnable test1 = new Runnable() {
@Override
public void run() {
SingleTon.getInstance().doSingleWork();
}
};
Runnable test2 = new Runnable() {
@Override
public void run() {
SingleTon.getInstance().doSingleWorkWithDataChaneg();
}
};
第一个起来的线程会在进入的运行的时候等待5s,而第二个线程立马进入会立即调用数据修改,便会导致出错,map对象在foreach循环里不允许对象修改
其实这个时候多线程对单例的操作,可以被认为是生成了不同的对象,所以这个时候的单例,就不是那么安全了
解决方法也很简单,在对数据操作部分加上方法锁
public synchronized void doTest()
如果是Android还可以使用单线程池