Шаблон проектирования 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, так как они обеспечивают потокобезопасность и простоту реализации.