I have created an simple singleton in Swift 3:
class MySingleton {
private var myName: String
private init() {}
static let shared = MySingleton()
func setName(_ name: String) {
myName = name
}
func getName() -> String {
return myName
}
}
Since I made the init()
private , and also declared shared
instance to be static let
, I think the initializer is thread safe. But what about the getter and setter functions for myName
, are they thread safe?
Thread Safe Singleton in JavaCreate the private constructor to avoid any new object creation with new operator. Declare a private static instance of the same class. Provide a public static method that will return the singleton class instance variable.
Is singleton thread safe? A singleton class itself is not thread safe. Multiple threads can access the singleton same time and create multiple objects, violating the singleton concept. The singleton may also return a reference to a partially initialized object.
It is thread-safe even if we remove final from public static final Singleton instance = new Singleton (); .
You can create a thread safe singleton using DispatchQueue. We will create a serial queue and add a sync method to set and get value. Below is the code you can achieve thread safe singleton class. It will write an another blog where we can improve the performance using dispatch barrier.
A slightly different way to do it (and this is from an Xcode 9 Playground) is to use a concurrent queue rather than a serial queue.
final class MySingleton {
static let shared = MySingleton()
private let nameQueue = DispatchQueue(label: "name.accessor", qos: .default, attributes: .concurrent)
private var _name = "Initial name"
private init() {}
var name: String {
get {
var name = ""
nameQueue.sync {
name = _name
}
return name
}
set {
nameQueue.async(flags: .barrier) {
self._name = newValue
}
}
}
}
.barrier
async dispatch. The block can be performed asynchronously because there is no need for the caller to wait for the value to be set. The block will not be run until all the other blocks in the concurrent queue ahead of it have completed. So, existing pending reads will not be affected while this setter is waiting to run. The barrier means that when it starts running, no other blocks will run. Effectively, turning the queue into a serial queue for the duration of the setter. No further reads can be made until this block completes. When the block has completed the new value has been set, any getters added after this setter can now run concurrently.You are correct that those getters that you've written are not thread safe. In Swift, the simplest (read safest) way to achieve this at the moment is using Grand Central Dispatch queues as a locking mechanism. The simplest (and easiest to reason about) way to achieve this is with a basic serial queue.
class MySingleton {
static let shared = MySingleton()
// Serial dispatch queue
private let lockQueue = DispatchQueue(label: "MySingleton.lockQueue")
private var _name: String
var name: String {
get {
return lockQueue.sync {
return _name
}
}
set {
lockQueue.sync {
_name = newValue
}
}
}
private init() {
_name = "initial name"
}
}
Using a serial dispatch queue will guarantee first in, first out execution as well as achieving a "lock" on the data. That is, the data cannot be read while it is being changed. In this approach, we use sync
to execute the actual reads and writes of data, which means the caller will always be forced to wait its turn, similar to other locking primitives.
Note: This isn't the most performant approach, but it is simple to read and understand. It is a good general purpose solution to avoid race conditions but isn't meant to provide synchronization for parallel algorithm development.
Sources: https://mikeash.com/pyblog/friday-qa-2015-02-06-locks-thread-safety-and-swift.html What is the Swift equivalent to Objective-C's "@synchronized"?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With