-3

I have created a simple Worker :

public class Worker {

    public synchronized void writeData() {
        try {
            System.out.println("write Data , thread id = " + Thread.currentThread().getId());
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void readData() {
        try {
            System.out.println("readData , thread id = " + Thread.currentThread().getId());
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

AFAIK, If multiple thread access the same Worker instance, the synchronized only blocks threads that access the same method. AKA if thread A invokes writeData and B uses readData, they will not influence each other (Correct me if I am wrong).

BUT, when I tried to demo it by the code below:

private static void testWithThreads() {
        final Worker worker = new Worker();

        new Thread(() -> {
            System.out.println("start read thread");
            for (int i = 0; i < 20; i++) {
                worker.readData();
            }

        }).start();

        new Thread(() -> {
            System.out.println("start write thread");
            for (int i = 0; i < 20; i++) {
                worker.writeData();
            }

        }).start();
    }

I got the output like this (Note that we have Thread.sleep for 2 seconds here):

start read thread
readData , thread id = 10
start write thread
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
write Data , thread id = 11
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10
readData , thread id = 10

Can anyone explain this to me? It seems they blocked each other in some way.

WoooHaaaa
  • 19,732
  • 32
  • 90
  • 138
  • `synchronized` methods sync on the **instance** you're calling the methods on. So they are syncing on the same object in this case, and the two methods will definitely block each other. – khelwood May 17 '17 at 10:43
  • 2
    ["When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object."](https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html) All synchronized methods are blocked for that object, not just one. – Tom May 17 '17 at 10:43
  • `Thread.currentthread.join()` is a self deadlock. The current thread waits for itself to 'die' forever.... – Persixty May 17 '17 at 10:51

3 Answers3

0

synchronized on a method level synchronizes access to all synchronized methods of the Object the methods belongs to, that only one thread can execute in any synchronized method of that object. The other threads will wait even if they try to access other synchronized method than the first thread.

The other Threads will block till the first one will get out from the synchronized blocks.

In your code beetween the invocation of synchornized methods in for loops, there is tiny time slot in which other thread can get into the writeData() before the first get again into readData() - a typical for loop is not atomic operation - but this time slot is so tiny, that it rarely happens - so your output looks like they are blocking each other in some way - and in one point the wind changes and other thread takes the lead.

to be more specific, comments are pointing where "unsynchronized" time slot begins in each for loop:

private static void testWithThreads() {
        final Worker worker = new Worker();

        new Thread(() -> {
            System.out.println("start read thread");
            for (int i = 0; i < 20; i++) {
                worker.readData();
                // any thread can now invoke writeData() if current thread is before next invocation of worker.readData();
            }

            try {
                Thread.currentThread().join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        new Thread(() -> {
            System.out.println("start write thread");
            for (int i = 0; i < 20; i++) {
                worker.writeData();
                // any thread can now invoke readData() if current thread is before next invocation of worker.writeData();
            }

            try {
                Thread.currentThread().join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }

If you want to have better interleaving you can do one of these things:

  • use wait() and notify()
  • do not use synchronization on that methods - synchronize the data.
  • move the sleep operation outside the synchronized write and read methods, they will give the threads more chance to get into the synchronized block.
Krzysztof Cichocki
  • 6,294
  • 1
  • 16
  • 32
0

the synchronized only blocks threads that access the same method

Wrong. It blocks threads trying to synchronize on the same object.

user207421
  • 305,947
  • 44
  • 307
  • 483
0

How it works is if A is using writeData for a Worker instance then B cannot use readData or writeData from the same Worker until it is given a chance.

If you were hoping to have your output to be:

read

write

read

write

etc...

then I would suggest using the functions wait(); and notifyAll(); This way you can make thread A give Thread B a turn once it is finished and vice versa.

You can read more about wait() and notifyAll() here.

xht5252
  • 11
  • 3