private static volatile AtomicInteger blockClientCount = new AtomicInteger(0);
private static Object lock = new Object();
private static Lock reentrantLock = new ReentrantLock();
private static Condition condition = reentrantLock.newCondition();
#Override
public void run() {
Random random = new Random(this.hashCode());
while (true) {
String request = random.nextInt(10) + "" + IOUtil.operators[random.nextInt(4)] + (random.nextInt(10) + 1);
System.out.println(this.getName() + " send request:" + request);
if (socketConnect()) {
BufferedReader in = null;
PrintWriter out = null;
try {
out = new PrintWriter(socket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
blockClientCount.incrementAndGet();
reentrantLock.lock();
try {
while (blockClientCount.get() % IOUtil.CLIENNT_NUM != 0) {
/**
* TODO java.lang.IllegalMonitorStateException
*/
condition.wait();
}
condition.signalAll();
} finally {
reentrantLock.unlock();
}
// synchronized (lock) {
// while (blockClientCount.get() % IOUtil.CLIENNT_NUM != 0)
// {
// lock.wait();
// }
// lock.notifyAll();
// }
out.println(request);
String response = in.readLine();
System.out.println(this.getName() + " accept response:" + response);
} catch (final Exception e) {
e.printStackTrace();
} finally {
IOUtil.close(in);
IOUtil.close(out);
}
}
try {
sleep(1000);
} catch (InterruptedException e) {
System.out.println(this.getName() + " was interrupted and stop");
IOUtil.close(socket);
break;
}
}
}
the java.lang.IllegalMonitorStateException will happen when i use ReentrantLock to wait, but it works well when i use synchronized , i can'not explain it.
The reason is that reentrantLock is unlock before condition.wait(),but i don't know why.
if someone can help me,i have submit the code to https://github.com/shanhm1991/demo_io.git
That's because you're calling the wrong method. You should call await() rather than wait() on a Condition object:
reentrantLock.lock();
try {
condition.await();
} finally {
reentrantLock.unlock();
}
You're currently calling wait which is the Object.wait() method, which requires you to be synchronized on the object that you are calling it on. But that's not what you want, you want the specific wait functionality of the Condition class, and that is only available through the method await.
Related
I am trying to create basic producer/consuner class using:
public class ProducerConsumer {
private final static int MAX_SIZE = 100;
private Queue<String> data = new PriorityQueue<>();
private Lock lock = new ReentrantLock();
private Condition bufferFull = lock.newCondition();
private Condition bufferEmpty = lock.newCondition();
public void produce(){
while(true) {
try {
lock.lock();
while (data.size() >= MAX_SIZE) {
bufferFull.await();
}
addData();
bufferEmpty.notifyAll();
} catch (InterruptedException e) {
System.out.println("error produce");
} finally {
lock.unlock();
}
}
}
public void consume(){
while(true) {
try {
lock.lock();
while (data.isEmpty()) {
bufferEmpty.await();
}
String value = data.poll();
System.out.println("Thread " + Thread.currentThread().getName() + " processing value " + value);
bufferFull.notifyAll();
} catch (InterruptedException e) {
System.out.println("error consume");
} finally {
lock.unlock();
}
}
}
private void addData(){
IntStream.range(0,10).forEach( i ->
data.add(new Date().toString())
);
}
public void start(int consumerNumber){
IntStream.range(0,consumerNumber)
.mapToObj(i -> new Thread(this::consume))
.collect(Collectors.toList())
.forEach(Thread::start);
Thread t = new Thread(this::produce);
t.start();
}
}
However it keeps throwing error: java.lang.IllegalMonitorStateException. My question is, why does it throw this error? method of this intance are running in threads, so they should own condition lock thus i dont understand meaning behind this error.
Thanks for help!
bufferEmpty.notifyAll() is the wrong method to call. That method requires you hold the monitor on the "bufferEmpty" object itself, which is unrelated to the lock instance you're using.
The right method to call is
bufferEmpty.signalAll();
public class SemActionPlace {
public SemMonitor StartConsumerProducer() {
SemMonitor monitor = new SemMonitor();
List<Thread> threads = new LinkedList<>();
Thread p1 = new Thread(new Producer(monitor), "P1");
p1.start();
Thread c1 = new Thread(new Consumer(monitor), "C-odd");
c1.start();
Thread c2 = new Thread(new Consumer(monitor), "C-even");
c2.start();
threads.add(p1);
threads.add(c1);
threads.add(c2);
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return monitor;
}
}
the code work just fine when I start thread through start() - join(), however, I failed to find mistake when I try to do the same through the executor service. It's important for me to save names of the threads and mutual monitor. Please, tell me how can I execute the threads through the executor service ?
The piece of code below doen't work properly. Where is mistake ?
public SemMonitor StartConsumerProducer() {
SemMonitor monitor = new SemMonitor();
Thread p1 = new Thread(new Producer(monitor), "P1");
Thread c1 = new Thread(new Consumer(monitor), "C-odd");
Thread c2 = new Thread(new Consumer(monitor), "C-even");
ThreadPoolExecutor service = (ThreadPoolExecutor) Executors.newFixedThreadPool(3);
service.execute(p1);
service.execute(c1);
service.execute(c2);
System.out.println(service.getCompletedTaskCount());
System.out.println(service.getCompletedTaskCount());
return monitor;
}
I need one simple thing from the executor server is that I wanna that it works like simple start() - join() solution works ( first piece of code ) .
class Consumer implements Runnable {
private final SemMonitor monitor;
Consumer(SemMonitor monitor) {
this.monitor = monitor;
}
#Override
public void run() {
long t = System.currentTimeMillis();
long end = t + 1000;
while (System.currentTimeMillis() < end) {
consoleLog(monitor.activeThreadName,false);
if (/*monitor.semaphore.tryAcquire() && */monitor.activeThreadName.equals( Thread.currentThread().getName())) {
try {
consoleLog(String.valueOf(Thread.currentThread().getName() + " was notified "),monitor.enableLog);
monitor.semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
monitor.get(Thread.currentThread().getName());
}
try{
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Producer implements Runnable {
private SemMonitor monitor;
Producer(SemMonitor monitor) {
this.monitor = monitor;
}
#Override
public void run() {
String threadNameToWork;
Integer randNum;
long t = System.currentTimeMillis();
long end = t + 500;
while (System.currentTimeMillis() < end) {
if (monitor.semaphore.tryAcquire()) {
randNum = ((Number) (random() * 100)).intValue();
if (randNum % 2 == 0) {
threadNameToWork = "C-even";
} else {
threadNameToWork = "C-odd";
}
try {
monitor.putItem(randNum, Thread.currentThread().getName(), threadNameToWork);
Thread.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
class Monitor {
private double currItem;
private boolean isConsumersShouldWaitProducer = true;
private boolean isConsuming = false;
private String threadNameToWork;
synchronized void putRandNumber(double producerOutput, String producerName, String threadNameToWork) {
if (isConsumersShouldWaitProducer) {
System.out.println("Consumers wait for new Production");
}
this.threadNameToWork = threadNameToWork;
currItem = producerOutput;
System.out.println("Producer " + producerName + " putRandNumber Item: " + currItem);
if (currItem > 3) {
notifyAll();
isConsumersShouldWaitProducer = false;
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
synchronized void consumeRandNumber(String threadName) {
if (isConsumersShouldWaitProducer) {
try {
this.wait();
} catch (InterruptedException e) {
System.out.println("Caught Interrupted Exception while waiting to consume currItem: " + e.getMessage());
}
}
if (isConsuming) {
try {
this.wait();
isConsuming = true;
} catch (InterruptedException e) {
System.out.println("Caught Interrupted Exception while waiting to consume currItem: " + e.getMessage());
}
}
switch (Thread.currentThread().getName()) {
/*switch (threadNameToWork) {*/
case "C-odd":
isConsuming = true;
if (currItem % 2 != 0 && threadNameToWork.equals(Thread.currentThread().getName())) {
consumeItems(threadName);
}
isConsuming = false;
notifyAll();
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
case "C-even":
isConsuming = true;
if (currItem % 2 == 0 && threadNameToWork.equals(Thread.currentThread().getName())) {
consumeItems(threadName);
}
isConsuming = false;
notifyAll();
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
default:
break;
}
}
private synchronized void consumeItems(String threadName) {
isConsumersShouldWaitProducer = true;
String randNumType = "*odd/even*";
System.out.println("Consumer:" + threadName + " consumed " + randNumType + " Items = " + currItem);
notifyAll();
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
You want to do something with Thread name right? The thread name you created in using new Thread will not pass into ExecutorService, but this will
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("thread-%d").build()
Then
ExecutorService exec = Executors.newSingleThreadExecutor(namedThreadFactory);
Now you have thread with name as thread-1, thread-2
OR set thread name in your run() method
Thread.currentThread().setName(myName)
To make sure your thread is finished, add this before you return the monitor,
service.shutdown();
while (!service.awaitTermination(10, TimeUnit.SECONDS)) {
log.info("Awaiting completion of threads.");
}
the code at the bottom (1) was just for exercising but makes me curious why always the same Thread and only this Thread is able to receive the resource made by the producer. When I update the producer part with a Thread.sleep in my code like (2) all Threads get the resource randomly (I think). But why? Does the Sheduler works with LIFO? Or whats the "problem" here?
1
public class ProducerConsumer {
private static int resource = 0;
private static AtomicInteger id = new AtomicInteger();
public static void main(String... args) throws InterruptedException {
final Object monitor = new Object();
Runnable producer = () -> {
try {
while (!Thread.interrupted()) {
// produce number
println("producing ...");
int number = (int) (Math.random() * 1000) + 1;
Thread.sleep(number);
println("produced " + number);
// send number
synchronized (monitor) {
resource = number;
println("notified");
monitor.notifyAll();
}
}
} catch (InterruptedException e) {
println("interrupted");
}
};
Runnable consumer = () -> {
final int innerId = id.getAndIncrement();
try {
while (!Thread.interrupted()) {
// receive number
int number;
synchronized (monitor) {
while (resource == 0) {
println(innerId + " waiting ...");
monitor.wait();
println(innerId + " woke up ...");
}
number = resource;
resource = 0;
}
// consume number
println("consumed " + number);
}
} catch (Exception e) {
println("interrupted");
}
};
new Thread(producer).start();
new Thread(consumer).start();
new Thread(consumer).start();
new Thread(consumer).start();
new Thread(consumer).start();
Thread.sleep(10_000);
Thread.currentThread().getThreadGroup().interrupt();
}
}
2
Runnable producer = () -> {
try {
while (!Thread.interrupted()) {
// produce number
println("producing ...");
final int number = (int) (Math.random() * 1000) + 1;
Thread.sleep(number);
println("produced " + number);
synchronized (monitor) {
setResource(number);
println("notified");
Thread.sleep(100);
monitor.notifyAll();
}
}
} catch (InterruptedException e) {
println("interrupted");
}
};
It is pseudo-random.
From the notifyAll doc:
The awakened threads will compete in the usual manner with any other threads that might be actively competing to synchronize on this object; for example, the awakened threads enjoy no reliable privilege or disadvantage in being the next thread to lock this object.
The implementation seems to fairly reliable (not random) in your case, but like the documentation says, it is not 100% reliable.
First of all, this is not a homework.
I have written a piece of code so that:
Thread-1 prints 1,4,7,... (diff is 3)
Thread-2 prints 2,5,8,...
Thread-3 prints 3,6,9,...
And the final output should be:
1,2,3,4,5,6,7,8,9,...
Here's the code that works wonderfully well:
package threadAlgo;
public class ControlOrder {
volatile Monitor monitor = new Monitor();
public static void main(String[] args) {
ControlOrder order = new ControlOrder();
Thread one = new Thread(new Task(order.monitor, 1));
one.setName("Thread-1");
Thread two = new Thread(new Task(order.monitor, 2));
two.setName("Thread-2");
Thread three = new Thread(new Task(order.monitor, 3));
three.setName("Thread-3");
one.start();
two.start();
three.start();
}
}
class Monitor {
int threadNumber = 1;
}
class Task implements Runnable {
private Monitor monitor;
private int myThreadNumber;
private int currentCount;
Task(Monitor monitor, int myThreadNumber) {
this.monitor = monitor;
this.myThreadNumber = myThreadNumber;
this.currentCount = myThreadNumber;
}
#Override
public void run() {
while (true) {
while (monitor.threadNumber != myThreadNumber) {
synchronized (monitor) {
try {
monitor.wait(100); //DOESN'T WORK WITHOUT THE TIMEOUT!!!
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
synchronized (monitor) {
if (monitor.threadNumber == myThreadNumber) {
System.out.println(Thread.currentThread().getName() + ": " + currentCount);
currentCount = currentCount + 3;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (myThreadNumber == 3) {
monitor.threadNumber = 1;
} else {
monitor.threadNumber = myThreadNumber + 1;
}
monitor.notifyAll();
}
}
}
}
The only problem is that if I use wait() instead of wait(timeout), then the thread halts.
UPDATE:
Wait condition (while loop) should be inside synchronized block. A lesson for beginners, including me.
You should always
perform notifyAll/notify in conjunction with a change in state.
check the state change before using wait() in a loop.
If you call notify() and no wait() is waiting, then the signal is lost, so unless you check a state change, (or timeout) you can block forever waiting for a signal which doesn't change.
I have a deposit function which is called by multiple clients at same time. I want the deposit function synchronized when same client (say client with id=someUniqueNo) calls it again but not synchronized when other client else call it ? How can I achieve this.
Using Spring framework in the project , Its cool even if any solution with the framework provides for such pattern.
Would something like this work?
private Map<String, ReentrantLock> lockMap = new HashMap<String, ReentrantLock>();
public void deposit(String clientId) {
updateLockMap(clientId);
ReentrantLock lock = lockMap.get(clientId);
try {
if(lock.tryLock(1, TimeUnit.MINUTES)) {
// do deposit stuff
}
} catch (InterruptedException e) {
// log
} finally {
lock.unlock();
}
}
private void updateLockMap(String clientId) {
if(!lockMap.keySet().contains(clientId)) {
lockMap.put(clientId, new ReentrantLock());
}
}
public void deposit(String clientId){
String id = clientId;
while(id.equals("someClientID")){
synchronized(lockObject){
// Do your stuff here in a sync'd way
}
}
else
{
// Do your stuff here in a NON synch'd way
}
}
Second attempt : ( not tested !)
public void deposit(String clientId){
String id = clientId;
// callers is a ConcurrenthashMap<String,String>
while(callers.get(id) != null){
synchronized(callers.get(id)){ // client already in call, so wait
wait();
}
}
callers.put(id,id); // client is ready to run a new call, so setup
// Do your stuff here in a NON synch'd way
synchronized(callers.get(id)){ // client done with a call ,
//so notify others who are calling
callers.put(id,null);
notifyAll();
}
}
Third attempt ( tested and working correctly )
public class SyncTest {
static int[] balances = new int[]{ 1,10,100,1000,10000};
public static void main(String[] args) {
// TODO Auto-generated method stub
SyncTest test = new SyncTest();
Client c1 = new Client("0", test,1);
Client c2 = new Client("1", test,10);
Client c3 = new Client("2", test,100);
Client c4 = new Client("3", test,1000);
Client c5 = new Client("4", test,10000);
Thread[] threads = new Thread[5];
threads[0] = new Thread(c1);
threads[1] = new Thread(c2);
threads[2] = new Thread(c3);
threads[3] = new Thread(c4);
threads[4] = new Thread(c5);
for(int i=0;i<5;i++){
threads[i].start();
}
for(int i=0;i<5;i++){
try{
threads[i].join();
}
catch(InterruptedException ex){
}
}
System.out.println("Final balances are ");
for(int i=0;i<5;i++){
System.out.print(balances[i] + " , " );
}
}
ConcurrentHashMap<String,String> callers = new ConcurrentHashMap<String, String>();
public void deposit(Client c) throws InterruptedException{
String id = c.id;
int amount = c.amount;
if(id == null) return ;
// callers is a ConcurrenthashMap<String,String>
System.out.println("Client " + id + " in deposit. Checking if already running..");
while( callers.get(id) != null && !(callers.get(id).equals("X"))) {
synchronized(id){
System.out.println("Client " + id + " in deposit. Found already running,going to wait..");
id.wait();
}
}
if(callers == null){
throw new NullPointerException("Callers is null!!");
}
System.out.println("Client " + id + " in deposit. Found not running already, Now running..");
callers.put(id,id);
int index = Integer.parseInt(id);
balances[index] += amount;
Thread.sleep(2000);
synchronized(id){
String old = callers.put(id,"X");
id.notifyAll();
System.out.println("Client " + id + " in deposit. Finished running , Notifying..");
}
}
static class Client implements Runnable {
String id ;
SyncTest test;
int amount;
Client c = this;
Client(String id , SyncTest t, int am){
this.id = id;
test = t;
amount = am;
}
public void run(){
process();
}
void process(){
System.out.println("Client " + id + " processing ...");
try{
Thread.sleep(1000);
Thread t1 = new Thread(
new Runnable(){
public void run(){
try{
test.deposit(c);
}
catch(InterruptedException ex){
}
}
}
);
Thread t2 = new Thread(
new Runnable(){
public void run(){
try{
test.deposit(c);
}
catch(InterruptedException ex){
}
}
}
);
Thread t3 = new Thread(
new Runnable(){
public void run(){
try{
test.deposit(c);
}
catch(InterruptedException ex){
}
}
}
);
Thread t4 = new Thread(
new Runnable(){
public void run(){
try{
test.deposit(c);
}
catch(InterruptedException ex){
}
}
}
);
/* Thread t5 = new Thread(
new Runnable(){
public void run(){
try{
test.deposit(c);
}
catch(InterruptedException ex){
}
}
}
);
Thread t6 = new Thread(
new Runnable(){
public void run(){
try{
test.deposit(c);
}
catch(InterruptedException ex){
}
}
}
);
Thread t7 = new Thread(
new Runnable(){
public void run(){
try{
test.deposit(c);
}
catch(InterruptedException ex){
}
}
}
);
Thread t8 = new Thread(
new Runnable(){
public void run(){
try{
test.deposit(c);
}
catch(InterruptedException ex){
}
}
}
);
*/
t1.start();t2.start();t3.start();t4.start();//t5.start();t6.start();t7.start();t8.start();
t1.join();t2.join();t3.join();t4.join();//t5.join();t6.join();t7.join();t8.join();
}
catch(InterruptedException ex){
System.out.println(" Exception " + ex.getMessage());
}
System.out.println("Client " + id + " done #####");
}
}
}
Not with the synchronized keyword, that's for sure. The built-in synchronization primitives know how to adjudicate between different threads and nothing more - they can't make the decision based on the value of parameters to a method. What you want to do is a synchronized message queue for each client or something equivalent, and have your function called only from that queue.
I would disagree with your assumption that two threads can access the same value without both being synchronized (or exclusively locked in some way).
If you have an update happening then you must ensure that all methods for updating AND READING the values are exclusively locked, otherwise chaos ensues. If you don't have these locks, then you can get partial or incorrect results.
Say you have a process like:
Update list of transactions
Update balance
Thread 1 starts the process, and does 1. The second thread READS (just reads) the list of transactions and balance. Thread 2 completes the process, and finishes.
Now thread 2 has an inconsistent view of the state of the account. The list does not match the balance. This is just an example, but it illustrates the problem.
It does not matter whether the first caller calls the request multiple times or you have multiple callers. All accesses to your data need to be synchronized. If you are in the process of changing something, then you need to present a consistent view to everyone, which means synchronizing.
Use a lock per client. That is, instead of synchronizing the method directly use explicit locking through lock objects. Arrange it such that you have one lock per client which you use to synchronize access to the deposit function.
You can do:
Client lock = yourClient;
synchronized (lock) {
}
Obtain the lock on the client same reference.