【问题标题】:How to run the background thread only once using ScheduledExecutors?如何使用 ScheduledExecutors 只运行一次后台线程?
【发布时间】:2014-01-22 04:46:07
【问题描述】:

下面是我的界面-

public interface IClient {
    public String read(ClientInput input);
}

这是我的接口实现 -

public class TempClient implements IClient {

    @Override
    public String read(ClientInput input) {

    }
}

下面是我的工厂代码-

public class TempClientFactory {

    public static IClient getInstance() {
    new TempScheduler().startScheduler();
    return ClientHolder.INSTANCE;
    }

    private static class ClientHolder {
        private static final TempClient INSTANCE = new TempClient();
    }
}

现在我有一个工厂,它可以像这样获取 TempClient 的实例,因此客户在调用我们的代码时将使用我们的工厂,如下所示。

IClient client = TempClientFactory.getInstance();
client.read(input);

下面是我的后台线程代码-

public class TempScheduler {

    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

        public void startScheduler() {
            final ScheduledFuture<?> taskHandle = scheduler.scheduleAtFixedRate(new Runnable() {
                public void run() {
                try {
                    callServers();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
                }
            }, 0, 10L, TimeUnit.MINUTES);
        }
    }

    //... some code
}

问题陈述:-

  1. 现在我有一个后台线程,它将在 从 URL 获取数据、解析并存储数据的场景 在一个变量中。现在我想要的是我们的客户一开始 通过使用工厂调用使用我们的代码,我想要 后台线程开始。所以我决定打这个电话 我上面的TempClientFactory,它工作得很好..

现在这种方法的问题是,客户将继续调用我的 TempClientFactory 所以它会一直启动后台线程。我正在寻找的是,如果后台线程启动一次,那么我不想要从另一个线程再次启动它,但它应该在后台每 10 分钟运行一次,因为它已经第一次启动了。

如果我的理解是正确的,假设如果我调用我的 TempClientFactory 一次,那么它将启动后台线程并且我的后台线程将继续每 10 分钟运行一次。如果我再次调用我的工厂,那么它将再次启动相同的背景,也将每 10 分钟运行一次?或者它只会启动一个每 10 分钟运行一次的后台线程?

【问题讨论】:

  • 不给它一个重复周期,直接使用schedule。如果你再次调用schedule,或者你当前调用它的兄弟,那么它将创建第二个线程。
  • 我只需要一个应该每 10 分钟运行一次的后台线程。我不想让客户/客户继续给我们打电话,然后我的后台线程被调用...我这里还有其他方法吗?

标签: java multithreading background-thread scheduledexecutorservice


【解决方案1】:

您需要手动阻止它再次被调用。您可以将static 块添加到内部类。

public class TempClientFactory {
    public static IClient getInstance() {
        return ClientHolder.INSTANCE;
    }

    private static class ClientHolder {
        private static final TempClient INSTANCE = new TempClient();

        static {
            // You could do this here, as well, if it were more complicated
            //  than a one-line statement (e.g., requires try/catch):
            //INSTANCE = new TempClient();

            new TempScheduler().startScheduler();
        }
    }
}

类似于 JVM 为实例化单例所保证的线程安全、延迟加载,它也将保证一次性访问此处启动调度程序。

因此,您将只启动和安排您的线程运行一次。

另外,我建议将您的 ex.printStackTrace(); 转换为实际的日志语句。

【讨论】:

  • 感谢您的帮助.. ex.printStackTrace 只是为了演示.. 我的记录器在那里.. 所以如果我按照您建议的方式进行操作,它只会启动我的后台线程只有一次,如果有人再次打电话给我的工厂,那么它不会再次启动后台线程..对吗?而且,它也是线程安全的吗?
  • 正确。它是线程安全的,原因与单例实例化的原因相同——JVM 使用内部类的static 内容保证它不加锁。它只会发生一次。
  • 您可以阅读更多关于它的信息here。它与单例实例化有关,但这个想法适用于我在这里所做的事情。
【解决方案2】:

使用单例方法可以确保您拥有一个类的单个实例。创建 TempScheduler 的对象只需调用

 public class TempClientFactory {

    public static IClient getInstance() 
   {

       TempScheduler  obj=TempScheduler.getTempScheduler(); 
       if(obj.flag==false) 
       {
        obj.startScheduler();
       }
       return ClientHolder.INSTANCE;
    }

    private static class ClientHolder {
        private static final TempClient INSTANCE = new TempClient();
    }
}






 public class TempScheduler {

            private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
            public static TempScheduler scheduler=null;
            public bool flag;

                public void startScheduler() {
                    flag=true;
                    final ScheduledFuture<?> taskHandle = scheduler.scheduleAtFixedRate(new Runnable() {
                        public void run() {
                        try {
                            callServers();
                        } catch (Exception ex) {
                            ex.printStackTrace();
                        }
                        }
                    }, 0, 10L, TimeUnit.MINUTES);
                }
            }

            private TempScheduler()
            {

            }

            synchronized public static TempScheduler getTempScheduler()
            {
             if(scheduler==null)
             {
                scheduler=new TempScheduler();
             }
             return scheduler;
           }


            //... some code
        }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-30
    • 1970-01-01
    相关资源
    最近更新 更多