将简单java对象从一个线程传递到另一个线程的快速方法

将简单java对象从一个线程传递到另一个线程的快速方法,java,performance,multithreading,nonblocking,Java,Performance,Multithreading,Nonblocking,我有一个接收对象的回调。我复制了这个对象,我必须将它传递给另一个线程进行进一步处理。尽快返回回调非常重要。理想情况下,回调会将副本写入某种无锁容器 我只从一个线程和一个处理线程调用回调 我只需要将一组双工线程传递给另一个线程,我知道双工线程的最大数量(大约40个) 有什么想法吗?我对Java不是很熟悉,所以我不知道在线程之间传递内容的常用方法。您是要创建一个新线程,还是需要访问另一个已经存在的线程 在案例1中,您可以从线程池请求一个新线程,将信息传递给它,让它运行,然后返回 在案例2中,您可能需

我有一个接收对象的回调。我复制了这个对象,我必须将它传递给另一个线程进行进一步处理。尽快返回回调非常重要。理想情况下,回调会将副本写入某种无锁容器

我只从一个线程和一个处理线程调用回调

我只需要将一组双工线程传递给另一个线程,我知道双工线程的最大数量(大约40个)


有什么想法吗?我对Java不是很熟悉,所以我不知道在线程之间传递内容的常用方法。

您是要创建一个新线程,还是需要访问另一个已经存在的线程

在案例1中,您可以从线程池请求一个新线程,将信息传递给它,让它运行,然后返回

在案例2中,您可能需要创建一个可以向其传递数据的中间线程,然后它会保留数据并尝试调用(例如)数据处理线程上的同步函数。如果不传递给中间线程,则不知道在将数据传递给其他同步线程时可能会阻塞多长时间

从关于线程池的java essentials:

newCachedThreadPool方法使用可扩展的线程池创建执行器。此执行器适用于启动许多短期任务的应用程序

关于如何“在线程之间传递内容”,请记住Java中线程最重要的部分是目标,即实现的类的实例。如果您构造一个
Thread
对象而不传入
Runnable
实例,那么目标就是
Thread
对象本身(因为
java.lang.Thread
实现了
java.lang.Runnable
)。否则,您可能会创建一个实现
Runnable
的自定义类,构造该类的实例,并将其传递给新构造的
线程
实例。在本例中,自定义类的实例是目标

确保线程(基本上是
Runnable
对象)能够访问对象等同于确保
Runnable
对象将对象引用为实例变量,从而使线程中执行的
run
方法能够访问对象

下面是如何将
double
数组的副本传递给新创建的线程的示例:

class MyRunner implements Runnable
{
    double[] m_data;

    public MyRunner(double[] data)
    {
        this.m_data = data;
    }

    public void run()
    {
        // this code is executed in a thread. It can access `m_data`.
    }
}

public class Callback
{
    public void doCallback(double[] data)
    {
        double[] dataCopy = null;
        if (data != null) {
            dataCopy = new double[data.length];
            System.arraycopy(data, 0, dataCopy, 0, data.length);
        }
        MyRunner target = new MyRunner(dataCopy);
        Thread worker = new Thread(target);
        worker.start();
    }
}

如果这只是一件一次性的事情-您得到了40个左右的双倍,并且想要开始一个新的线程处理,那么您可以这样做:

public void callback(final double[] myDoubles)
{
  new Thread(){
   public void run() {
      // you can use myDoubles here. it will run on a separate thread.
   }}.start()   
};
如果这是经常发生的事情,那么您需要研究并使用一个新的方法。不管名称如何,队列只有在满时才会阻塞

您可以创建一个数组,其大小是回调方法放入队列的适当大小的两倍。put()操作非常快速,因此回调方法不会延迟太久。(除非队列已满。)

当对象可用时,您的另一个线程将使用队列上的take()方法获取该对象。take()方法会一直阻塞,直到对象可用为止—如果您不希望这样做,但希望保持线程运行,执行其他操作,则使用poll()


最后一件要考虑的事情是,你想要一个工作线程来处理回调的双倍,还是想要几个?当工作需要一次完成一个线程时,使用一个线程是有意义的——例如,如果将数组写入文件,则使用多个线程执行此操作通常没有意义。Buf如果在每个数组上完成的工作是独立的,那么它很适合拥有多个工作线程。

创建一个用double数组构造的实现,然后将其传递到一个数组中

像这样:

public class MyDoublesProcessor implements Runnable {
    double[] doublesArray;
    public MyDoublesProcessor(double[] array) {
        doublesArray = array;
    }

    public void run() {
        //do stuff with array
    }
}

Executor exec = Executors.newFixedThreadPool(1);

void callback(double[] array) { //or whatever your callback is
    exec.execute(new MyDoublesProcessor(array));
}

对于案例2,如果在中间线程设法将对象传递给第三个线程之前再次调用回调,该怎么办?从线程池获取线程的速度有多快?它锁定了吗?在案例2中,您基本上仍然执行案例1(创建一个新线程,由线程管理器管理),但随后让该线程等待处理线程。答案经过编辑以反映线程池信息。我不会手工编写这些东西-使用并发UTIL更安全。您是否将所有的双精度对象放在一起并可以作为一个组传递,还是它们一个接一个地传递?当您说“复制”一个对象时,您的意思是复制引用吗,还是深度克隆?并非所有BlockingQueue实现都是有界的。仅为有界实现放置块。另外,将代码块放在一个SO代码块格式中。我认为您在这里对线程之间传递值进行了简单的介绍。LinkedBlockingQueue可以通过将绑定大小设置为Integer.MAX_值来选择“无界”,但所有队列都在有限的空间中运行,并且实际上受可用内存量的限制。有界队列提供了比“无界”队列更有用的语义,特别是在面对其他并发错误时:如果工作线程工作过度或死锁,无界队列将不断增长。有界队列的失败是可以预见的,这清楚地表明了工作线程的问题。无界队列永远不会满,因此永远不会阻塞。