有没有办法让Java线程只在初始化时做某事?

时间:2012-04-29 14:21:50

标签: java multithreading

对不起,如果这是基本的,我就是看不到答案。基本上我使用ThreadPoolExecutor运行几个线程来启动10个线程。我希望每个线程在其生命周期内与服务器建立自己的连接。是否有可能,如果可以,我在哪里放这个代码?

示例代码:

class DoWork implements Runnable{

    protected String[] data = null; 

    public DoWork(String[] data) {
        // get the data each thread needs to work
        this.data = data;
    }

    public void run() {
        // Do the work here

    }

}

我的理解是,如果工作队列中有项目,则ThreadPoolExecutor使10个线程保持活动状态,并且当工作完成时,它们就会死亡。在这个结构的某处我可以添加连接信息吗?我不想在DoWork方法中执行此操作,因为这将针对每个工作单元完成(因此,在工作队列中打开多个项目的连接,可能是数千个,并且在我尝试时会导致超时)。放在类声明和方法之间似乎没有做任何事情(虽然我可能做错了)。

有关如何实现这一目标的任何想法?

更新:不是100%需要,但我很好奇是否还有办法让它在终止时做某事(也许这样我就可以关闭与服务器的连接,而不是等待它超时)。

4 个答案:

答案 0 :(得分:5)

您可以使用ThreadLocal存储每个线程的连接:

public class ConnectionProvider {
    private static final ThreadLocal<Connection> threadLocalConnection = 
         new ThreadLocal<Connection>() {
             @Override 
             protected Connection initialValue() {
                 // TODO create and return the connection
             }
         };

    public static Connection getConnection() {
        return threadLocalConnection.get();
    }
}

或者,如果几个线程可以使用(在不同的时间)连接,则可以创建连接池。 run方法将从池中获取连接(将动态创建它并将其添加到已创建的连接集中,无可用),并在完成使用后将其释放到池中(在finally块中)。这将使连接可用于任何其他任务。这样做的好处是可以使所需连接数远低于线程池中的线程数。

答案 1 :(得分:1)

创建自己的线程工厂并将其作为参数传递给线程池构造函数。线程工厂创建的线程应该重写方法run(),如下所示:

public void run() {
  connectToServer();
  super.run();
}

问题是,谁和如何使用这种联系?如果您的意思是提供给线程池的每个作业都应该使用由其运行的线程创建的连接,那么将该连接保存为ThreadLocal。

答案 2 :(得分:1)

改编自ThreadPool of CLI Processes

的其他答案

这将创建一个可以从中进行绘制的连接池。这将阻止Connections为每个线程上下移动。但是,这仅在哪个线程使用哪个连接无关紧要时才有效。如果它确实重要,你必须调整这个代码或使用其他人建议的ThreadLocal并在线程死亡时挂钩。

当新工作项排队时,Thread会向连接池请求连接。如果没有,它将创建一个新的。如果一个可用,它将验证连接是否仍然有效,然后返回该对象。完成工作项后,它可以将其返回到连接池。

public class StackOverflow_10037379_jdk6 {

    private static Logger sLogger = Logger.getLogger(StackOverflow_10372827_jdk6.class.getName());           

    public static class ConnectionPoolableObjectFactory extends BasePoolableObjectFactory<Connection> {

        public ConnectionPoolableObjectFactory() {

        }

        @Override
        public Connection makeObject() throws Exception {                
            Connection connection = // createConnection
            return connection;
        }

        @Override
        public boolean validateObject(Connection connection) {
            return connection.isValid();
        }

        @Override
        public void destroyObject(Connection connection) throws Exception {
            connection.close();
        }

        @Override
        public void passivateObject(Connection connection) throws Exception {

        }
    }

    public static class WorkItem implements Runnable {

        private ObjectPool<Connection> mPool;
        private String mWork;

        public CLIWorkItem(ObjectPool<Connection> pool, String work) {
            mPool = pool;
            mWork = work;
        }

        @Override
        public void run() {
            Connection connection = null;
            try {
                connection = mPool.borrowObject();
                // do stuff with connection
            } catch (Exception ex) {
                sLogger.log(Level.SEVERE, null, ex);
            } finally {
                if (connection != null) {
                    try {
                        // Seriously.. so many exceptions.
                        mPool.returnObject(connection );
                    } catch (Exception ex) {
                        sLogger.log(Level.SEVERE, null, ex);
                    }
                }
            }
        }
    }

    public static void main(String[] args) throws Exception {

        // Change the 5 to 20 in your case. 
        ObjectPool<Connection> pool =
                new GenericObjectPool<Connection>(
                new ConnectionPoolableObjectFactory(), 5);

        BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(100, true);

        ThreadPoolExecutor executor = new ThreadPoolExecutor(20, 20, 1, TimeUnit.HOURS, queue);

        // print some stuff out.
        executor.execute(new WorkItem(pool, "Message 1\r\n"));
        executor.execute(new WorkItem(pool, "Message 2\r\n"));
        executor.execute(new WorkItem(pool, "Message 3\r\n"));
        executor.execute(new WorkItem(pool, "Message 4\r\n"));
        executor.execute(new WorkItem(pool, "Message 5\r\n"));
        executor.execute(new WorkItem(pool, "Message 6\r\n"));
        executor.execute(new WorkItem(pool, "Message 7\r\n"));
        executor.execute(new WorkItem(pool, "Message 8\r\n"));
        executor.execute(new WorkItem(pool, "Message 9\r\n"));
        executor.execute(new WorkItem(pool, "Message 10\r\n"));
        executor.execute(new WorkItem(pool, "Message 11\r\n"));

        executor.shutdown();
        executor.awaitTermination(4000, TimeUnit.HOURS);

        pool.close();
    }
}

答案 3 :(得分:1)

使用连接池会不会更容易?然后,每个任务只需在启动时从池中检索连接,并在完成后返回它。这样您就不必创建比线程更多的连接,并且在完成所有任务后,您可以轻松地销毁连接池(及其所有连接)。