Synchronization VS Lock

Synchronization vs Lock

If you're simply locking an object, I'd prefer to use synchronized

Example:

Lock.acquire();
doSomethingNifty(); // Throws a NPE!
Lock.release(); // Oh noes, we never release the lock!

You have to explicitly do try{} finally{} everywhere.

Whereas with synchronized, it's super clear and impossible to get wrong:

synchronized(myObject) {
doSomethingNifty();
}

That said, Locks may be more useful for more complicated things where you can't acquire and release in such a clean manner. I would honestly prefer to avoid using bare Locks in the first place, and just go with a more sophisticated concurrency control such as a CyclicBarrier or a LinkedBlockingQueue, if they meet your needs.

I've never had a reason to use wait() or notify() but there may be some good ones.

difference between locks and synchronized methods

Both of your examples you provided will serve the same purpose and they are equal in terms of thread-safe (I would also volatile your balance). ReentrantLock is more 'unstructured', this means that you can lock a critical section in one method and unlock it in another method; something you cannot do with synchronized because it's structure on a block.

There's also a performance issue where you want to utilize ReentrantLock over synchronized but that is true only when involving multiple threads.

Lock and Condition vs Synchronization

Lock vs synchronized
1. Lock provides more visibility and options for locking, unlike synchronized where a thread might end up waiting indefinitely for the lock, we can use tryLock() to make sure thread waits for specific time only.

2. Synchronization code is much cleaner and easy to maintain whereas with Lock we are forced to have try-finally block to make sure Lock is released even if some exception is thrown between lock() and unlock() method calls.

3. synchronization blocks or methods can cover only one method whereas we can acquire the lock in one method and release it in another method with Lock API.

4. synchronized keyword doesn’t provide fairness whereas we can set fairness to true while creating ReentrantLock object so that longest waiting thread gets the lock first.

5. We can create different conditions for Lock and different thread can await() for different conditions.

From here. For me, the most important reasons to use Lock are 3 and 4.

what the difference between using synchronized keyword and lock

'synchronize' will lock any resources accessed within the method. 'lock' allows you more granularity, and control (e.g. only locking some resources, or locking only if a certain condition is met, for example).

There's a pretty good code sample near the top of this link:
http://download.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/locks/Lock.html

The difference between read/write locks vs synchronized shown on basic code

The difference between synchronized and read/write locks is such that when you are using synchronized it allows only one thread at a time to access. With read/write lock you can have many readers at the same time (given that there are no write locks already in) so you can get better concurrent performance under some cases, especially when having many reads here.

You should add many more threads that are accessing this object to test performance.

And you can simply count time between finishing and starting of operations to measure performance (in example - Long startTime = System.nanoTime();).

Read up here to see how to check whether thread has ended so you can measure execution time :
How to know if other threads have finished?


edit to answer comment:
Hey, my answer is a bit simplified (ok, very, as multithreading is hard) as I was writing this in between doing stuff, so, for now, I can link you with some other resources which provide more in-depth look.

  • What does 'synchronized' mean?
  • How do I write a correct micro-benchmark in Java? <- when writing performance tests it's good to be aware of this
  • https://javarevisited.blogspot.com/2013/03/reentrantlock-example-in-java-synchronized-difference-vs-lock.html
  • https://javarevisited.blogspot.com/2011/04/synchronization-in-java-synchronized.html

very simple example as per your existing class:

class Number {

private int value;

public Number(int value) {
this.value = value;
}

public synchronized int getValue() {
return value;
}

public synchronized int changeData(int change) {
value = change;
return value;
}
}

Why use a ReentrantLock if one can use synchronized(this)?

A ReentrantLock is unstructured, unlike synchronized constructs -- i.e. you don't need to use a block structure for locking and can even hold a lock across methods. An example:

private ReentrantLock lock;

public void foo() {
...
lock.lock();
...
}

public void bar() {
...
lock.unlock();
...
}

Such flow is impossible to represent via a single monitor in a synchronized construct.


Aside from that, ReentrantLock supports lock polling and interruptible lock waits that support time-out. ReentrantLock also has support for configurable fairness policy, allowing more flexible thread scheduling.

The constructor for this class accepts an optional fairness parameter. When set true, under contention, locks favor granting access to the longest-waiting thread. Otherwise this lock does not guarantee any particular access order. Programs using fair locks accessed by many threads may display lower overall throughput (i.e., are slower; often much slower) than those using the default setting, but have smaller variances in times to obtain locks and guarantee lack of starvation. Note however, that fairness of locks does not guarantee fairness of thread scheduling. Thus, one of many threads using a fair lock may obtain it multiple times in succession while other active threads are not progressing and not currently holding the lock. Also note that the untimed tryLock method does not honor the fairness setting. It will succeed if the lock is available even if other threads are waiting.


ReentrantLock may also be more scalable, performing much better under higher contention. You can read more about this here.

This claim has been contested, however; see the following comment:

In the reentrant lock test, a new lock is created each time, thus there is no exclusive locking and the resulting data is invalid. Also, the IBM link offers no source code for the underlying benchmark so its impossible to characterize whether the test was even conducted correctly.


When should you use ReentrantLocks? According to that developerWorks article...

The answer is pretty simple -- use it when you actually need something it provides that synchronized doesn't, like timed lock waits, interruptible lock waits, non-block-structured locks, multiple condition variables, or lock polling. ReentrantLock also has scalability benefits, and you should use it if you actually have a situation that exhibits high contention, but remember that the vast majority of synchronized blocks hardly ever exhibit any contention, let alone high contention. I would advise developing with synchronization until synchronization has proven to be inadequate, rather than simply assuming "the performance will be better" if you use ReentrantLock. Remember, these are advanced tools for advanced users. (And truly advanced users tend to prefer the simplest tools they can find until they're convinced the simple tools are inadequate.) As always, make it right first, and then worry about whether or not you have to make it faster.


One final aspect that's gonna become more relevant in the near future has to do with Java 15 and Project Loom. In the (new) world of virtual threads, the underlying scheduler would be able to work much better with ReentrantLock than it's able to do with synchronized, that's true at least in the initial Java 15 release but may be optimized later.

In the current Loom implementation, a virtual thread can be pinned in two situations: when there is a native frame on the stack — when Java code calls into native code (JNI) that then calls back into Java — and when inside a synchronized block or method. In those cases, blocking the virtual thread will block the physical thread that carries it. Once the native call completes or the monitor released (the synchronized block/method is exited) the thread is unpinned.

If you have a common I/O operation guarded by a synchronized, replace the monitor with a ReentrantLock to let your application benefit fully from Loom’s scalability boost even before we fix pinning by monitors (or, better yet, use the higher-performance StampedLock if you can).

What is the difference between synchronized on lockObject and using this as the lock?

Personally I almost never lock on "this". I usually lock on a privately held reference which I know that no other code is going to lock on. If you lock on "this" then any other code which knows about your object might choose to lock on it. While it's unlikely to happen, it certainly could do - and could cause deadlocks, or just excessive locking.

There's nothing particularly magical about what you lock on - you can think of it as a token, effectively. Anyone locking with the same token will be trying to acquire the same lock. Unless you want other code to be able to acquire the same lock, use a private variable. I'd also encourage you to make the variable final - I can't remember a situation where I've ever wanted to change a lock variable over the lifetime of an object.



Related Topics



Leave a reply



Submit