如何创建线程以避免IllegalThreadStateException

时间:2016-04-26 16:12:27

标签: java multithreading

这是一个多线程练习,我使用Monitor来同步线程。我没有监视器或同步问题,没有死锁。我想在main中创建一些线程。下面的代码生成线程,将它们添加到ArrayList并启动它们。使用switch-case生成不同的线程。由于每个线程都应该添加到ArrayList并启动,我将这两行放在switch-case的末尾,以便不在每种case-state中编写相同的代码。但是以这种方式它会启动IllegalThreadStateException。

为了使我的代码有效,我可以应用不同的方法,但我对所有这些都有一些疑问。哪一个是最合适的方式呢?

创建一个将创建新myThread实例的函数,将其添加到ArrayList并启动它。但是因为我必须从main调用它,或者它应该是静态的(正如我所知,创建一个没有充分理由的静态函数不是一个好习惯)或者我应该像new myClass().someMethod()那样称它,但是因为我必须创建许多新线程,它会创建许多myClass实例,看起来不太好。

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);
}

}

2 个答案:

答案 0 :(得分:0)

即使未创建新主题,您也会调用thrd.start();。因为你已经在循环之外声明了变量,所以它仍然会引用先前启动的线程。在线程上调用start()两次将导致异常。

myThread thrd = null;循环内移动while,如果它不为空,则只调用start();

答案 1 :(得分:0)

让我们进行思考实验,Math.random * 3始终返回0 - 这是可能的,因为它显然是random(只是不太可能)。

迭代1

int s == t == p == 0 

我们输入第一个案例,type == 0。我们生成一个新的&#34; P&#34; Threadadd Liststart()

p++

迭代2

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

我们输入第一个案例,type == 0。我们生成一个新的&#34; P&#34; Threadadd Liststart()

p++

...

迭代5

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

我们输入第一个案例,type == 0。我们生成一个新的&#34; P&#34; Threadadd Liststart()

p++

迭代6

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

我们输入第一个案例,type == 0我们没有做任何事情,因为p&gt; = pcount 。我们的thrd仍然指向我们在迭代5 中创建的Thread

我们将相同的Thread 添加到Liststart()

IllegalThreadStateException

现在,显然Math.random * 3将返回不同的值,但它会返回重复项(并且您的代码是围绕该代码设计的) - 所以得到这种情况。

如何避免它?好吧,你实际上想要生成随机数:

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
}