使用两个线程

时间:2015-10-27 19:22:37

标签: java multithreading concurrency

我有两个线程,它们都访问Vector。 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

3 个答案:

答案 0 :(得分:4)

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

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

消费时,您不需要根据&#34;消耗的所有物品&#34;关闭您的消费者。这是由于竞争条件导致消费者可能提前比赛&#34;生产者和检测空列表只是因为生产者还没有写下要消费的物品。

有许多方法可以完成对消费者的关闭,但是没有一种方法可以通过查看要单独使用的数据来完成。

我的建议是制作人&#34;发出信号&#34;生产者完成生产时的消费者。然后,当消费者同时拥有&#34;信号&#34;不再生成数据且列表为空。

替代技术包括创建&#34;关闭&#34;项目。 &#34;制作人&#34;添加关闭项目,而消费者仅在&#34;关闭&#34;项目被看到了。如果您有一组消费者,请记住您不应该删除关闭项目(或者只有一个消费者会关闭)。

此外,消费者可以监控&#34;生产者,如果生产者是&#34;活着/存在&#34;并且列表为空,消费者假定有更多数据可用。当生产者死亡/不存在且没有数据可用时,就会发生关机。

您使用哪种技术取决于您偏好的方法以及您尝试解决的问题。

我知道人们喜欢优雅的解决方案,但如果您的单个制作人知道单个消费者,那么第一个选项就像。

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

请注意,列表中项目之间缺乏锁定本质上是危险的,但您只说了一个消费者,因此没有争用从列表中读取。

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

答案 1 :(得分:0)

我认为这是典型的Producer–consumer problem。试着看看Semaphore

答案 2 :(得分:0)

更新:在消费者(读者)中更改while循环后问题消失了。如果列表为空,它现在进入循环但不做任何事情,而不是退出线程。以下是更新的读者线程。当然,也可以在Edwin建议的代码中添加一个不错的关闭机制。

        public void run() {
        System.out.println("reader started! ");         
        while(true) {
            if(!list1.isEmpty()) {
                int x = list1.remove(0);
                System.out.println("reader: "+x);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }               
    }   

请注意,这不是从真实产品中获取的代码段,也不会包含在其中!