我有两个线程都访问一个向量。 t1添加一个随机数,而t2删除并显示第一个数字。下面是代码和输出。 t2似乎只执行一次(在t1开始之前)并永远终止。我在这里想念什么吗? (PS:也经过ArrayList测试)

import java.util.Random;
import java.util.Vector;

public class Main {

public static Vector<Integer> list1 = new Vector<Integer>();

public static void main(String[] args) throws InterruptedException {
    System.out.println("Main started!");

    Thread t1 = new Thread(new Runnable() {

        @Override
        public void run() {
            System.out.println("writer started! ");
            Random rand = new Random();

            for(int i=0; i<10; i++) {
                int x = rand.nextInt(100);
                list1.add(x);
                System.out.println("writer: " + x);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }

    });

    Thread t2 = new Thread(new Runnable() {

        @Override
        public void run() {
            System.out.println("reader started! ");
            while(!list1.isEmpty()) {

                int x = list1.remove(0);
                System.out.println("reader: "+x);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }

    });


    t2.start();
    t1.start();

    t1.join();
    t2.join();
}

}
输出:
主要开始!
读者开始了!
作家开始了!
作家:40
作者:9
作家:23
作者:5
作者:41岁
作家:29
作者:72
作者:73
作者:95
作者:46

最佳答案

这听起来像是了解并发性的玩具,所以我之前没有提到它,但是现在我会提到(在顶部,因为它很重要)。

如果这是生产代码,请不要自己动手编写代码。 java.util.concurrent中有很多执行良好(调试)的并发数据结构。使用它们。

消费时,您无需基于“所有消费项目”关闭消费者。这是由于竞争状况造成的,消费者可能会“领先于生产者”并仅由于生产者尚未写出要消费的物品而检测到空列表。

有多种方法可以关闭使用者,但通过孤立地查看要消耗的数据,它们都无法完成。

我的建议是,当生产者完成生产时,生产者“向消费者发出信号”。然后,当消费者同时拥有“信号”和不再生成任何数据且列表为空时,它将停止。

替代技术包括创建“关机”项目。 “生产者”添加了关闭项,而消费者仅在看到“关闭”项时才关闭。如果您有一组使用者,请记住,您不应该删除关机项(否则只有一个使用者会关机)。

同样,消费者可以“监视”生产者,以便如果生产者“活跃/存在”并且列表为空,则消费者会假设会有更多数据可用。当生产者已死/不存在并且无可用数据时,将发生关机。

您使用哪种技术取决于您喜欢的方法和您要解决的问题。

我知道人们喜欢优雅的解决方案,但是如果您的单个生产者知道单个消费者,那么第一个选择就是。

public class Producer {

   public void shutdown() {
      addRemainingItems();
      consumer.shutdown();
   }
}

消费者看起来像{
public class Consumer {

   private boolean shuttingDown = false;

   public void shutdown() {
     shuttingDown = true;
   }

   public void run() {
     if (!list.isEmpty() && !shuttingDown) {
        // pull item and process
     }
   }
}

请注意,缺少对列表中项目锁定的本质上是危险的,但是您只声明了一个使用者,因此没有争用列表中的内容的争执。

现在,如果您有多个使用者,则需要提供保护以确保单个线程不会同时被两个线程拉出(并且需要以关闭所有线程的方式进行通信)。

07-28 01:58
查看更多