向主线程发出信号线程错误

时间:2016-12-04 20:02:05

标签: java multithreading threadpool

我在Java中有一个线程,它连接到套接字并将信息发送到另一个处理该信息的线程。

现在,如果"制作人"线程因任何原因失败,我希望整个程序停止,因此必须发生某种通知。

这是我的计划(非常简化):

public class Main {
  private Queue<String> q = new ConcurrentLinkedQueue();

  public static void main(String[] args) throws IOException {
    new Thread(new Producer(q)).start();
    new Thread(new Consumer(q)).start();

    // Catch any error in the producer side, then stop both consumer and producer, and do some extra work to notify me that there's an error...
  }
}

主代码只创建一个共享队列,并启动生产者和消费者。到目前为止,我猜它还可以吗?现在Producer代码是这样的:

public class Producer implements Runnable {
  private Queue<String> q;

  public Producer(Queue<String> q) {
    this.q = q;
  }

  public void run() {
    try {
      connectToSocket();
      while(true) {
        String data = readFromSocket()
        q.offer(data);
      }
    } catch (Exception e) {
      // Something really bad happened, notify the parent thread so he stops the program...
    }
  }
}

生产者连接到套接字,读取和发送队列化的字符串数据......消费者:

public class Consumer implements Runnable {
  private Queue<String> q;

  public Consumer(Queue<String> q) {
    this.q = q;
  }

  public void run() {
    while(true) {
      String dataFromSocket = q.poll();
      saveData(dataFromSocket);
    }
  }
}

我的代码做的远不止这些,但我认为现在我已经解释了我想要做的事情。我已经阅读过关于wait()notify()的信息,但我认为这样做不会有效,因为我不想等待我的帖子出现异常,我想处理它以更好的方式。有哪些替代方案?

一般来说,我的代码看起来合理吗?在这里使用ExecutorService会有帮助吗?

非常感谢!

4 个答案:

答案 0 :(得分:1)

您可以使用Thread&#39; s UncaughtExceptionHandler

Thread.setDefaultExceptionHandler(
new UncaughtExceptionHandler() {
    public void unchaughtException(Thread th, Throwable exception) {
        System.out.println("Exception from Thread" + th + ". Exception:" + exception);
    }
});

Java文档 http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.UncaughtExceptionHandler.html

答案 1 :(得分:0)

给出当前代码的最简单的解决方案是等待生产者线程完成然后中断消费者:

Thread producerThread = new Thread(new Producer(q));
producerThread.start();     
Thread consumerThread = new Thread(new Consumer(q));
consumerThread.start();
try {
    producerThread.join();
} finally {
    consumerThread.interrupt();
}

正如您所提到的,执行程序会为您提供一种更通用的方法来在您需要退出时关闭所有内容(例如,当终端中断 ctrl - c时)。

ScheduledExecutorService executor = Executors.newScheduledThreadPool(10);
Producer producer = new Producer(q);
Consumer consumer = new Consumer(q);
executor.submit(producer::run);
executor.submit(consumer::run);
Runtime.getRuntime().addShutdownHook(new Thread(executor::shutdownNow));

请注意,清理工作必须比关闭执行程序更全面。您必须事先关闭套接字以允许线程被中断。

这是一个更完整的示例,可以处理来自双方的关闭。您可以通过使用nc -l 1234启动测试服务器来测试它。杀死任一进程(nc或java客户端)将导致另一个进程的干净退出。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;

public class Main {
    private ExecutorService executor;
    private Socket socket;
    private AtomicBoolean running = new AtomicBoolean(true);

    public static void main(String[] args) throws IOException {
        Main main = new Main();
        main.run();
    }

    private Main() throws IOException {
        executor = Executors.newCachedThreadPool();
        socket = new Socket("localhost", 1234);
    }

    private void run() throws IOException {
        BlockingQueue<String> q = new SynchronousQueue<>();
        Producer producer = new Producer(socket, q);
        Consumer consumer = new Consumer(q);

        // Start the producer. When it ends call stop
        CompletableFuture.runAsync(producer, executor).whenComplete((status, ex) -> stop());
        // Start the consumer.
        CompletableFuture.runAsync(consumer, executor);
        // Add a shutdown hook to stop everything on break
        Runtime.getRuntime().addShutdownHook(new Thread(this::stop));
    }

    private void stop() {
        if (running.compareAndSet(true, false)) { // only once
            // Close the socket to unblock the producer
            try {
                socket.close();
            } catch (IOException e) {
                // ignore
            }

            // Interrupt tasks
            executor.shutdownNow();
            try {
                // Give tasks some time to clean up
                executor.awaitTermination(1, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                // ignore
            }
        }
    }

    static class Producer implements Runnable {
        private BufferedReader in;
        private BlockingQueue<String> q;

        public Producer(Socket socket, BlockingQueue<String> q) throws IOException {
            this.in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            this.q = q;
        }

        @Override
        public void run() {
            try {
                while (true) {
                    String data = in.readLine();
                    if (data == null) {
                        break;
                    }
                    q.put(data);
                }
            } catch (InterruptedException | IOException e) {
                // Fall through
            }
            System.err.println("Producer done");
        }
    }

    static class Consumer implements Runnable {
        private BlockingQueue<String> q;

        public Consumer(BlockingQueue<String> q) {
            this.q = q;
        }

        @Override
        public void run() {
            try {
                while (true) {
                    System.out.println(q.take());
                }
            } catch (InterruptedException e) {
                // done
            }
            System.err.println("Client done");
        }
    }
}

答案 2 :(得分:-1)

将消费者线程作为'守护程序'线程

启动

将消费者线程标记为“守护程序”并让主线程也结束:

来自Thread.setDaemon(boolean)的Java API文档:

  

将此线程标记为守护程序线程或用户线程。当运行的唯一线程都是守护程序线程时,Java虚拟机将退出。

public Position getBestMove() {
   return successorEvaluations.stream()
                              .max(s -> s.score())
                              .orElse(DEFAULT_SUCCESSOR_EVALUATION)
                              .pos();
}

这样,当主线程和生产者线程终止(成功或异常)时,您的应用程序会自动停止。

如果主线程需要知道producerThread失败,你可以将它与public class Main { private Queue<String> q = new ConcurrentLinkedQueue(); public static void main(String[] args) throws IOException { Thread producerThread = new Thread(new Producer(q)); // producerThread.setDefaultUncaughtExceptionHandler(...); producerThread.start(); Thread consumerThread = new Thread(new Consumer(q)); consumerThread.setDeamon(true); consumerThread.start(); } } @Manish建议结合起来......

答案 3 :(得分:-1)

volatile 怎么样?

public class Main {
  volatile boolean isStopMain = false;
  private Queue<String> q = new ConcurrentLinkedQueue();

  public static void main(String[] args) throws IOException {
    new Thread(new Producer(q)).start(); 
    new Thread(new Consumer(q)).start();
    // Catch any error in the producer side, then stop both consumer and producer, and do some extra work to notify me that there's an error...
    while (true) {
       if(isStopMain){
      System.exit(0); //or other operation to stop the main thread.
     }
    }
  }
}

并且在制片人:

public class Producer implements Runnable {
  private Queue<String> q;

  public Producer(Queue<String> q) {
    this.q = q;
  }

  public void run() {
    try {
      connectToSocket();
      while(true) {
        String data = readFromSocket()
        q.offer(data);
      }
    } catch (Exception e) {
  // Something really bad happened, notify the parent thread so he stops the program...
      Main.isStopMain = true;
    }
  }
}

我想知道你是否想要杀死子线程中的父线程?如果是,您可能需要了解以下内容:How do I terminate parent thread from child?