在我的采访中,面试官以单身模式开始了他的问题。我在下面写道。然后,他问我们不应该在getInstance
方法中检查Nullity吗?
我回答说,它是 NOT 必需,因为成员是静态类型并且正在同时初始化。但是,他似乎对我的回答不满意。我是否正确?
class Single {
private final static Single sing = new Single();
private Single() {
}
public static Single getInstance() {
return sing;
}
}
现在,接下来的问题是他要求为多线程环境编写单例类。然后,我写了双重检查单例类。
class MultithreadedSingle {
private static MultithreadedSingle single;
private MultithreadedSingle() {
}
public static MultithreadedSingle getInstance() {
if(single==null){
synchronized(MultithreadedSingle.class){
if(single==null){
single= new MultithreadedSingle();
}
}
}
return single;
}
}
然后,他反对使用synchronized
并仔细检查并说它没用。你为什么要两次检查,为什么要使用synchronized?我试图用多种情况说服他。但是,他没有。
后来,在家里我尝试了下面的代码,我正在使用带有多个线程的简单单例类。
public class Test {
public static void main(String ar[]) {
Test1 t = new Test1();
Test1 t2 = new Test1();
Test1 t3 = new Test1();
Thread tt = new Thread(t);
Thread tt2 = new Thread(t2);
Thread tt3 = new Thread(t3);
Thread tt4 = new Thread(t);
Thread tt5 = new Thread(t);
tt.start();
tt2.start();
tt3.start();
tt4.start();
tt5.start();
}
}
final class Test1 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " : " + Single.getInstance().hashCode());
}
}
}
class Single {
private final static Single sing = new Single();
private Single() {
}
public static Single getInstance() {
return sing;
}
}
以下是输出:
Thread-0 : 1153093538
Thread-0 : 1153093538
Thread-0 : 1153093538
Thread-0 : 1153093538
Thread-0 : 1153093538
Thread-4 : 1153093538
Thread-1 : 1153093538
Thread-2 : 1153093538
Thread-3 : 1153093538
Thread-3 : 1153093538
Thread-3 : 1153093538
Thread-3 : 1153093538
Thread-3 : 1153093538
Thread-2 : 1153093538
Thread-2 : 1153093538
Thread-2 : 1153093538
Thread-2 : 1153093538
Thread-1 : 1153093538
Thread-1 : 1153093538
Thread-1 : 1153093538
Thread-1 : 1153093538
Thread-4 : 1153093538
Thread-4 : 1153093538
Thread-4 : 1153093538
Thread-4 : 1153093538
所以,问题是,是否有必要在多线程环境中使用synchronize
或/和双重检查方法?看起来我的第一个代码本身(没有添加任何额外的代码行)是这两个问题的答案。任何更正和知识分享将不胜感激。
答案 0 :(得分:6)
你的第一个例子绝对正确,通常是首选的成语&#34;对于单身人士。另一个是制作单元素枚举:
public enum Single {
INSTANCE;
...
}
这两种方法非常相似,除非该类是Serializable,在这种情况下,枚举方法更容易正确 - 但如果类不是Serializable,我实际上更喜欢你的方法enum one,as风格问题。注意&#34;意外&#34;由于实现了接口或扩展了一个本身可序列化的类,因此成为Serializable。
对于双重检查锁示例中的第二次检查是否正确,你也是对的。但是,sing
字段必须为volatile
才能使其在Java中运行;否则,没有正式的&#34;发生之前&#34;写入sing
的一个线程和读取它的另一个线程之间的边缘。这可能导致第二个线程看到null
,即使第一个线程分配给变量,或者,如果sing
实例具有状态,它甚至可能导致第二个线程只看到某个状态(看到部分构造的对象)。
答案 1 :(得分:2)
1)Class#1适用于多线程环境
2)类#2是一个具有延迟初始化和双重检查锁定的单例,它是一种已知模式,需要使用同步。但是你的实现被破坏了,它需要volatile
在该字段上。您可以在本文
3)使用一种方法的Singleton不需要使用延迟模式,因为它的类只会在第一次使用时加载和初始化。
答案 2 :(得分:1)
你的第一个答案似乎对我有好处,因为没有任何竞争条件的机会。
至于知识共享,在Java中实现单例的最佳方法是使用Enum。创建一个只包含一个实例的枚举,就是这样。至于代码示例 -
public enum MyEnum {
INSTANCE;
// your other methods
}
从好书Effective Java -
[....]这种方法在功能上等同于公共领域方法,除了它更简洁,免费提供序列化机制,并提供防止多个实例化的铁定保证,即使面对复杂的序列化或反射攻击。[...]单元素枚举类型是实现单例的最佳方式。
答案 3 :(得分:0)
根据Double-checked_locking,它可能是最好的方式
$data = file_get_contents("php://input");
$objData = json_decode($data);
$key = $objData->data;
if(!empty($key)){
$sql = "SELECT filmName as film, url as param, imagePath as image FROM films WHERE filmName LIKE '%$key%' LIMIT 5";
$result = mysqli_query($conn, $sql) or die(mysqli_error($conn));
$output = array();
while ($row = mysqli_fetch_array($result)) {
$output[] = $row;
}
echo json_encode($output);
}
或使用Initialization-on-demand holder idiom
class Foo {
private volatile Helper helper;
public Helper getHelper() {
Helper result = helper;
if (result == null) {
synchronized(this) {
result = helper;
if (result == null) {
helper = result = new Helper();
}
}
}
return result;
}
}
答案 4 :(得分:0)
在#2的情况下,将'volatile'关键字添加到静态字段'single'。
时,请考虑这种情况现在进入volatile关键字。
易失性变量始终写入主存储器。因此,没有缓存不一致性。