我对单身人士的同步有疑问。假设我有一个像这样的单例模式:
public class Singleton {
private final Object obj;
private static Singleton instance;
private Singleton(Object obj) {
this.obj = obj;
}
public static synchronized Singleton getInstance(Object obj) {
if (instance == null) {
instance = new Singleton(obj);
}
return instance;
}
public synchronized void methodA() {
// ...
}
public synchronized void methodB() {
// ...
}
}
我想同步它的访问权限。我有两个假设,我需要验证。
假设: 由于所有方法都在同步,包括初始化程序,因此Singleton上的每次访问都是线程安全和同步的。
假设:
当我想确保一个线程想要调用methodA()
然后立即methodB()
而没有另一个线程调用单例的方法时,在这样的实例上同步是否正确?
Singleton singleton = Singleton.getInstance(obj);
synchronized (singleton) {
singleton.methodA();
singleton.methodB();
}
说明:
1.假设是否正确,因为非静态方法上的synchronized在对象本身上同步,并且因为它始终是同一个对象,所以访问是同步的?对getInstance(obj)
的调用会在类本身上同步吗?
2.假设是否正确,因为getInstance(obj)
每个线程获得相同的对象,因此同步是正确的,因为另一个线程将等待同步块(...methodA(); methodB();
)退出?
答案 0 :(得分:1)
假设我理解正确,您的假设是正确的。我只想指出,在大多数情况下,您可以使用更简单的单例模式:
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
该字段将以线程安全的方式在第一个静态引用(例如getInstance()
)上初始化,而无需显式同步。
此外,instance
应为final
。
答案 1 :(得分:0)
您的第一个假设是正确的,但是,通过专用锁定对象而不是使用synchronized
方法进行同步是一种很好的做法。这样做可以将并发问题排除在API之外,并允许您稍后从 java.util.concurrent 包中替换更有效的实现。您还会阻止deadlocks,因为其他对象无法获取相同的锁定对象。
public class Example {
// leaving out the singleton aspect here
// consider java.util.concurrent.locks instead
private Object lock = new Object();
public synchronized void methodA() {
synchronized(lock) {
// ...
}
}
public synchronized void methodB() {
synchronized(lock) {
// ...
}
}
}
客户端仍然可以在实例上进行同步:
synchronized(instance) {
instance.methodA();
instance.methodB();
}
例如,查看SynchronizedCollection
类中java.util.Collections
的实现,它也使用内部锁对象。
关于在单例上进行同步的一个问题:如果应用程序中有很多并发线程(例如,在Web应用程序中),则此中央锁定很快就会成为性能瓶颈,因为所有线程都必须等待彼此
答案 2 :(得分:0)
两者都是真的。
测试它的最佳方法(测试解决方案总是好的,而不是依赖于假设)将在调用方法之间等待一段时间,并从不同的线程调用methodA,然后检查执行顺序。例如:
public synchronized void methodA() {
println("methodA");
}
public synchronized void methodB() {
println("methodB");
}
然后,在测试用例或主要方法中:
new Thread(){
public void run(){
Singleton singleton = Singleton.getInstance();
synchronized (singleton) {
singleton.methodA();
singleton.wait(5000L);//this gives us 5 seconds to call it from different thread
singleton.methodB();
}
}
}.start();
new Thread(){
public void run(){
Singleton.getInstance().methodA();
}
}
}.start();
答案 3 :(得分:0)
如你所知,synchronized关键字用于获取锁定 宾语。如果你想在执行后调用你愿意的方法 一种方法。然后我们可以从同步方法调用方法 在代码之下,但Thread在它出来之前不会发布 第一个同步块。
intertools
这里Thread将获取锁定进入同步method1A at 最后它将调用另一个synchronized方法1B。线程是 在完成method1A执行后将释放锁。
答案 4 :(得分:0)
你们两个假设都是真的。但是,为了创建Singleton,我将建议以下方法,它是100%线程安全且没有同步问题,因为它利用了Java的类加载机制。
就像这个类一样,Provider只会加载一次,因此只存在一个Network类实例,并且没有两个线程可以在同一个JVM中创建2个实例,因为类不会被加载两次。
public class Network {
private Network(){
}
private static class Provider {
static final Network INSTANCE = new Network();
}
public static Network getInstance() {
return Provider.INSTANCE;
}
//More code...
}
答案 5 :(得分:0)
线程安全单例样本;
public class Singleton {
private static Singleton instance;
private Singleton(){
}
public static Singleton getInstance(){
synchronized (Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
return instance;
}
}