62,615
社区成员
发帖
与我相关
我的任务
分享
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
public static synchronized Object newobj(){
return new Object();
}
public static synchronized void addobj(ArrayList list){
list.add(new Object());
}
public static Singleton getInstance() {
if (instance == null){
instance = new Singleton();
}
return instance;
}
但上面的方法有个问题,假如有两种线程thread1、thread2同时调用这个getInstance方法,且理论上存在以下可能:
thread1调用方法getInstance时,在“if (instance == null){”这一行以后,在“new Singleton()”之前,由于操作系统调度(或者是JVM调度)的原因,导致线程进入wait;
在thread1重新run的时候,thread2开始执行,thread2运行好,一下就执行完了,那么thread2势必会执行“new Singleton()”
那么一定时间后,thread1重新run,由于thread1已经做过判断的,恢复run后,直接执行“new Singleton()”
鉴于上述极端情况,Singleton被new了两次。
因此,出现了以下情况的单例模式写法:
public static Singleton instance;
private static Object instanceLock = new Object();
public static Singleton getInstance2() {
synchronized (instanceLock) {
if (instance == null)
instance = new Singleton();
}
return instance;
}
但上面的方法也有一个弊端,就是每次调用getInstance2时,都会进入synchronized同步状态,这样限制了高并发时,多线程访问的速度,因此又演变成如下方案:
public static Singleton getInstance3() {
if (instance == null) {
synchronized (instanceLock) {
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
这样,极端情况下,只有所有调用者在第一次调用getInstance3时,会进入同步状态,后续不会进入同步状态。if (instance == null) {
synchronized (Singleton.class) {
if (instance == null)
instance = new Singleton();
}
}
而getInstance()是所以这么做,是因为后面使用的时候,所有的函数调用都会通过单例getInstance()函数,这样就每次都需要加锁控制,因而有性能的问题。因而才发展出了如上的代码,把synchronized放到里面去加锁同步,但这个方法就会出现指令重排等问题,所以还需要加上volatile修饰的办法来避免。
public synchronized String toString() {
int max = size() - 1;
if (max == -1)
return "{}";
StringBuilder sb = new StringBuilder();
Iterator<Map.Entry<K,V>> it = entrySet().iterator();
sb.append('{');
for (int i = 0; ; i++) {
Map.Entry<K,V> e = it.next();
K key = e.getKey();
V value = e.getValue();
sb.append(key == this ? "(this Map)" : key.toString());
sb.append('=');
sb.append(value == this ? "(this Map)" : value.toString());
if (i == max)
return sb.append('}').toString();
sb.append(", ");
}
}
[code=java]
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
[code]
这是Hashtable的toString方法和StringBuilder的toString方法的源码,HashTable的toString最后返回StringBuilder的toString,StringBuilder的toString会new一个String对象,如果按照上面问题中的说法,那有可能在return返回之后,new String对象还没有构建好,那这岂不是乱套了。对这块疑惑了很久,始终得不到一个权威的解答。
import java.util.ArrayList;
import java.util.List;
public class Test implements Runnable {
private List<Object> list;
public Test() {
this.list = new ArrayList<Object>();
}
public static synchronized void addobj(List<Object> list) {
list.add(new Object());
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(this.list.get(this.list.size() - 1));
Test.addobj(this.list);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Test t = new Test();
new Thread(t).start();
new Thread(t).start();
}
}
为什么说不能在多线程中访问呢?
System.out.println(this.list.get(this.list.size() - 1));
Test.addobj(this.list);
这2行代码,第一行打印出来的信息有可能是其它线程写入的,按照上面说的逻辑,那完全有可能会导致引用已经写入list中去了,但是new的Object对象还没准备好的问题啊。
[/quote]
可以参考
http://www.importnew.com/27002.html
当然我在上一段回复的时候我就知道你会举这个例子,但你这个例子问题难道不是多线程环境下对同一个资源访问的问题么,这个是集合的线程安全问题了,不是你所说的那个对象的问题了,因为就算解决了你说的问题,这个集合依然存在问题,但解决集合的线程安全问题,你那个问题也就解决了
为啥单例的双重判定要加volatile,那是因为用了双重判定,如果只用一重判定,根本不需要加volatile来修饰变量
因为双重判定的第一重判定在同步块外层,没有synchronized修饰,而且由于instance = new Singleton();并非原子操作,所以存在你所说的问题
[/quote]
感谢你,在这块我感觉你确实比我理解的要深一些,通过大家的回复,我感觉对这块理解也更深入一些了。谢谢各位。
import java.util.ArrayList;
import java.util.List;
public class Test implements Runnable {
private List<Object> list;
public Test() {
this.list = new ArrayList<Object>();
}
public static synchronized void addobj(List<Object> list) {
list.add(new Object());
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(this.list.get(this.list.size() - 1));
Test.addobj(this.list);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Test t = new Test();
new Thread(t).start();
new Thread(t).start();
}
}
为什么说不能在多线程中访问呢?
System.out.println(this.list.get(this.list.size() - 1));
Test.addobj(this.list);
这2行代码,第一行打印出来的信息有可能是其它线程写入的,按照上面说的逻辑,那完全有可能会导致引用已经写入list中去了,但是new的Object对象还没准备好的问题啊。
[/quote]
可以参考
http://www.importnew.com/27002.html
当然我在上一段回复的时候我就知道你会举这个例子,但你这个例子问题难道不是多线程环境下对同一个资源访问的问题么,这个是集合的线程安全问题了,不是你所说的那个对象的问题了,因为就算解决了你说的问题,这个集合依然存在问题,但解决集合的线程安全问题,你那个问题也就解决了
为啥单例的双重判定要加volatile,那是因为用了双重判定,如果只用一重判定,根本不需要加volatile来修饰变量
因为双重判定的第一重判定在同步块外层,没有synchronized修饰,而且由于instance = new Singleton();并非原子操作,所以存在你所说的问题
import java.util.ArrayList;
import java.util.List;
public class Test implements Runnable {
private List<Object> list;
public Test() {
this.list = new ArrayList<Object>();
}
public static synchronized void addobj(List<Object> list) {
list.add(new Object());
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(this.list.get(this.list.size() - 1));
Test.addobj(this.list);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Test t = new Test();
new Thread(t).start();
new Thread(t).start();
}
}
为什么说不能在多线程中访问呢?
System.out.println(this.list.get(this.list.size() - 1));
Test.addobj(this.list);
这2行代码,第一行打印出来的信息有可能是其它线程写入的,按照上面说的逻辑,那完全有可能会导致引用已经写入list中去了,但是new的Object对象还没准备好的问题啊。