大聪明教你学Java | 带你学习Java多线程(续:创建线程的其他方式与activeCount方法的浅谈)
前言
相信每一位程序猿对“多线程”这个概念应该都不陌生,无论是在开发还是面试的时候,都会遇到多线程的问题。不过,一定有很多小伙伴才刚刚接触到多线程,那么在此就由小弟为各位小伙伴细细说说什么是多线程。
在开始之前,先简单介绍一下什么是线程~
Java 给多线程编程提供了内置的支持。 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
多线程是实现并发机制的一种有效手段。进程和线程一样,都是实现并发的一个基本单位。线程是比进程更小的执行单位,线程是进程的基础之上进行进一步的划分。所谓多线程是指一个进程在执行过程中可以产生多个更小的程序单元,这些更小的单元称为线程,这些线程可以同时存在,同时运行,一个进程可能包含多个同时执行的线程。
书接上文
在上一篇博客(大聪明教你学Java | 带你学习Java多线程)中有小伙伴提到了线程的其他创建方式以及activeCount方法,那我们就接着这两方面继续聊一聊....
创建线程的其他方式
实现callable接口
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @description: TestCallable
* @author: 庄霸.liziye
* @create: 2021-10-28 09:10
* 创建线程的方式三:实现 Callable 接口。
* 此方法可以有返回值,并且可以抛出异常。
* 执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。 FutureTask是Future接口的实现类
*/
public class TestCallable {
public static void main(String[] args) {
ThreadDemo td = new ThreadDemo();
//执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。
FutureTask<Integer> result = new FutureTask<>(td);
new Thread(result).start();
//接收线程运算后的结果
try {
Integer sum = result.get(); //FutureTask 可用于闭锁 类似于CountDownLatch的作用,在所有的线程没有执行完成之后这里是不会执行的
System.out.println(sum);
System.out.println("------------------------------------");
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
class ThreadDemo implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i <= 100000; i++) {
sum += i;
}
return sum;
}
}
从上面的代码中,我们可以看出来实现Callable接口和实现Runnable接口是有很多取别的~
- Callable规定的方法是call(),而Runnable规定的方法是run().
- Callable的任务执行后可以返回值的,而Runnable的任务是不能返回值的
- 运行Callable任务可拿到一个Future对象, Future表示异步计算的结果;通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。
- call()方法可抛出异常,而run()方法是不能抛出异常的
线程池
如果我们经常创建和销毁线程,对线程是使用量特别大的,那么在这种情况下,我们就可以选择使用线程池的方式来创建线程~
顾名思义,线程池就是一个存放了很多线程的池子,使用线程时直接从线程池中获取,使用完放回池中。使用线程池可以避免频繁创建销毁,实现重复利用,提高响应速度也降低资源消耗。跟数据库连接池是类似的道理(●'◡'●)
public class ThreadPool {
public static void main(String[] args){
//创建固定线程个数为十个的线程池
//Executors.newCachedThreadPool() 创建一个可程根据需要创建新线的线程池
//Executors.newFixedThreadPool(n) 创建一个可重用固定线程数的线程池
//Executors.newSingleThreadExecutor() 创建一个只有一个线程的线程池
//Executors.newScheduledThreadPool(n) 创建一个线程池,可延迟执行或者定期地执行。
ExecutorService executorService = Executors.newFixedThreadPool(10);
//new一个Runnable接口的对象
NumberThread number = new NumberThread();
NumberThread1 number1 = new NumberThread1();
//执行线程,最多十个
//适合适用于Runnable
executorService.execute(number1);
executorService.execute(number);
//适合使用于Callable
//executorService.submit();
//关闭线程池
executorService.shutdown();
}
}
class NumberThread implements Runnable{
@Override
public void run() {
for(int i = 0;i<=100;i++){
if (i % 2 ==0 )
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
class NumberThread1 implements Runnable{
@Override
public void run() {
for(int i = 0;i<100; i++){
if(i%2==1){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
activeCount方法
线程类的 activeCount()方法用于返回当前线程的线程中活动线程的数量组。返回的值只是一个估计值,因为在此方法遍历内部数据结构时,线程数可能会动态变化。
咱们还是用代码来做个示例
public class Test extends Thread{
public static void main(String[] args){
window t1 = new window();
window t2 = new window();
window t3 = new window();
t1.setName("售票口1");
t2.setName("售票口2");
t3.setName("售票口3");
t1.start();
t2.start();
t3.start();
Thread.currentThread().getThreadGroup().list();
System.out.println("Thread.activeCount() = " + Thread.activeCount());
}
}
class window extends Thread{
private static int ticketNum = 100; //将其加载在类的静态区,所有线程共享该静态变量
@Override
public void run() {
while(true){
if(ticketNum>0){
System.out.println(getName()+" 售出第 "+ticketNum+" 张票");
ticketNum--;
}else{
break;
}
}
}
}
各位小伙伴可以猜猜看,执行上面的代码打印出的线程数是多少呢~ 有些小伙伴会猜测是三个线程,有些则会说是四个线程...这两个答案都不对,其实是五个线程。我们看一下打印结果~
我们把每一个线程的名字都打印出来了,分别是售票口1、售票口2、售票口3、main、Monitor Ctrl-Break。
售票口1、售票口2、售票口3 这三个线程大家肯定都知道是怎么来的(自己创建的三个线程模拟售票);名字为main的线程是咱们执行main方法的时候出现的线程,那么Monitor Ctrl-Break线程是什么呢?在这里我先卖个关子~
咱们之前一直是用run来执行main方法,咱们换成DeBug执行一下,看看会发生什么呢?
Monitor Ctrl-Break线程居然消失了!!!
突发奇想~咱们换个IDE来执行同样的代码又会发生什么呢?
在其他的IDE中执行同样的代码,我们无论是用run还是Debug的方式运行,线程数都是4个。
这时候我们也就知道了Monitor Ctrl-Break线程是在idea中才有的,而且还是要用run启动方式它才会出现,所以写代码的小伙伴一定要注意这里喽~
🥝**P.S. Monitor Ctrl-Break线程来源于IDEA的源码,有兴趣的小伙伴可以自己研究研究(●'◡'●)**🥝
小结
关于创建线程的其他方式与activecount方法的讲解到此告一段落,本人经验有限,很多地方可能讲的没有很到位,如果您在阅读的时候想到了什么问题,欢迎在评论区留言,我们后续再一一探讨🙇
希望各位小伙伴动动自己可爱的小手,来一波点赞+关注 (✿◡‿◡) 让更多小伙伴看到这篇文章~ 蟹蟹呦(●'◡'●)
如果文章中有错误,欢迎大家留言指正;若您有更好、更独到的理解,欢迎您在留言区留下您的宝贵想法。
爱你所爱 行你所行 听从你心 无问东西
转载自:https://juejin.cn/post/7082179797408808996