user3717434 user3717434 - 7 months ago 6
Java Question

how to create threads to avoid IllegalThreadStateException

It's a multithreading exercise in which i use a Monitor to synchronize threads. I don't have problem with monitor or syncronization, there is no deadlock. I want to create some threads in the main. Code below generates threads, adds them to an ArrayList and starts them. Generates different threads with a switch-case. Since every thread should be added to ArrayList and started, i put these two lines at the end of switch-case, to not write same code in every case-state. But in this way it launches an IllegalThreadStateException.

To make my code work, i can apply different approaches, but i have some doubts for all of them. Which one would be the most appropriate way to do it?

Creating a function which will create a new myThread instance, add it to the ArrayList and start it. But since i have to call it from main, or it should be static(as i know, creating a static function without a good reason is not a good practice) or i should call it like

new myClass().someMethod()
but since i have to create many new threads, it would create many instances of myClass, not seem good.

public class myClass {

public static void main(String[] args) {

int scount=10, tcount=5, pcount=5;
final int SIZE = 20;


ArrayList<User> users = new ArrayList<User>();
myMonitor monitor = new myMonitor(SIZE);
User u = null;
int s = 0, t = 0, p = 0; //counters

//GENERATED CASUALLY DIFFERENT TYPE OF THREADS
while(s < scount || t < tcount || p < pcount){

int type = (int)(Math.random() * 3);

switch(type){
case 0:
if(p < pcount){
u = new User(monitor, p, "USER_TYPE_1");
p++;
}
break;
case 1:
if(t < tcount){
u = new User(monitor, p, "USER_TYPE_2");
t++;
}
break;
case 2:
if(s < scount){
u = new User(monitor, p, "USER_TYPE_2");
s++;
}
break;
}
users.add(u);
u.start();
}

}

}





public class User extends Thread{
myMonitor monitor;
final private int number;
final private String type;
final private int k;
final private int MIN = 1;
final private int MAX = 5;


public User(myMonitor monitor, int number, String type) {
this.monitor = monitor;
this.number = number;
this.type = type;
this.k = (int)(Math.random() * ((MAX - MIN) + 1)) + MIN;
}

public int getNumber() {
return number;
}

public String getType() {
return type;
}

@Override
public void run(){

for(int i=0; i<k; i++){

switch(this.type){
case "TYPE1":
monitor.startType1();
break;
case "TYPE2":
monitor.startType2(i);
break;
case "TYPE3":
monitor.startType3();
break;
}

try{

Long duration = (long) Math.ceil(Math.random() * 1000);
Thread.sleep(duration);
System.out.printf("%s-%d used system for the %d.time. Took %d ms\n",
this.type, this.number, i+1, duration);
} catch (InterruptedException e) {
e.printStackTrace();
}

switch(this.type){
case "TYPE1":
monitor.endType1();
break;
case "TYPE2":
monitor.endType2(i);
break;
case "TYPE3":
monitor.endType3();
break;
}

try{
Long duration = (long) Math.ceil(Math.random() * 1000);
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}

}
System.out.printf("%s-%d finished\n", this.type, this.number);
}


}

Answer

Lets carry out a thought experiment, Math.random * 3 always returns 0 - this is possible because it's obviously random (just unlikely).

Iteration 1

int s == t == p == 0 

We enter the first case, as type == 0. We generate a new "P" Thread and add it to the List and start() it.

p++

Iteration 2

int s == t == 0; p == 1 

We enter the first case, as type == 0. We generate a new "P" Thread and add it to the List and start() it.

p++

...

Iteration 5

int s == t == 0; p == 4 

We enter the first case, as type == 0. We generate a new "P" Thread and add it to the List and start() it.

p++

Iteration 6

int s == t == 0; p == 5

We enter the first case, as type == 0. We do nothing as p >= pcount. Our thrd still points to the Thread we created in Iteration 5.

We add the same Thread to the List and start() it.

IllegalThreadStateException

Now, obviously Math.random * 3 will return different values but it will return duplicates (and your code is designed around that) - so you will get this situation.

How to avoid it? Well, you do not actually want to generate random numbers:

final List<Integer> desiredValues = new ArrayList<>(Arrays.asList(0,0,0,0,0,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2));
Collections.shuffle(desiredValues)
for(final Integer value : desiredValues) {
    //case switch
}
Comments