Рубрики
Без рубрики

Потокобезопасность в одноэлементных классах Java

Безопасность потоков в одноэлементном классе-важная тема. Как создать потокобезопасный одноэлементный класс в Java с использованием синхронизации и ранней инициализации.

Автор оригинала: Pankaj Kumar.

Синглтон-один из наиболее широко используемых шаблонов творческого проектирования для ограничения объектов, создаваемых приложениями. Если вы используете его в многопоточной среде, то потокобезопасность одноэлементного класса очень важна.

В реальных приложениях ресурсы, такие как подключения к базам данных или Корпоративные информационные системы (EIS), ограничены, и их следует использовать разумно, чтобы избежать любого дефицита ресурсов.

Для достижения этой цели мы можем реализовать шаблон одноэлементного проектирования . Мы можем создать класс-оболочку для ресурса и ограничить количество объектов, созданных во время выполнения, одним.

Потокобезопасный синглтон в Java

В общем, мы выполняем следующие действия, чтобы создать одноэлементный класс:

  1. Создайте закрытый конструктор , чтобы избежать создания любого нового объекта с помощью нового оператора.
  2. Объявите частный статический экземпляр того же класса.
  3. Предоставьте общедоступный статический метод, который вернет переменную экземпляра одноэлементного класса. Если переменная не инициализирована, то инициализируйте ее или просто верните переменную экземпляра.

Используя описанные выше шаги, я создал одноэлементный класс, который выглядит следующим образом.

Используя описанные выше шаги, я создал одноэлементный класс, который выглядит следующим образом.

package com.journaldev.designpatterns;

public class ASingleton {

	private static ASingleton instance = null;

	private ASingleton() {
	}

	public static ASingleton getInstance() {
		if (instance == null) {
			instance = new ASingleton();
		}
		return instance;
	}

}

В приведенном выше коде метод getInstance() не является потокобезопасным.

Несколько потоков могут получить к нему доступ одновременно. Для первых нескольких потоков, когда переменная экземпляра не инициализирована, несколько потоков могут войти в цикл if и создать несколько экземпляров. Это нарушит вашу одноэлементную реализацию.

Как добиться потокобезопасности в одноэлементном классе?

Существует три способа, с помощью которых мы можем обеспечить безопасность потоков.

  1. Создайте переменную экземпляра во время загрузки класса.
  2. Плюсы :

    • Потокобезопасность без синхронизации
    • Простота в реализации

    Минусы :

    • Раннее создание ресурса, который может не использоваться в приложении.
    • Клиентское приложение не может передать никаких аргументов, поэтому мы не можем использовать его повторно. Например, наличие универсального одноэлементного класса для подключения к базе данных, в котором клиентское приложение предоставляет свойства сервера базы данных.
  3. Синхронизируйте метод getInstance () .
  4. Плюсы :

    • Безопасность резьбы гарантируется.
    • Клиентское приложение может передавать параметры
    • Достигнута ленивая инициализация

    Минусы :

    • Низкая производительность из-за накладных расходов на блокировку.
    • Ненужная синхронизация, которая не требуется после инициализации переменной экземпляра.
  5. Используйте синхронизированный блок внутри цикла if и переменную volatile
  6. Плюсы :

    • Безопасность резьбы гарантируется
    • Клиентское приложение может передавать аргументы
    • Достигнута ленивая инициализация
    • Накладные расходы на синхронизацию минимальны и применимы только для первых нескольких потоков, когда переменная равна нулю.

    Минусы :

    • Дополнительное условие if

Рассматривая все три способа обеспечения потокобезопасности, я думаю, что третий-лучший вариант. В этом случае измененный класс будет выглядеть следующим образом:

package com.journaldev.designpatterns;

public class ASingleton {

	private static volatile ASingleton instance;
	private static Object mutex = new Object();

	private ASingleton() {
	}

	public static ASingleton getInstance() {
		ASingleton result = instance;
		if (result == null) {
			synchronized (mutex) {
				result = instance;
				if (result == null)
					instance = result = new ASingleton();
			}
		}
		return result;
	}

}

Локальная переменная результат кажется ненужной. Но он предназначен для повышения производительности нашего кода. В тех случаях, когда экземпляр уже инициализирован (большую часть времени), доступ к полю volatile осуществляется только один раз (из-за “возвращаемого результата;” вместо “возвращаемого экземпляра;”). Это может повысить общую производительность метода на целых 25 процентов.

Если вы считаете, что есть лучшие способы достичь этого или если потокобезопасность нарушена в приведенной выше реализации, пожалуйста, прокомментируйте и поделитесь ею со всеми нами.

Бонусный Совет

Строка-не очень хороший кандидат для использования с синхронизированным ключевым словом. Это потому, что они хранятся в пуле строк, и мы не хотим блокировать строку, которая может использоваться другим фрагментом кода. Поэтому я использую объектную переменную. Узнайте больше о синхронизации и безопасности потоков в java .