简体   繁体   中英

Is there a way to get a Java thread to do something only upon initialization?

Sorry if this is basic, I just don't see the answer. Basically I am running a few threads using ThreadPoolExecutor to launch 10 threads. I want each thread to make its own connection to a server for the duration of its life. Is it possible and if so where do I put this code?

Example code:

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

    }

}

My understanding has been if there are items in the work queue then ThreadPoolExecutor keeps the 10 threads alive and when the work is done they die. Is there somewhere in this structure I can add connection information? I don't want to do it in the DoWork method because that'll be done for each work unit(thus opening connections for as many items in the work queue, could be thousands and causes timeouts when I tried). Putting in-between the class declaration and the method didn't seem to do anything(although I could be doing it wrong).

Any ideas of how to accomplish this?

UPDATE: not 100% needed but I'm curious if there's also a way to get it do something upon termination(maybe so I can close the connection to the server instead of waiting for it to timeout).

You could use a ThreadLocal to store the connection for each thread:

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();
    }
}

Or, if the connections can be used (at different times) by several threads, you could create a pool of connections. The run method would get a connection from the pool (which would dynamically create it and add it to the set of created connections is none is available), and release it to the pool (in a finally block) when it has finished using it. This would make the connection available for any other task. This would have the advantage of making the number of needed connections potentially lower than the number of threads in the thread pool.

Create your own thread factory and pass it to the thread pool constructor as a parameter. The threads created by the thread factory should override the method run() as follows:

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

The question is, who and how would use that connections? If you mean that every job supplied to the thread pool should use the connection created by the thread it is running on, then save that connection as a ThreadLocal.

Adapted from my other answer at ThreadPool of CLI Processes

This creates a Connection Pool in which you can draw from. This will prevent Connections from going up and down for each thread. However, this only works if it doesn't matter which thread uses which connection. If it does matter, you'd have to adapt this code or use the ThreadLocal someone else suggested and hook into when a Thread dies.

When a new work item is queued the Thread then asks the connection pool for a connection. If one is not available it will create a new one. IF one is available, it will verify the connection is still valid, then return that object. When the work item is finished it can return it to the connection pool.

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();
    }
}

Wouldn't it be easier to just use a connection pool? Each task would then simply retrieve a connection from the pool when started and return it when finished. That way you wouldn't have to create more connections than threads and you could easily destroy the connection pool (and all of its connections) after all tasks have finished.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM