How to force a Java thread to close a thread-local database connection

后端 未结 9 1116
鱼传尺愫
鱼传尺愫 2021-02-13 12:41

When Using a thread-local database connection, closure of the connection is required when the thread exists.

This I can only do if I can override the run() method of the

相关标签:
9条回答
  • 2021-02-13 13:10

    If you are of a sensitive disposition, look away now.

    I wouldn't expect this to scale very well; it effectively doubles the number of threads in the system. There may be some use cases where it is acceptable.

    public class Estragon {
      public static class Vladimir {
        Vladimir() { System.out.println("Open"); }
        public void close() { System.out.println("Close");}
      }
    
      private static ThreadLocal<Vladimir> HOLDER = new ThreadLocal<Vladimir>() {
        @Override protected Vladimir initialValue() {
          return createResource();
        }
      };
    
      private static Vladimir createResource() {
        final Vladimir resource = new Vladimir();
        final Thread godot = Thread.currentThread();
        new Thread() {
          @Override public void run() {
            try {
              godot.join();
            } catch (InterruptedException e) {
              // thread dying; ignore
            } finally {
              resource.close();
            }
          }
        }.start();
        return resource;
      }
    
      public static Vladimir getResource() {
        return HOLDER.get();
      }
    }
    

    Better error handling and so on is left as an exercise for the implementer.

    You could also have a look at tracking the threads/resources in a ConcurrentHashMap with another thread polling isAlive. But that solution is the last resort of the desperate - objects will probably end up being checked too often or too seldom.

    I can't think of anything else that doesn't involve instrumentation. AOP might work.

    Connection pooling would be my favoured option.

    0 讨论(0)
  • 2021-02-13 13:12

    Wrap your Runnable with a new Runnable with a

    try {
      wrappedRunnable.run();
    } finally {
      doMandatoryStuff();
    }
    

    construction, and let THAT be executed instead.

    You could even make it into a method, ex:

      Runnable closingRunnable(Runnable wrappedRunnable) {
        return new Runnable() {
          @Override
          public void run() {
            try {
              wrappedRunnable.run();
            } finally {
              doMandatoryStuff();
            }
          }
        };
      }
    

    and call that method passing in the runnable you're looking for.

    You may also want to consider using an Executor instead. Makes it much easier to manage Runable and Callables.

    If you do use an ExecutorService, you can use it like executor.submit(closingRunnable(normalRunnable))

    If you know that you'll be shutting down your entire ExecutorService and want connections to close at that point, you can set a thread factory which also does the close "after all tasks are done and shutdown called on the executor", example:

      ExecutorService autoClosingThreadPool(int numThreads) {
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(numThreads, numThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); // same as Executors.newFixedThreadPool
        threadPool.setThreadFactory(new ThreadFactory() {
          @Override
          public Thread newThread(Runnable r) {
            return new Thread(closingRunnable(r)); // closes it when executor is shutdown
          }
        });
        return threadPool;
      }
    

    In terms of whether or not doMandatoryStuff can know whether or not the connection was ever opened or not previously, one thing that comes to mind is having a second ThreadLocal that just tracks if it was opened or not (ex: when connection is opened, get then set an AtomicInteger to 2, at cleanup time, check to see if it's still at its default, say, 1...)

    0 讨论(0)
  • 2021-02-13 13:12

    I don't really understand why you are not using traditional connection pooling. But I shall assume you have your reasons.

    How much freedom do you have? As some DI frameworks do support object life cycles and thread-scoped variables (all nicely proxied). Could you use one of them? I think Spring would do this all out of the box, while Guice would need a 3rd party lib to handle life cycles and thread scopes.

    Next how much control do you have on either the creating of the ThreadLocal variable or the creation of threads? I am guessing you have complete control on the ThreadLocal but limited to none on the creation of threads?

    Could you use Aspect-oriented programming to monitor for new Runnable or Threads extending the run() method to include the clean up? You will also need to extend the ThreadLocal so it can register itself.

    0 讨论(0)
  • 2021-02-13 13:13

    What we did was

    @Override
    public void run() {
      try {
        // ...
      } finally {
        resource.close();
      }
    }
    

    Basically just always (possibly open and then) close it for all paths through the thread. In case it helps anybody out there :)

    0 讨论(0)
  • 2021-02-13 13:20

    You had to open the connection once, so you also have to handle the closure at the same place. Depending on your environment the threads may be reused and you cannot expect the thread to be garbage collected before the shutdown of the application.

    0 讨论(0)
  • 2021-02-13 13:21

    Override the get() method in ThreaedLocal so that it sets a List property on the subclass. This property can easily be queried to determine if the get() method had been called for a particular thread. You could then access the ThreadLocal to clean it up in that case.

    Updated in response to comment

    0 讨论(0)
提交回复
热议问题