Sunday, May 19, 2024
HomeJavaJava Concurrency: Synchronized - Java Code Geeks

Java Concurrency: Synchronized – Java Code Geeks


Beforehand we had an introduction to threads in Java and a few of the internals. On this weblog we are going to proceed on creating thread secure code utilizing synchronized.

Intrinsic lock or monitor lock is achieved utilizing the key phrase synchronized

For synchronized we want an object to make use of for locking. Each object has an intrinsic lock related to it.

When a thread enters a synchronized block the lock is acquired and as soon as the operation is finished the lock might be launched.

Let’s begin with our utility which might be single threaded for begin. It is going to be a counter utility.

We are going to outline an interface

bundle com.gkatzioura.concurrency.intrinsiclock;

public interface Counter {
    void increment();

    Integer get();

}

and a non thread secure implementation

bundle com.gkatzioura.concurrency.intrinsiclock;

public class SimpleCounter implements Counter {

    non-public Integer counter = 0;

    @Override
    public void increment() {
        counter++;
    }

    @Override
    public Integer get() {
        return counter;
    }

}

To attain thread security we will use synchronized features. On synchronized strategies the intrinsic lock acquired is the lock of the strategy’s object. Basically the lock might be that class occasion.
Our class will rework to this.

bundle com.gkatzioura.concurrency.intrinsiclock;

public class CounterSynchronizedFunctions implements Counter {

    non-public Integer counter = 0;

    public synchronized void increment() {
        counter++;
    }

    public synchronized Integer get() {
        return counter;
    }

}

A number of threads will enhance the worth. If greater than two threads increment the identical worth on the similar time we lose increment operations.
Thus we would like all threads to increment the counter and to not overwrite one another’s outcomes.
One other factor to look at is that now we have synchronized on the get methodology. It is because we wish to get a constant worth. We wish to get a worth which has been modified by one other thread, this might not be potential if the counter worth fetched, can be from the CPU cache and has not been up to date with newest modifications. Through the use of synchronized on this case we make the most of the synchronized ensures. When getting into the synchronized block all of the variables seen by the thread are up to date from the primary reminiscence. Additionally once we depart the synchronized block the variables modifications are written again to reminiscence.

Since we put synchronized within the class strategies we use the lock of that class occasion.
This method is useful nevertheless it has the pitfall that if the identical object is used for different locking usages the counter is not going to function correctly.

Let’s simulate this one.

bundle com.gkatzioura.concurrency.intrinsiclock;

import java.util.Date;

import org.junit.jupiter.api.Check;

import lombok.extern.slf4j.Slf4j;


@Slf4j
class CounterSynchronizedFunctionsTest {

    @Check
    void title() throws InterruptedException {
        CounterSynchronizedFunctions counter = new CounterSynchronizedFunctions();

        Thread outdated = new Thread(() -> {
            synchronized (counter) {
                attempt {
                    log.data("Buying lock first [{}]", new Date());
                    Thread.sleep(10000);
                    log.data("Releasing lock [{}]", new Date());
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        Thread newOne = new Thread(() -> {
            log.data("I must be blocked [{}]", new Date());
            counter.increment();
            log.data("I'm unblocked [{}]", new Date());
        });

        outdated.begin();
        Thread.sleep(1000);
        newOne.begin();

        outdated.be a part of();
        newOne.be a part of();
    }
}

The primary thread makes use of the category occasion of Counter as a monitoring lock. The second thread will attempt to use the increment operate and due to this fact will block, till the lock is launched from the primary thread.

We are able to validate by checking the output

12:50:54.631 [Thread-0] INFO com.gkatzioura.concurrency.intrinsiclock.CounterSynchronizedFunctionsTest - Buying lock first [Sun Mar 05 12:50:54 GMT 2023]
12:50:55.639 [Thread-1] INFO com.gkatzioura.concurrency.intrinsiclock.CounterSynchronizedFunctionsTest - I must be blocked [Sun Mar 05 12:50:55 GMT 2023]
12:51:04.656 [Thread-0] INFO com.gkatzioura.concurrency.intrinsiclock.CounterSynchronizedFunctionsTest - Releasing lock [Sun Mar 05 12:51:04 GMT 2023]
12:51:04.658 [Thread-1] INFO com.gkatzioura.concurrency.intrinsiclock.CounterSynchronizedFunctionsTest - I'm unblocked [Sun Mar 05 12:51:04 GMT 2023]

That is one thing that may occur for the reason that object will be handed round by means of the codebase.
To keep away from points like this we are going to use a lock seen solely inside the category.

bundle com.gkatzioura.concurrency.intrinsiclock;

public class CounterInternallySynchronized implements Counter {

    non-public static remaining Object lock = new Object();

    non-public Integer counter = 0;

    public void increment() {
        synchronized (lock) {
            counter++;
        }
    }

    public Integer get() {
        synchronized (lock) {
            return counter;
        }
    }

}

We are able to attempt now the identical check however with a special implementation.

bundle com.gkatzioura.concurrency.intrinsiclock;

import java.util.Date;

import org.junit.jupiter.api.Check;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class CounterInternallySynchronizedTest {

    @Check
    void title() throws InterruptedException {
        Counter counter = new CounterInternallySynchronized();

        Thread outdated = new Thread(() -> {
            synchronized (counter) {
                attempt {
                    log.data("Buying lock utilizing counter occasion [{}]", new Date());
                    Thread.sleep(10000);
                    log.data("Releasing lock utilizing counter occasion [{}]", new Date());
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        Thread newOne = new Thread(() -> {
            log.data("I shouldn't be blocked [{}]", new Date());
            counter.increment();
            log.data("I completed operation unblocked [{}]", new Date());
        });

        outdated.begin();
        Thread.sleep(1000);
        newOne.begin();

        outdated.be a part of();
        newOne.be a part of();
    }

}
12:52:58.314 [Thread-0] INFO com.gkatzioura.concurrency.intrinsiclock.CounterInternallySynchronizedTest - Buying lock utilizing counter occasion [Sun Mar 05 12:52:58 GMT 2023]
12:52:59.334 [Thread-1] INFO com.gkatzioura.concurrency.intrinsiclock.CounterInternallySynchronizedTest - I shouldn't be blocked [Sun Mar 05 12:52:59 GMT 2023]
12:52:59.335 [Thread-1] INFO com.gkatzioura.concurrency.intrinsiclock.CounterInternallySynchronizedTest - I completed operation unblocked [Sun Mar 05 12:52:59 GMT 2023]
12:53:08.344 [Thread-0] INFO com.gkatzioura.concurrency.intrinsiclock.CounterInternallySynchronizedTest - Releasing lock utilizing counter occasion [Sun Mar 05 12:53:08 GMT 2023]

As anticipated we hold our code thread secure whereas we keep away from any unintended lock acquisition.

Printed on Java Code Geeks with permission by Emmanouil Gkatziouras, accomplice at our JCG program. See the unique article right here: Java Concurrency: Synchronized

Opinions expressed by Java Code Geeks contributors are their very own.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments