Java多线程 - 从列表中删除项目

时间:2016-12-10 20:12:00

标签: java multithreading arraylist

这里有多线程新手,请耐心等待。 我正在尝试运行2个线程,从任务列表中删除项目(总共10个任务),直到taskList为空。

到目前为止我所拥有的是:

主要方法:

char findMostFrequentCharacter(char c)

任务管理员课程:

char findMostFrequentCharacter(char c[10]) {
  int counts[256]; // Assuming these are ASCII characters, we can have max 256 different values
  // Fill it with zeroes (you can use memset to do that, but for clarity I'll write a for-loop
  for (int i = 0; i < 256; i++) c[i] = 0;

  // Do the actual counting
  for (int i = 0; i < 10; i++) // For each character
    counts[ c[i] ]++;  // Increase it's count by 1, note that c[i] is going to have values between 65 (upper case A) and 122 (lower case z)

  char mostFrequent = 0;
  // Find the one that is most frequent

  for (char ch = 'A'; ch <= 'z' ch++) // This will ensure that we iterate over all upper and lower case letters (+ some more but we don't care about it)
    if (counts[ch] > counts[c]) c = ch;  // Take the one that is more frequent, note that in case they have the same count nothing will happen which is what we want since we are iterating through characters in alphabetical order
  return c;
}

问题是,它要么运行2次,要么总是运行10次相同的线程。我知道这可能是一个愚蠢的问题,但我希望有人可以澄清它!提前致谢

编辑:我能够使用@Lidae的建议使其工作

采取如此编辑的任务方法:

public static void main(String[] args) {


        List<Task> taskList = new ArrayList<Task>();
        List<Thread> threadList = new ArrayList<Thread>();


        for (int i = 1; i <= 10; i++) {

            taskList.add(new Task("some details");
        }

        TaskManager manager = new TaskManager();
        gestor.setTaskList(taskList);

        Thread t1 = new Thread(taskManager);            
        Thread t2 = new Thread(taskManager);    

        threadList.add(t1);
        threadList.add(t2);

        if(threadList.size() > 0){
            for (Thread thread : threadList){                               
                thread.start();             
            }
        }   

        for (Thread thread : threadList){
            try {
                thread.join();
            } catch (InterruptedException e) {
                System.out.println("thread " + Thread.currentThread().getName() + " was interrupted");
            }
        }

        System.out.println("END OF MAIN");

    }

4 个答案:

答案 0 :(得分:2)

由于多个线程试图同时访问同一个对象,因此您的代码存在一些并发问题。例如,可能发生的一件事是线程A从列表中获取任务(在Task task = availableTasks.get(index)中),然后有一个上下文切换,并且线程B删除了该任务,并且当线程A尝试删除任务,它已经消失了(这不会导致你的代码中出现异常,但无论如何,这可能都很糟糕,具体取决于你计划完成任务的具体内容。)

当您尝试从中获取任务时,您也无法确定列表是否为空:它在上一次检查while循环时是空的,但是在它之间和它尝试的时间之间承担任务,另一个线程可能已经完成了最后一项任务。即使您删除了对Thread.sleep的调用,也是如此。

您需要确保只有一个线程一次修改availableTasks列表。这可以通过各种方式完成,例如通过使用信号量,或者使用共享数据对象中的同步方法。

答案 1 :(得分:1)

  

它始终是运行10次的同一个线程

我的猜测是因为你的列表太小,所以第一个线程运行并在第二个线程有机会开始工作之前完成工作。使任务列表更长,例如1000多个任务,甚至更多。

  

它要么运行2次

这可能是因为您的任务列表不是线程安全的,使用Collections.SynchronizedList使其线程安全

    for (int i = 1; i <= 10; i++) {

        taskList.add(new Task("some details");
    }

    taskList = Collections.synchronizedList(taskList);

    TaskManager manager = new TaskManager();

答案 2 :(得分:1)

无法重现这一点。

我已经纠正了(很多)您的代码的编译问题并运行它,获得:

Thread-1 printing some details for task 5
Thread-0 printing some details for task 8
Thread-0 printing some details for task 2
Thread-1 printing some details for task 7
Thread-1 printing some details for task 1
Thread-0 printing some details for task 3
Thread-0 printing some details for task 6
Thread-1 printing some details for task 9
Thread-0 printing some details for task 4
Thread-1 printing some details for task 0

因此两个线程都运行并处理任务。

重要的一点是应该同步对任务列表的访问。而且不只是Collections.synchronizedList,您至少有四个地方可以访问您的任务列表。这就是为什么程序的执行几乎总是以:

结束
java.lang.IllegalArgumentException: n must be positive
    at java.util.Random.nextInt(Random.java:250)
    at TaskManager.takeTask(TaskManager.java:25)
    at TaskManager.run(TaskManager.java:18)
    at java.lang.Thread.run(Thread.java:662)

您的TaskManager.run方法首先检查isEmpty,然后从列表中获取随机任务。另一个线程可以在这两个操作之间删除列表的最后一个任务。导致random.nextInt(0) 尽管您之前已检查过该列表不为空。

更好的是:

private Task nextTask() {
    synchronize(availableTask) {
        if (availableTask.isEmpty()) {
            return null;
        } else {
            return availableTasks.get(random.nextInt(availableTasks.size()));
        }
    }
}

答案 3 :(得分:0)

添加@Lidae的回答。它是多用户问题的标准多生产者。同时有几篇文章..