Reputation: 13
I wrote this program to check if a thread_1 holding lock on two different objects: LOCK_OBJECT
and FULL
goes into waiting mode on LOCK_OBJECT
using FULL.wait()
. I didn't think the consumer would get the LOCK_OBJECT
lock, but the print didn't. So is there anything you missed?
Why does the consumer get the LOCK_OBJECT
lock based on the printed results?
This is my code:
public class TestSync {
private volatile Integer amount = 0;
private final Object LOCK_OBJECT = new Object();
private final Object FULL = new Object();
public void doubleSync() throws InterruptedException {
System.out.println("Producer trying to get LOCK_OBJECT lock ");
synchronized (LOCK_OBJECT) {
System.out.println("Producer get LOCK_OBJECT lock ");
Print.sleep(3000);
while (amount >= 0) {
synchronized (FULL) {
System.out.println("full!");
FULL.wait();
}
}
System.out.println("continue~");
amount++;
}
}
public void simpleSync() {
System.out.println("Consumer trying to get LOCK_OBJECT lock ");
synchronized (LOCK_OBJECT) {
System.out.println("Consumer get LOCK_OBJECT lock");
}
System.out.println("Consumer release LOCK_OBJECT lock");
}
public static void main(String[] args) throws InterruptedException {
AtomicInteger atomicInteger = new AtomicInteger(0);
ExecutorService threadPool = Executors.newFixedThreadPool(1, new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
int index = atomicInteger.incrementAndGet();
System.out.println("create no " + index + " thread");
Thread t = new Thread(r, "one Thread-" + index);
return t;
}
});
threadPool.execute(() -> {
try {
new TestSync().doubleSync();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Print.sleep(1000);
ExecutorService executorService = Executors.newFixedThreadPool(1, new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
int index = atomicInteger.incrementAndGet();
System.out.println("create no " + index + " thread");
Thread t = new Thread(r, "two Thread-" + index);
return t;
}
});
executorService.execute(() -> {
try {
new TestSync().simpleSync();
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
And this is the print result:
create no 1 thread
sleep 1000
Producer trying to get LOCK_OBJECT lock
Producer get LOCK_OBJECT lock
sleep 3000
create no 2 thread
Consumer trying to get LOCK_OBJECT lock
Consumer get LOCK_OBJECT lock
Consumer release LOCK_OBJECT lock
full!
Upvotes: 1
Views: 194
Reputation: 718788
Does
wait()
release all locks?
No. It only releases the lock for the object you call wait()
on.
As for the behavior of your code ... we cannot fully explain it because it is missing a crucial method: Print
.
However, it looks like you are using multiple instances of TestSync
. That would mean that the producer and consumer threads have different objects for the respective lock objects ... and there is no mutual exclusion.
It is also a bad idea (wasteful) to use multiple thread pools, and particularly to create and destroy them dynamically. The primary advantage of thread pools compared with disposable threads is that the (significant) thread creation is amortized. But when you shut down (or "lose") a thread pool, the threads get discarded anyway ... and have to be created again for the "next" thread pool.
(This doesn't affect the behavior of your example. But it is a bad habit ... and it is liable to bite you if you do it in a real application.)
Lesson: Programming with primitive mutexes and wait/notify requires considerable care. It is better to use the higher level concurrency primitives ... where possible.
Upvotes: 0
Reputation: 21
1.It is always better to proceed with a one ExecutorService implementation (as we maintain limited number of thread alive all the time for performance reasons)
2.wait() does releases the lock for a single object but after that all the threads (considering same priority) are equally probable to get the lock
Upvotes: 0
Reputation: 20914
The code in your question does not compile. What is Print
? It appears twice in your code. Once in method main
Print.sleep(1000);
In the below code, I simply created a class named Print
and defined a static method sleep
.
Locks work on objects that are shared between threads. In your code you create a separate TestSync
object for each thread. Hence no shared objects. Try creating one instance of TestSync
and send it to both threads. Also, you don't need a separate ExecutorService
for each thread. You can use a single ExecutorService
to launch many threads. The below code demonstrates.
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
public class TestSync {
private volatile Integer amount = 0;
private final Object LOCK_OBJECT = new Object();
private final Object FULL = new Object();
public void doubleSync() throws InterruptedException {
System.out.println("Producer trying to get LOCK_OBJECT lock ");
synchronized (LOCK_OBJECT) {
System.out.println("Producer get LOCK_OBJECT lock ");
Print.sleep(3000);
while (amount >= 0) {
synchronized (FULL) {
System.out.println("full!");
FULL.wait();
}
}
System.out.println("continue~");
amount++;
}
}
public void simpleSync() {
System.out.println("Consumer trying to get LOCK_OBJECT lock ");
synchronized (LOCK_OBJECT) {
System.out.println("Consumer get LOCK_OBJECT lock");
}
System.out.println("Consumer release LOCK_OBJECT lock");
}
public static void main(String[] args) throws InterruptedException {
AtomicInteger atomicInteger = new AtomicInteger(0);
ExecutorService threadPool = Executors.newFixedThreadPool(2, new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
int index = atomicInteger.incrementAndGet();
System.out.println("create no " + index + " thread");
Thread t = new Thread(r, "Thread-" + index);
return t;
}
});
TestSync ts = new TestSync();
threadPool.execute(() -> {
try {
ts.doubleSync();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Print.sleep(1000);
threadPool.execute(() -> {
try {
ts.simpleSync();
} catch (Exception e) {
e.printStackTrace();
}
});
threadPool.shutdown();
}
}
class Print {
public static void sleep(long interval) {
System.out.println("sleep " + interval);
}
}
Note that it is recommended to call method shutdown()
, of interface ExecutorService
, after you have submitted all your tasks.
Here is the output when I run the above code.
create no 1 thread
Producer trying to get LOCK_OBJECT lock
Producer get LOCK_OBJECT lock
sleep 1000
create no 2 thread
Consumer trying to get LOCK_OBJECT lock
sleep 3000
full!
As you can see, the consumer does not obtain the lock.
Upvotes: 1