美文网首页
SingleThreadPool

SingleThreadPool

作者: 程序员札记 | 来源:发表于2022-03-09 10:59 被阅读0次

    SingleThreadPool 的构造函数如下

        public static ExecutorService newSingleThreadExecutor() {
            return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
        }
    

    newSingleThreadExecutor() 这是一个单线程池,至始至终都由一个线程来执行。它是FxiedThreadPool的极端方式。 作用:该方法返回一个只有一个线程的线程池,即每次只能执行一个线程任务,多余的任务会保存到一个任务 。FixedThreadPool和newSingleThreadExecutor:都有的问题申请解决队列可能会消耗十分大的内存,甚至OOM。

    singleThreadExecutor的意义

    Java中的singleThreadExecutor表示单线程池,就是这个线程池里面只有一个线程,这个对象存在的意义是什么?跟直接起一个线程有什么区别?

    我的理解是不是单线程池的好处就是线程可以复用,将单线程池singleThreadExecutor声明成一个静态变量,完后在不同的A类,B类,C类中都可以在singleThreadExecutor的execute方法中去启动线程

    
    singleThreadExecutor.execute(new Runnable() {
             
                    @Override
                    public void run() {
                        
                    }
    
    }
    

    这里虽然每次都用了new Runnable关键字,但这几个线程其实都是同一个线程?是这样理解吗?

    首先要弄情况一个Runnable并非是一个线程,一个Thread才是一个线程。 singleThreadExecutor内部会创建一个Thread,这个Thread的工作就是从一个队列中取出用户提交的任务进行执行,如果执行过程中发生未受检的异常,singleThreadExecutor会自动重新启动一个线程再继续工作,这一点比用户自己创建一个线程自己管理轻松多了。同时自己维护一个任务队列也不是件简单的事,所以singleThreadExecutor的意义还是很大的。

    一个线程的创建方式,可以自己通过直接new Thread的方式创建并启动。也可能通过new ThreadPool的方式间接创建。具体采用哪种,跟实际应用场景有关。 如对于满足以下场景可以直接用new Thread的方式。而不需要采用ThreadPool 1、线程执行任务内容已确定 2、线程不需要频繁创建和销毁 3、线程不需要重用(跟第2点有点类似) 场景举例:某项目中,需要提供一个定时记录用户在线数量的功能,记录操作异步独立进行。抽象代码如下:

    public class Test {
        public static int userNum = 10;
        public static void main(String[] args) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    while(true) {
                        System.out.println("记录用户数:" + userNum);
                        try {
                            Thread.sleep(5000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
     
            //do other something.....
     
        }
    }
    

    该线程会一直存在,异步独立运行着。这个场景下功能比较简单,是没必要采用singleThreadExecutor的。singleThreadExecutor是通过创建一个线程池来管理。内部做了大量处理,如自定义很多处理类、建立容器接收任务、处理任务时的加锁解锁操作、线程的状态判断等等。采用后者相关于做了很多没必要的工作。且内存占用也比前者多。 现在在换一种场景:某项目中,需要提供当有用户登录或下线时记录用户在线数量的功能。记录操作异步独立进行。上述代码可以调整为:

    public class Test {
        public static int userNum = 10;
        public static void main(String[] args) {
            //do other something.....
            login();
            //do other something.....
            logout();
        }
     
        private static void logout() {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("记录用户数:" + userNum);
                }
            }).start();
        }
     
        private static void login() {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("记录用户数:" + userNum);
                }
            }).start();
        }
    }
    
    
    

    这种场景下,你会发现该实现操作会出现大量线程的频繁创建的销毁。所以我们肯定要做一些优化。如优化成下面这种:

    
    public class Test {
        public static int userNum = 10;
        static RecordThread th;
        static {
            th = new RecordThread();
            th.start();
            System.out.println("记录线程已启动");
        }
        public static void main(String[] args) {
            System.out.println("do something.....");
            login();
            System.out.println("do something.....");
            logout();
        }
     
        private static void logout() {
            th.addTask(new Runnable() {
                @Override
                public void run() {
                    System.out.println("记录用户数:" + userNum);
                }
            });
        }
     
        private static void login() {
            th.addTask(new Runnable() {
                @Override
                public void run() {
                    System.out.println("记录用户数:" + userNum);
                }
            });
        }
    }
     
    class RecordThread extends Thread {
        List<Runnable> taskList = new ArrayList<>();
        @Override
        public void run() {
            while (true) {
                if (taskList.size() > 0) {
                    Runnable task = taskList.remove(0);
                    task.run();
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        public void addTask(Runnable task) {
            taskList.add(task);
        }
    }
    
    

    优化后我们又会发现新的问题:线程并发安全问题没处理(ArrayList)、任务执行异常将导致线程终止,如下代码:

    private static void logout() {
          th.addTask(new Runnable() {
              @Override
              public void run() {
                  throw new NullPointerException();
              }
          });
      }
    
    

    所以后续代码你会继续优化。最终随着你的优化,你会发现,最终代码相当于是你自己写了一个类似ThreadPool的管理类。 所以二者如何使用。楼主需要先了解二者的所拥有的功能。再实际场景中再去做选择。
    综上所述,在全局需要要一个thread 的时候,就需要SingleThreadPool

    SingleThreadPool 和 FixThreadPool(1) 有啥区别

        public static ExecutorService newSingleThreadExecutor() {
            return new FinalizableDelegatedExecutorService
                (new ThreadPoolExecutor(1, 1,
                                        0L, TimeUnit.MILLISECONDS,
                                        new LinkedBlockingQueue<Runnable>()));
        }
    
    
    

    FinalizableDelegatedExecutorService 这个类不可以造型成 ThreadPool进行coresize的修改了。所以和FixThreadPool 最大的区别就是SingleThreadExecutor 不可以修改CoreSize。

    相关文章

      网友评论

          本文标题:SingleThreadPool

          本文链接:https://www.haomeiwen.com/subject/rhxtdrtx.html