欢迎来到“雪碧聊技术”CSDN博客!
在这里,您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者,还是具有一定经验的开发者,相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导,我将不断探索Java的深邃世界,分享最新的技术动态、实战经验以及项目心得。
让我们一同在Java的广阔天地中遨游,携手提升技术能力,共创美好未来!感谢您的关注与支持,期待在“雪碧聊技术”与您共同成长!
目录
一、认识线程池
1、什么是线程池?
2、不复用线程的问题
3、线程池的工作原理
①工作线程
②任务队列
二、创建线程池
1、如何创建线程池?
方式一:JDK5.0开始,提供了代表线程池的接口:ExecutorService。使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象。
举例:
方式二:使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象。
2、线程池的注意事项
①什么时候开始创建临时线程?
②什么时候会拒绝新任务?
3、任务拒绝策略
三、处理Runnable任务
1、ExecutorService的常用方法
2、总结
四、处理Callable任务
1、ExecutorService的常用方法
2、总结
五、通过Executors工具类,创建线程池
1、该工具类中有哪些静态方法?
2、Executors工具类使用时可能存在的风险
3、总结
六、并发、并行
1、什么是进程?
举例
2、什么是线程?
3、什么是并发?
举例
4、什么是并行?
举例
5、总结
举例
六、综合案例-抢红包游戏
1、介绍
2、编码
一、认识线程池
1、什么是线程池?
线程池就是一个可以复用线程的技术。
2、不复用线程的问题
用户每发起一个请求,后台就需要创建一个新线程来处理,下次新任务来了肯定又要创建新线程处理,创建新线程的开销是很大的,并且请求过多时,肯定会产生大量的线程出来,这样会严重影响系统的性能。
就好比:每次吃完饭都扔一个碗,长期以来肯定吃不消这么大的消耗。
3、线程池的工作原理
①工作线程
工作线程:就是线程池里面的线程,来处理任务队列里面的任务,有点类似于饭店的服务员。
②任务队列
任务队列:里面存放的都是实现了Runnable/Callable接口的任务类,等待工作线程的完成。
二、创建线程池
1、如何创建线程池?
方式一:JDK5.0开始,提供了代表线程池的接口:ExecutorService。使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象。
该实现类 ThreadPoolExecutor的构造器一共有七个参数,如下:
举例:
public class Test9 {public static void main(String[] args) {//目标:创建线程池对象来使用//1、使用线程池(ExecutorService接口)的实现类ThreadPoolExecutor声明七个参数来创建线程池对象ExecutorService pool = new ThreadPoolExecutor(3,5,10, TimeUnit.SECONDS,new ArrayBlockingQueue<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());}
}
方式二:使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象。
下面五会讲。
2、线程池的注意事项
①什么时候开始创建临时线程?
新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程。
②什么时候会拒绝新任务?
核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始拒绝任务。
3、任务拒绝策略
三、处理Runnable任务
1、ExecutorService的常用方法
举例:
public class Test9 {public static void main(String[] args) {//目标:创建线程池对象来使用//1、使用线程池(ExecutorService接口)的实现类ThreadPoolExecutor声明七个参数来创建线程池对象ExecutorService pool = new ThreadPoolExecutor(3,5,10,TimeUnit.SECONDS,new ArrayBlockingQueue<>(3),Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());//2、使用线程池处理任务!看会不会复用线程?Runnable target = new My_Runnable();//任务对象pool.execute(target);pool.execute(target);pool.execute(target);pool.execute(target);pool.execute(target);pool.execute(target);//3、关闭线程池:一般不关闭线程池//pool.shutdown();//等所有任务执行完毕后,再关闭线程池!//pool.shutdownNow();//立即关闭,不管任务是否执行完毕!}
}//线程任务类(等待被线程对象执行的任务)
class My_Runnable implements Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + "执行了~~");}
}
执行结果:
pool-1-thread-2执行了~~
pool-1-thread-3执行了~~
pool-1-thread-2执行了~~
pool-1-thread-3执行了~~
pool-1-thread-2执行了~~
pool-1-thread-1执行了~~
可见此时6个任务,但是线程池就3个核心线程,因此会产生复用。于是就看到上面确实有很多线程执行了好几个任务,因此线程确实是被复用了。
2、总结
四、处理Callable任务
1、ExecutorService的常用方法
举例:
public class Test10 {public static void main(String[] args) {//目标:创建线程池对象来使用//1、使用线程池(ExecutorService接口)的实现类ThreadPoolExecutor声明七个参数来创建线程池对象ExecutorService pool = new ThreadPoolExecutor(3,5,10,TimeUnit.SECONDS,new ArrayBlockingQueue<>(3),Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());//2、使用线程池处理任务!看会不会复用线程?Future<String> f1 = pool.submit(new My_Callable(100));Future<String> f2 = pool.submit(new My_Callable(200));Future<String> f3 = pool.submit(new My_Callable(300));Future<String> f4 = pool.submit(new My_Callable(400));//输出线程执行完,返回的结果try {System.out.println(f1.get());System.out.println(f2.get());System.out.println(f3.get());System.out.println(f4.get());} catch (InterruptedException e) {throw new RuntimeException(e);} catch (ExecutionException e) {throw new RuntimeException(e);}//3、关闭线程池:一般不关闭线程池//pool.shutdown();//等所有任务执行完毕后,再关闭线程池!//pool.shutdownNow();//立即关闭,不管任务是否执行完毕!}
}//1、定义一个实现Callable接口的实现类
class My_Callable implements Callable<String>{private int n;public My_Callable(int n){this.n = n;}//2、重写call方法,定义线程执行体public String call() throws Exception {int sum = 0;for (int i = 0; i <= n; i++) {sum+=i;}return Thread.currentThread().getName()+"-"+n+"的和是:"+sum;}
}
运行结果:
pool-1-thread-1-100的和是:5050
pool-1-thread-2-200的和是:20100
pool-1-thread-3-300的和是:45150
pool-1-thread-1-400的和是:80200
可见此时线程1被复用。
2、总结
五、通过Executors工具类,创建线程池
1、该工具类中有哪些静态方法?
举例:
public class Test10 {public static void main(String[] args) {//1、通过线程池工具类:Executors,调用其静态方法直接得到线程池ExecutorService pool = Executors.newFixedThreadPool(3);//2、使用线程池处理任务!看会不会复用线程?Future<String> f1 = pool.submit(new My_Callable(100));Future<String> f2 = pool.submit(new My_Callable(200));Future<String> f3 = pool.submit(new My_Callable(300));Future<String> f4 = pool.submit(new My_Callable(400));try {System.out.println(f1.get());System.out.println(f2.get());System.out.println(f3.get());System.out.println(f4.get());} catch (InterruptedException e) {throw new RuntimeException(e);} catch (ExecutionException e) {throw new RuntimeException(e);}//3、关闭线程池:一般不关闭线程池//pool.shutdown();//等所有任务执行完毕后,再关闭线程池!//pool.shutdownNow();//立即关闭,不管任务是否执行完毕!}
}//1、定义一个实现Callable接口的实现类
class My_Callable implements Callable<String>{private int n;public My_Callable(int n){this.n = n;}//2、重写call方法,定义线程执行体public String call() throws Exception {int sum = 0;for (int i = 0; i <= n; i++) {sum+=i;}return Thread.currentThread().getName()+"-"+n+"的和是:"+sum;}
}
运行结果:
pool-1-thread-1-100的和是:5050
pool-1-thread-2-200的和是:20100
pool-1-thread-3-300的和是:45150
pool-1-thread-3-400的和是:80200
2、Executors工具类使用时可能存在的风险
3、总结
六、并发、并行
1、什么是进程?
正在运行的程序(软件)就是一个进程。
举例
2、什么是线程?
线程属于进程,一个进程可以同时运行很多个线程。
进程中的多个线程是并发+并行执行的。
3、什么是并发?
并发:进程中的线程是由CPU负责调度执行的,但CPU同时处理线程的数量是有限的(由CPU的核数决定),为了能保证全部线程都能往前执行,因此CPU会轮询为系统的每个线程服务,由于CPU切换的速度很快,给我们的感觉是这些线程在同时执行,这就是并发。
举例
假设我的CPU是单核的(同一时刻只能运行一个线程),但是会让很多线程轮流切换上CPU执行,切换速度极快,仿佛是同时在运行所有进程中的线程,这就叫“并发”。
4、什么是并行?
并行:在同一个时刻上,同时有多个线程(数量取决于CPU的核数)在被CPU调度执行。
举例
假设我的CPU是4核,那么同一时刻,会有4个线程同时上cpu执行,这就叫“并行”。
说白了,并行的线程数,取决于CPU的核数。
5、总结
举例
通过查看某人电脑的任务管理器,得知该电脑的CPU是20核的,也就是说,同一时刻允许20个线程上CPU执行,那么这20个线程就是并行关系;
该电脑目前需要处理6800个线程,那么就会20个一组上CPU轮流切换运行,由于切换速度极快,仿佛是同时在运行,这就叫“并发”。
六、综合案例-抢红包游戏
1、介绍
2、编码
public class Test12 {public static void main(String[] args) {/*** 目标:完成多线程的综合小案例* 红包雨游戏:某企业有100名员工,员工的工号依次是1,2,3,4...到100* 现在公司举办了年会活动,活动中有一个红包雨环节,要求共计发出200个红包雨,其中小红包在【1 - 30】 元之间* 总占比80%,大红包在【31 - 100】元, 总占比20%* 分析:100个员工实际上就是100个线程,来竞争200个红包*/List<Integer> redPacket = getRedPacket();//2、定义线程类,创建100个线程,竞争同一个集合for (int i = 1; i <= 100; i++) {new EmployeeGetPacket(redPacket, "员工"+i).start();}}//1、准备这200个随机的红包返回,放到这个List集合中public static List<Integer> getRedPacket(){Random r = new Random();//其中小红包在【1 - 30】 元之间,总占比80%(即160个),大红包在【31 - 100】元,总占比为20%(即40个)ArrayList<Integer> redPacket = new ArrayList<>();for (int i = 1; i <= 160; i++) {redPacket.add(r.nextInt(30) + 1);}for (int i = 1; i <= 40; i++) {redPacket.add(r.nextInt(70) + 31);}return redPacket;}
}//员工抢红包的线程
class EmployeeGetPacket extends Thread{private List<Integer> redPacket;//红包集合//构造器public EmployeeGetPacket(List<Integer> redPacket, String name) {super(name);this.redPacket = redPacket;}public void run() {String name = Thread.currentThread().getName();while(true){//100个人来抢redPackage集合中的钱synchronized (redPacket){if(redPacket.size() == 0){break;}//随机一个索引得到红包int index = (int)(Math.random() * redPacket.size());Integer money = redPacket.remove(index);System.out.println(name + "抢到了" + money + "元");if(redPacket.size() == 0){System.out.println("活动结束!");break;}}try {Thread.sleep(1000);}catch (InterruptedException e){throw new RuntimeException(e);}}}
}
运行结果:
员工1抢到了3元
员工99抢到了20元
员工98抢到了59元
员工100抢到了14元
员工96抢到了2元
员工97抢到了17元
员工95抢到了20元
员工94抢到了12元
员工93抢到了8元
员工92抢到了26元
员工91抢到了7元
...不全部展示了,因为200条抢红包记录,太长了...
以上就是线程池、并发、并行、抢红包游戏案例的全部内容,想了解更多Java知识,请关注本博主~~