Шаблон проектирования Singleton (Одиночка) гарантирует, что класс имеет только один экземпляр, и предоставляет глобальную точку доступа к этому экземпляру. В Java существует несколько способов реализации Singleton, каждый из которых имеет свои преимущества и подводные камни. Рассмотрим основные подходы.
Это самый простой способ реализации Singleton, но он не подходит для многопоточных приложений, так как может привести к созданию нескольких экземпляров.
public class SimpleSingleton {
private static SimpleSingleton instance;
private SimpleSingleton() {
// Приватный конструктор
}
public static SimpleSingleton getInstance() {
if (instance == null) {
instance = new SimpleSingleton();
}
return instance;
}
}
getInstance(), может быть создано несколько экземпляров.Чтобы сделать Singleton потокобезопасным, можно использовать синхронизацию. Однако это может привести к снижению производительности из-за блокировок.
public class ThreadSafeSingleton {
private static ThreadSafeSingleton instance;
private ThreadSafeSingleton() {
// Приватный конструктор
}
public static synchronized ThreadSafeSingleton getInstance() {
if (instance == null) {
instance = new ThreadSafeSingleton();
}
return instance;
}
}
Этот подход улучшает производительность, синхронизируя только часть кода, где создается экземпляр.
public class DoubleCheckedLockingSingleton {
private static volatile DoubleCheckedLockingSingleton instance;
private DoubleCheckedLockingSingleton() {
// Приватный конструктор
}
public static DoubleCheckedLockingSingleton getInstance() {
if (instance == null) {
synchronized (DoubleCheckedLockingSingleton.class) {
if (instance == null) {
instance = new DoubleCheckedLockingSingleton();
}
}
}
return instance;
}
}
volatile, чтобы избежать проблем с видимостью изменений между потоками.Этот подход использует статический внутренний класс для создания экземпляра Singleton. Он потокобезопасен и не требует синхронизации.
public class BillPughSingleton {
private BillPughSingleton() {
// Приватный конструктор
}
private static class SingletonHelper {
private static final BillPughSingleton INSTANCE = new BillPughSingleton();
}
public static BillPughSingleton getInstance() {
return SingletonHelper.INSTANCE;
}
}
Перечисления в Java являются потокобезопасными и гарантируют, что экземпляр будет создан только один раз. Это самый простой и безопасный способ реализации Singleton.
public enum EnumSingleton {
INSTANCE;
public void doSomething() {
System.out.println("Выполнение действия...");
}
}
Можно использовать классы из пакета java.util.concurrent, такие как AtomicReference, для реализации Singleton.
import java.util.concurrent.atomic.AtomicReference;
public class ConcurrentSingleton {
private static final AtomicReference<ConcurrentSingleton> INSTANCE = new AtomicReference<>();
private ConcurrentSingleton() {
// Приватный конструктор
}
public static ConcurrentSingleton getInstance() {
for (;;) {
ConcurrentSingleton instance = INSTANCE.get();
if (instance != null) {
return instance;
}
instance = new ConcurrentSingleton();
if (INSTANCE.compareAndSet(null, instance)) {
return instance;
}
}
}
}
AtomicReference и может быть излишне сложной для простых случаев.AtomicReference, подходит для сложных сценариев.Выбор реализации Singleton зависит от требований вашего приложения. Для большинства случаев рекомендуется использовать Bill Pugh Singleton или Enum Singleton, так как они обеспечивают потокобезопасность и простоту реализации.