线程池7种创建方式

线程池的7种创建方式

  • 一. 线程的缺点
  • 二 .线程池
    • 1.定义:使用池化技术来管理和使用线程的技术
    • 2.线程池的优点:
    • 3.线程池的创建方式(共7种):
      • (1)创建固定个数的线程池(任务数趋向无限大,可能会造成OOM,谨慎使用)
      • (2)创建带缓存的线程池(根据任务数量生成对应的线程数)
      • (3)创建可以执行定时任务的线程池
        • 【1】 scheduleWithFixedDelay的使用
        • 【 2】 schedule使用
        • 【3】scheduleAtFixedRate使用
      • (4)创建单个执行定时任务的线程池
      • (5)创建单个线程的线程池
      • (6)JDK8+支持:根据当前的工作环境(cpu核心数,任务量)生成对应的线程池,是异步线程池(创建即结束)
    • synchronize:同步
      • 异步执行的流程:
      • (7) 原始创建线程池的方法
        • 优点:
        • ThreadPoolExecutor参数:
    • - 拒绝策略(5种)
      • - 默认拒绝策略
      • 自定义拒绝策略:
    • - JDK4种拒绝策略:
      • (1)ThreadPoolExecutor.AbortPolicy() 默认解决策略
      • (2)ThreadPoolExecutor.CallerRunsPolicy() 使用调用线程池的线程来执行任务,使用==主线程==来执行任务
      • (3)ThreadPoolExecutor.DiscardOldestPolicy()
      • (4)ThreadPoolExecutor.DiscardPolicy()
  • 4.线程池的执行流程
  • 5.线程池里面的2个重要内容:
  • 6.自定义线程池的行为
  • 7.Executors 创建线程池的问题:

一. 线程的缺点

1:线程的创建需要开辟三个内存资源:本地方法栈,虚拟机栈,程序计数器等线程私有变量的内存,同时销毁的时候需要销毁以上三个区域的内存,频繁的创建和消耗会带来一定的性能开销。

2:在任务量远大于线程可以处理的任务量的时候, 使用线程不能很好的管理任务和友好的拒绝任务。

3:阿里开发手册要求:
[强制]线程资源必须通过线程池提供,不允许在应用中自行显式创建线程
说明:线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销,解决资源不足的问题。
如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换” 的问题。

二 .线程池

1.定义:使用池化技术来管理和使用线程的技术

2.线程池的优点:

(1)可以避免频繁的创建和消耗线程
(2)可以更好地管理线程的个数和资源的个数
(3)拥有更多功能,比如线程池可以进行定时任务的执行。
(4)线程池可以更友好的拒绝不能处理的任务

3.线程池的创建方式(共7种):

(1)创建固定个数的线程池(任务数趋向无限大,可能会造成OOM,谨慎使用)

public class ThreadPoolDemo1 {
    public static void main(String[] args) {
        //创建一个固定个数的线程池
       ExecutorService service =  Executors.newFixedThreadPool(10);//最大为10
        for (int i = 0; i < 2; i++) {
        
                    //执行任务
            service.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程名:" + Thread.currentThread().getName());

                }
            })

问题:创建了10个线程来执行2个任务,那当前程序创建了几个线程?
: 创建了2个线程

(2)创建带缓存的线程池(根据任务数量生成对应的线程数)

public class ThreadPoolDemo3 {
    public static void main(String[] args) {
     //创建带缓存的线程池
      ExecutorService service =  Executors.newCachedThreadPool();
      //执行十个任务
        for (int i = 0; i < 10 ; i++) {
            service.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程名:"+Thread.currentThread().getName());
                }
            });
        }
    }
}

适用场景:有大量短期任务时newCachedThreadPool() 根据当前的任务创建一定数量的线程池

(3)创建可以执行定时任务的线程池

【1】 scheduleWithFixedDelay的使用

public class ThreadPoolDemo4 {
    public static void main(String[] args) {
        //创建可以执行定时任务的线程池
       ScheduledExecutorService service =  Executors.newScheduledThreadPool(10);
       //执行任务
        service.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                System.out.println("执行任务:"+new Date());
            }
        },1,3, TimeUnit.SECONDS);
    }
}

参数1:线程池的任务
参数2:定时任务延迟多长时间开始执行
参数3:定时任务执行频率
参数4:配合参数2和参数3使用的时间单位

【 2】 schedule使用

public class ThreadPoolDemo6 {
    public static void main(String[] args) {
        ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
        System.out.println("执行任务之前:"+new Date());
        //执行任务
        service.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("执行任务:"+new Date());
            }
        },3, TimeUnit.SECONDS);
    }
}

以上两者区别
(1)schedule没有延迟执行的时间设置
(2)schedule定时任务只能执行一次

【3】scheduleAtFixedRate使用

public class ThreadPoolDemo7 {
    public static void main(String[] args) {
            //创建可以执行定时任务的线程池
            ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
            //System.out.println("执行定时任务之前:"+new Date());
            //执行任务
            //1.执行固定周期的延迟任务
            service.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("执行任务:"+new Date());
                }
            },1,3, TimeUnit.SECONDS);
        }
}


scheduleAtFixedRate与scheduleWithFixedDelay区别

scheduleAtFixedRate:任务开始时间是以上次任务开始时间作为起始时间的,固定频率执行

scheduleWithFixedDelay:任务开始时间是以上次任务结束时间作为开始时间的,时间不固定,根据任务执行的时间来定

(4)创建单个执行定时任务的线程池

public class ThreadPoolDemo8 {
    public static void main(String[] args) {
        //创建单个执行定时任务的线程池
        ScheduledExecutorService service =  Executors.newSingleThreadScheduledExecutor();
        //开始定时任务
        service.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("执行任务:"+new Date());
            }
        },1,3, TimeUnit.SECONDS);

    }
}

(5)创建单个线程的线程池

public class ThreadPoolDemo9 {
    public static void main(String[] args) {
        //创建单个线程的线程池
        ExecutorService service =  Executors.newSingleThreadExecutor();
        //执行任务
        for (int i = 0; i < 10 ; i++) {
            service.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程名:" + Thread.currentThread().getName());
                }
            });
        }
    }
}

意义:
a.无需频繁的创建和销毁线程
b.可以更好的分配和管理以及存储任务(任务队列)

(6)JDK8+支持:根据当前的工作环境(cpu核心数,任务量)生成对应的线程池,是异步线程池(创建即结束)

public class ThreadPoolDemo10 {
    public static void main(String[] args) {
        //根据当前工作环境创建线程
        ExecutorService service = Executors.newWorkStealingPool();
        for (int i = 0; i < 10 ; i++) {
            service.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程名:" + Thread.currentThread().getName());
                }
            });
        }
    }
}

synchronize:同步

同步:按照某种规则按序执行
同步执行流程:
(1)main调用线程池
(2)线程池执行完之后
(3)关闭线程池,main也会随之关闭

异步执行的流程:

(1)main调用异步线程池
(2)异步线程池后台执行,对main线程来说异步线程池已经执行完成,关闭main线程

(7) 原始创建线程池的方法

优点:

(1)线程数据可控 (2)任务队列大小可控

ThreadPoolExecutor参数:

1.核心线程数(正式员工)
2.最大线程数(正式员工+临时工)
3.生存时间(long)
4.时间单位
5.任务队列
6.线程工厂(可设置优先级、命名、类型… )
7.拒绝策略

public class ThreadPoolDemo1 {
    public static void main(String[] args) {
        //原始创建线程池的方法
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5,10,60, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(1000));//一定要给任务队列设置值,否则可能出现OOM
        for (int i = 0; i < 5 ; i++) {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程名:"+
                            Thread.currentThread().getName());
                }
            });
        }


    }
}

问题:核心线程数: 5,最大线程数: 10,任务队列: 5
当有6个任务时(多出了1个任务)
JDK会将多余的任务放到任务队列

当有11个任务时 (多出1个任务,此时会判断最大线程数,如果线程池中的线程数没有到达最大线程数,就会创建一个线程执行任务)

线程池创建线程是懒加载,在有任务的时候才会创建线程,并不是执行new的时候就会创建线程

核心线程: 5,最大线程数: 5,任务队列: 5
当任务 = 11时
第一步: 11- 5=6
第二步: 6-5=1
第三步:最大线程数–线程池线程数量=0,线程池执行拒绝策略

- 拒绝策略(5种)

- 默认拒绝策略

/**
 *拒绝策略演示
 * 默认的拒绝策略
 */
public class ThreadPoolDemo4 {
    public static void main(String[] args) {
        //创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5,5,0, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(5));
        for (int i = 0; i < 11; i++) {
            int finalI = i;
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("序列号:"+finalI+"线程名:"+Thread.currentThread().getName());
                }
            });
        }
    }

线程池7种创建方式_第1张图片

自定义拒绝策略:

RejectedExecutionHandler()

public class ThreadPoolDemo7 {
    public static void main(String[] args) throws InterruptedException {
        //创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5, 0, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(5),
                new RejectedExecutionHandler() {
                    @Override
                    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                        //自定义拒绝策略
                        System.out.println("执行了拒绝策略");
                    }
                });
        for (int i = 0; i < 11; i++) {
            int finalI = i;
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("序列号:"+finalI+"线程名:"+Thread.currentThread().getName());
                }
            });
            //Thread.sleep(200);
        }
    }
}

线程池7种创建方式_第2张图片

- JDK4种拒绝策略:

线程池7种创建方式_第3张图片

(1)ThreadPoolExecutor.AbortPolicy() 默认解决策略

不执行任务,直接抛出异常

public class ThreadPoolDemo5 {
    public static void main(String[] args) {
        //创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5,5,0, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(5),
                new ThreadPoolExecutor.AbortPolicy());
        for (int i = 0; i < 11; i++) {
            int finalI = i;
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("序列号:"+finalI+"线程名:"+Thread.currentThread().getName());
                }
            });
        }
    }
}

线程池7种创建方式_第4张图片

(2)ThreadPoolExecutor.CallerRunsPolicy() 使用调用线程池的线程来执行任务,使用主线程来执行任务

public class ThreadPoolDemo5 {
    public static void main(String[] args) {
        //创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5,5,0, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(5),
                new ThreadPoolExecutor.CallerRunsPolicy());
        for (int i = 0; i < 11; i++) {
            int finalI = i;
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("序列号:"+finalI+"线程名:"+Thread.currentThread().getName());
                }
            });
        }
    }
}

线程池7种创建方式_第5张图片

(3)ThreadPoolExecutor.DiscardOldestPolicy()

忽略老任务

public class ThreadPoolDemo6 {
    public static void main(String[] args) throws InterruptedException {
        //创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5,5,0, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(5),
                new ThreadPoolExecutor.DiscardOldestPolicy());
        for (int i = 0; i < 11; i++) {
            int finalI = i;
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("序列号:"+finalI+"线程名:"+Thread.currentThread().getName());
                }
            });
            //Thread.sleep(200);
        }
    }
}

线程池7种创建方式_第6张图片

(4)ThreadPoolExecutor.DiscardPolicy()

忽略新任务

public class ThreadPoolDemo6 {
    public static void main(String[] args) throws InterruptedException {
        //创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5,5,0, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(5),
                new ThreadPoolExecutor.DiscardPolicy());
        for (int i = 0; i < 11; i++) {
            int finalI = i;
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("序列号:"+finalI+"线程名:"+Thread.currentThread().getName());
                }
            });
            //Thread.sleep(200);
        }
    }
}

线程池7种创建方式_第7张图片

4.线程池的执行流程

当拿到一个任务,会判断当前线程池里面的线程数量是否达到了最大值,如果没有达到就会创建新的线程执行任务;
当任务来了之后,当前线程池的线程数量已经是最大值,并且没有空闲线程,当前的任务就会被放到线程池的任务队列里等待执行。
线程池7种创建方式_第8张图片

5.线程池里面的2个重要内容:

线程,任务队列(存放多余任务)

6.自定义线程池的行为

设置线程池命名规则,线程池的优先级等,下面代码为示例

public class ThreadPoolDemo3 {
    public static void main(String[] args) {
        //自定义线程工厂
        ThreadFactory threadFactory = new MyThreadFactory();
        //创建线程池
      ExecutorService service =  Executors.newFixedThreadPool(10,threadFactory);
        for (int i = 0; i <10 ; i++) {
            service.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程名:"+
                            Thread.currentThread().getName() + ",线程优先级:"+
                            Thread.currentThread().getPriority());
                }
            });
        }
    }
    private static int count = 1;
   static class MyThreadFactory implements ThreadFactory{

        @Override
        public Thread newThread(Runnable r) {
             Thread thread = new Thread(r);
             //自定义线程池功能
            //设置线程池的命名
             thread.setName("myThreadPool-"+ count++);
             //设置线程池的优先级
            thread.setPriority(10);
             return thread;
        }
    }
}

7.Executors 创建线程池的问题:

(1)线程数量不可控(造成线程的过度切换和争取 -> 程序执行比较慢)
(2)任务数量不可控(任务数无限大 Integer. MAX_VALUE),当任务量比较大的情况下就会造成内存溢出情况(OOM ----->0utOfMemoryError)

你可能感兴趣的:(web,java,多线程,线程池)