创建线程的方式有那些?

article/2025/9/17 0:44:16

目录

一.创建线程的4种方式

 二.创建线程方式有什么区别?


一.创建线程的4种方式

(1)写一个类继承Thread,覆盖重写run方法

(2)创建一个Runnable类型的对象,实现run()方法,传入Thread的构造方法中

(3)实现Callable接口,实现call()方法;

(4)通过Executor的工具类创建线程池,通过线程池获取线程

(一)继承Thread,覆盖重写run方法

继承java.lang包下的Thread类,覆盖重写thread类的run()方法,在run方法中实现运行在线程上的代码;如果线程类直接继承 Thread 类,其代码结构大致如下:

创建一个子线程,就是创建一个Thread类型的对象,并且启动。
  Thread类:
 *                 所属包:java.lang;

                 成员变量:
 *                           private Runnable target;
 *              构造方法
 *                             public Thread();

                               public Thread(Runnable r);
 *                            public Thread(Runnable r,String name);

              静态方法:
 *                     static Thread currentThread();
 *            成员方法:
 *                          void start();//该方法不能执行多次
 *                         void run();
 *                        String getName();
 *                        void setName(String name);

先看一段单线程序

package com.apesource.demo02.多线程;
public class Example01 {public static void main(String[] args) {// TODO Auto-generated method stubMyThread myThread=new MyThread();//创建MyThread实例对象myThread.run();//调用MyThread类的run()方法while(true) {System.out.println("Main方法在运行");}}
}
class MyThread{public void run() {while(true) {   //该循环是一个死循环System.out.println("MyThread类的run()方法在运行");}}
}

运行结果:

   可以看出程序一直打印"MyThread"类的run()方法在运行,这是因为该程序是一个单线程程序,当调用MyThread类的run()方法时,遇到死循环,循环会一直进行.因此MyThread类的打印语句将永远执行,而main()方法中的打印语句无法得到执行。

如果希望文件中的两个while循环中的打印语句能够并发执行,就需要实现多线程。为此JDK中提供了一个线程类Thread,通过继承Thread类,并重写Thread类中的run()方法可实现多线程.在Thread类中,提供了一个start()方法,用于启动新线程.线程启动后,虚拟机会自动调用run()方法,如果子类重写了,该方法便会执行子类中的方法.通过修改上面的代码来演示如何通过继承Thread类的方式来实现多线程。

package com.apesource.demo02.多线程;
public class Example01 {public static void main(String[] args) {// TODO Auto-generated method stubMyThread myThread=new MyThread();//创建MyThread实例对象myThread.start();//开启线程while(true) {System.out.println("Main方法在运行");}}
}
class MyThread extends Thread{public void run() {while(true) {   //该循环是一个死循环System.out.println("MyThread类的run()方法在运行");}}
}

运行结果:

运行结果可以看到,两个while循环中的打印语句轮流执行了,说明实现了多线程.为了更好的理解单线程和多线程的执行过程,通过一个图例分析一下:

单线程的程序在运行时,会按照代码的调用顺序执行.而在多线程中,main()方法和MyThread类的run()方法可以同时运行,互不影响,这正是单线程和多线程的区别.

为什么要使用多线程?

本计算机底层来说:线程是程序执行的最小单位,域程间的对接和调度的成本远小于进程。另外,多核CPU时代意味着多个线程可以同时运行,这减少了线程上下文切换的开销。举个例子:假如我们要计算一个复杂的任务,我们只用一个线程的话,只会有一个CPU核心被利用到,而创建多个线程就可以让多个CPU核心被利用到,这样就提高了CPU的利用率。

从互联网发展趋势来说:现在的系统动不动就要求百万级甚至千万级的并发量,而多线程并发编程正是开发高并发系统的基础, 利用好多线程机制可以大大提高系统整体的并发能力以及性能;

练习:

请使用继承Thread类的方式开启两个线程
        (1)第一个线程的名字设置为:a   第二个线程的名字设置为:b
        (2)第一个线程里面实现计算1+2+3+4+....+100的和
        (3)第二个线程里面实现计算1+2+3+4+....+200的和
        程序最终打印结果:

代码:

public class Test02 {public static void main(String[] args) {Thread t1 = new SubThread1();t1.setName("a");t1.start();Thread t2 = new SubThread2();t2.setName("b");t2.start();}
}class SubThread1 extends Thread{@Overridepublic void run() {//获取线程的名称String name = super.getName();int count = 0;for (int i = 1; i <= 100; i++) {count+=i;}System.out.println(name+":"+count);}
}class SubThread2 extends Thread{@Overridepublic void run() {//获取线程的名称String name = super.getName();int count = 0;for (int i = 1; i <= 200; i++) {count+=i;}System.out.println(name+":"+count);}
}

运行结果:

(二)实现Runnable接口创建多线程

 在第二个例子中通过继承Thread类实现了多线程,但是这种方式有一定的局限性.因为java中只支持单继承,一个类一旦继承了某个父类就无法再继承Thread类,例如学生类Student继承Person类,就无法通过继承Thread类创建线程。

为了克服这种弊端,Thread类提供了另外一个构造方法Thread(Runnable target构造方法创建线程对象时,只需为该方法传递一个实现了Runnable接口的实例对象,这样创建的线程将调用实现了Runnable接口的类中的run()方法作为运行代码,而不需要调用Thread类中的run()方法。

通过案例来演示:

public class Test03 {public static void main(String[] args) {//创建Runnable类型的对象Runnable r = new MyRunnable();//Thread t = new Thread(r,"线程1");//创建线程对象t.start();Thread t2 = new Thread(r,"线程2");t2.start();//		//通过匿名内部类的形式创建Runnable类型的对象
//		Runnable r = new Runnable() {
//			@Override
//			public void run() {
//				System.out.println(Thread.currentThread().getName());
//			}
//		};
//		Thread t = new Thread(r,"线程1");
//		t.start();
//		Thread t2 = new Thread(r,"线程2");
//		t2.start();}
}class MyRunnable implements Runnable{@Overridepublic void run() {//获取当前线程Thread currentThread = Thread.currentThread();//获取线程的名称String name = currentThread.getName();System.out.println(name);}
}

MyRunnable类实现了 Runnable接口,并重写了Runnable接口中的run()方法,通过Thread类的构造方法将MyRunnable类的实例对象作为参数传入.从运行结果可以看出,main()方法和run()方法都执行了,说明实现了多线程。

(三)通过线程池获取线程

线程池的作用

线程池作用就是限制系统中执行线程的数量。根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;少了浪费了系统资源,多了造成系统拥挤效率不高。用线程池控制线程数量,其他线程排队等候。一个任务执行完毕,再从队列的中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如果线程池中有等待的工作线程,就可以开始运行了;否则进入等待队列。

线程池好处
       1.降低资源消耗。使用了线程池后线程执行完任务不销毁,再来任务还可以使用该线程执行。
        2.提高响应速度。创建线程池,可以提前启动线程,等待任务的到来。
        3.提高线程的可管理性。提前设置线程的数量

线程池执行流程

  • 提交一个任务,线程池里存活的核心线程数小于线程数corePoolSize时,线程池会创建一个核心线程去处理提交的任务。
  • 如果线程池核心线程数已满,即线程数已经等于corePoolSize,一个新提交的任务,会被放进任务队列workQueue排队等待执行。
  • 当线程池里面存活的线程数已经等于corePoolSize了,并且任务队列workQueue也满,判断线程数是否达到maximumPoolSize,即最大线程数是否已满,如果没到达,创建一个非核心线程执行提交的任务。
  • 如果当前的线程数达到了maximumPoolSize ,还有新的任务过来的话,直接采用拒绝策略处理。

线程池如何创建?
 通过Executors创建

 java.util.concurrent.ExecutorService; 是java线程池框架的主要接口,用Future保存任务的运行状态。  
  Executors:
  静态方法
           static ExecutorService newFixedThreadPool(int nThread);核心线程数
   ExecutorService接口:
  (1)  void execute(Runnable r);提交任务到线程池
  (2)  void shutdown();

核心参数的作用:

线程池可以通过ThreadPoolExecutor来创建
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
                                                        long keepAliveTime,TimeUnit unit,
                                                       BlockingQueue <Runnable> workQueue,
                                                        ThreadFactory threadFactory,
                                                        RejectedExecutionHandler handler)
核心参数的作用:

  • corePoolSize :线程池核心线程数最大值
  • maximumPoolSize :线程池最大线程数大小
  • keepAliveTime :线程池中非核心线程空闲的存活时间大小
  • unit :线程空闲存活时间单位
  • workQueue :存放任务的阻塞队列
  • threadFactory :用于设置创建线程的工厂,可以给创建的线程设置有意义的名字,可方便排查问题。
  • handler :线程池的饱和策略事件。

线程池都有那几种工作队列:

  • AraylockingQueue (有界队列) :用数组实现的有界阻籍队列,按FIFO排序。
  • LinkedBlockingQueue (可设置容量队列) : 基于链表结构的阻塞队列,按FIFO排序任务,容量可以选择进行设置,不设置的话,将是个无边界的阻塞队列,最大长度为Integer.MAX _VALUE,吞吐最通常要高于ArrayBlockingQuene,newFixedThreadPool线程池使用了这个队列;
  • DelayedWorkQueue (延迟队列) :任务定时周期的延迟执行的队列。根据指定的执行时间从小到大排序,否则根据插入到队列的先后排序。newScheduledThreadPool线程池使用了这个队列。
  • PriorityBlockingQueue (优先级队列) :具有优先级的无界阻塞队列;
  • SynchronousQueue (同步队列) : 不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高LinkedBlockingQuene,newCachedThreadPool线程地使用这个队列。
     

线程池的四种拒绝策略是什么?

  • AbortPolicy :默认策略,丢弃任务共抛出ijetedExctionExcetion异常;
  • DiscardPolicy :丢弃任务,不抛出异常;
  • DiscardOldestPolicy :丢弃队列中的末尾任务( 最旧的任务,也就是最早进入队列的任务)后,继续将当前任务提交给线程池;
  • CallerRunsPolicy :交给调用线程池的线程进行处理(谁调用, 谁处理)

常见的线程池及使用场景:    


newFixedThreadPool (固定数目线程的线程池)
线程池特点:

  •                核心线程数和最大线程数大小一样
  •                没有所谓的非空闲时间,即keepAliveTime为0
  •                阻塞队列为无界队列LinkedBlockingQueue

工作机制:

  •                  提交任务
  •                 如果线程数少于核心线程,创建核心线程执行任务
  •                 如果线程数等于核心线程,把任务添加到LinkedBlockingQueue阻塞队列
  •                 如果线程执行完任务,去阻塞队列取任务,继续执行

使用场景:

  • FixedThreadPool适用于处理CPU密集型的任务,确保CPU在长期被工作线程使用的情况下,尽可能的少的分配线程,即适用执行长期的任务。

newCachedThreadPool (可缓存线程的线程池)
   线程池特点:

  •                       核心线程数为0
  •                       最大线程数为Integer.MAX _VALUE
  •                       阻塞队列是SynchronousQueue            
  •                      非核心线程空闲存活时间为60秒
     工作机制:
  •              提交任务
  •             因为没有核心线程,所以任务直接加到SynchronousQueue队列。
  •             判断是否有空闲线程,如果有,就去取出任务执行。
  •             如果没有空闲线程,就新建一个线程执行。
  •            执行完任务的线程,还可以存活60秒,如果在这期间,接到任务,可以继续存活下去;否则,被销毁。

使用场景:

  •             用于并发执行大量短期的小任务。

newSingleThreadExecutor (单线程的线程池)
线程池特点:

  •                    核心线程数为1
  •                    最大线程数也为1
  •                    阻塞队列是LinkedBlockingQueue
  •                    keepAliveTime为0

工作机制:

  • 提交任务
  • 线程池是否有一条线程在,如果没有,新建线程执行任务
  • 如果有,将任务加到阻塞队列
  • 当前的唯线程,从队列取任务,执行完一个,再继续取,一个线程夜以继日地干活。

使用场景:

  • 适用于串行执行任务的场景, -个任务个任务地执行。

newScheduledThreadPool (定时及周期执行的线程池)
     线程池特点:

  •                     最大线程数为Integer.MAX VALUE
  •                     阻塞队列是DelayedWorkQueue
  •                     keepAliveTime为0
  •                     scheduleAtFixedRate():按某种速率周期执行
  •                     scheduleWithFixedDelay() :在某个延迟后执行

   工作机制:

  •                 添加一个任务
  •                 线程池中的线程从DelayQueue中取任务
  •                 线程从DelayQueue中获取time大于等于当前时间的task
  •                执行完后修改这个task的time为下次被执行的时间
  •                这个task放回DelayQueue队列中

使用场景:
周期性执行任务的场景,需要限制线程数量的场景。
 

通过代码演示:

public class Test02 {public static void main(String[] args) {//创建Runnable类型的对象Runnable r = new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName());}};//创建固定线程池ExecutorService e = Executors.newFixedThreadPool(2);//执行任务e.execute(r);//关闭线程池e.shutdown();}
}

运行结果:

打印出来的线程名不是main,说明是通过线程池来创建的

 第二个案例:sleep的目的

1.为了暂停当前线程,设置暂停时间时间结束系统继续调用该线程,减缓当前线程的执行。

2.如果有调用其他接口进行操作,可避免被误认为是恶意攻击

public class Test03 {public static void main(String[] args) {//创建Runnable类型的对象Runnable r = new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName());try {Thread.sleep(10000);         //暂停10秒} catch (InterruptedException e) {e.printStackTrace();}}};Runnable r2 = new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName());}};//创建固定线程池ExecutorService e = Executors.newFixedThreadPool(2);//执行任务e.execute(r);e.execute(r);e.execute(r2);//关闭线程池e.shutdown();}
}

(3)ExecutorService接口:

Future submit(Runnable)提交任务到线程池并返回Future

                   Future<?> submit(Runnable r);

代码演示:

public class Test04 {public static void main(String[] args) {Runnable r = new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName());}};//创建固定线程池ExecutorService e = Executors.newFixedThreadPool(2);//执行任务e.submit(r);//关闭线程池e.shutdown();}
}

(4)ExecutorService接口:

Future submit(Callable)提交任务到线程池并返回Future,call方法的计算结果会封装到Future

         <T> Future<T> submit(Callable<T> r);

代码演示:
 

public class Test05 {public static void main(String[] args) throws InterruptedException, ExecutionException {/** 	通过匿名内部类的方式创建Callable类型的对象* * 	线程执行完任务后,希望获取某个结果。*/Callable<String> c = new Callable<String>() {@Overridepublic String call() throws Exception {System.out.println(Thread.currentThread().getName());return "test callable";}};//创建固定线程池ExecutorService e = Executors.newFixedThreadPool(2);//执行任务Future<String> f = e.submit(c);//获取call方法的返回结果String result = f.get();System.out.println(result);//关闭线程池e.shutdown();}
}

 二.创建线程方式有什么区别?

  1. 实现Runnable和实现Callable接口的方式基本相同,区别在于Callable接口线程执行call()方法后可以有返回值。
  2. 线程类实现Runnable接口或Callable接口,还可以继承其他类,但由于JAVA的单继承性,继承Thread类后,不能在继承其他类.推荐使用接口方式实现线程,系统扩展更灵活。
  3. 线程类实现Runnable接口或Callable接口,比较容易实现多个线程共享同一个target对象,非常适合实现使用多线程处理同一份资源的业务场景,从而可以将CPU,代码和数据分开,形成清晰的代码模型,体现面向对象的思想。
  4. 线程类实现Runnable接口或Callable接口如果需要访问当前线程,必须调用Thread.currentThread()方法。但继承Thread类后,只需要通过this即可;
  5. Thread/Runnable/Callable这三种线程创建方式,如果创建关闭频繁会消耗系统资源,影响性能,而使用线程池可以不用线程的时候放回线程池,用的时候再从线程池取,目前在项目开发中主要使用线程池。

http://chatgpt.dhexx.cn/article/7ZTHnre9.shtml

相关文章

创建线程几种方式

创建线程的几种方式&#xff1a; 方式1&#xff1a;通过继承Thread类创建线程 步骤&#xff1a;1.定义Thread类的子类&#xff0c;并重写该类的run方法&#xff0c;该方法的方法体就是线程需要执行的任务&#xff0c;因此run()方法也被称为线程执行体 2.创建Thread子类的实例&a…

线程创建的四种方式

java中创建线程的四种方法以及区别 Java使用Thread类代表线程&#xff0c;所有的线程对象都必须是Thread类或其子类的实例。Java可以用四种方式来创建线程&#xff0c;如下所示&#xff1a; 1&#xff09;继承Thread类创建线程 2&#xff09;实现Runnable接口创建线程 3&am…

四种线程创建方式

一. 继承Thread类 继承Thread类创建线程的步骤: 创建一个自定义类继承Thread类,重写run()方法,将所要单独线程运行写入run()方法中;创建Thread类的子类的对象;调用该对象的start()方法,该start()方法表示开启线程,然后调用执行run方法; Testpublic void test() {Thread.curre…

创建线程的四种方式

我们创建线程池一般有四种方式&#xff0c;分别是&#xff1a; 1.继承Thread类&#xff0c;重写run()方法&#xff1b; 2.实现Runnable接口&#xff0c;重写run()方法&#xff1b; 3.实现Callable接口&#xff0c;重写call()方法&#xff1b; 4.使用线程池创建线程&#xff1b;…

线程创建常用的四种方式

java中创建线程的四种方法以及区别 Java使用Thread类代表线程&#xff0c;所有的线程对象都必须是Thread类或其子类的实例。Java可以用四种方式来创建线程&#xff0c;如下所示&#xff1a; 1&#xff09;继承Thread类创建线程 2&#xff09;实现Runnable接口创建线程 3&…

银行测试核心项目之测试阶段分享

最近有小伙伴说「想了解核心系统建设中&#xff0c;冒烟、SIT、UAT、回归测试的重点&#xff0c;如何设计测试案例&#xff0c;或相关的资料推荐等」。 这个话题很笼统&#xff0c;测试这一块儿除了业务测试&#xff0c;还有性能测试、安全测试等&#xff1b;以及不同的角色对…

金融银行测试面试题分享

1、网上银行转账是怎么测的&#xff0c;设计一下测试用例。 回答思路&#xff1a; 宏观上可以从质量模型&#xff08;万能公式&#xff09;来考虑&#xff0c;重点需要测试转账的功能、性能与安全性。设计测试用例可以使用场景法为主&#xff0c;先列出转账的基本流和备选流。…

2022年软件测试——精选金融银行面试真题

前言 小伙伴们好久不见呀&#xff0c;现已经到了八月份了过了这一个月就是金九银十了&#xff0c;在这里呢笔者给大家准备了一份软件测试金融方面的面试题&#xff0c;笔者在这里就不多说废话了哟&#xff0c;咱们直接进入正题哈。 1、P2P你们也测试后台管理吗&#xff1f;个人…

金融行业软件测试

金融行业软件测试 一、什么是金融行业二、金融行业的业务特点1. 金融行业的业务特点 三、金融行业测试主要测试范围1. 功能测试2. 业务验证测试3. 客户端测试4. 接口测试5. 性能测试6. 安全性测试 四、金融行业软件测试的现状五、第三方测试的优点 一、什么是金融行业 金融业是…

走出新手村,软件测试银行项目怎么测试 + 面试题(答案)

前言 业务&#xff1a; 银行类app具体模块业务讲解&#xff1a; 掌上生活&#xff08;消费/理财&#xff09; 消费&#xff1a; 与电商平台业务类似 饭票&#xff1a;GPS定位城市服务、饭票购买消费、餐厅查询、代金券、周三五折、banner图、我的饭票、我的抵扣券 影票 …

【工作总结】银行软件测试工作总结

最近换工作进入到外包公司&#xff0c;驻点到银行进行集成测试工作&#xff0c;为了让自己更熟悉金融行业&#xff0c;特别是银行业相关业务&#xff0c;以及总结自己测试的一些经验&#xff0c;希望将自己了解和学习的内容记录下来。主要从以下几个方面学习&#xff1a; 一、…

大咖or学员?金融银行软件测试案例分享!

2019年4月14日汇智动力学院在成都校区开展了本年度的第一次线下技术交流沙龙&#xff08;银行金融测试方向&#xff09;&#xff1b; 刘德宝老师做技术分享 通过在上个月末举办的老学员周末娱乐活动&#xff08;雀圣大赛&#xff09;&#xff0c;我们做了一系列的问卷调查&…

银行软件测试面试问题

测试技术面试题 1、什么是兼容性测试&#xff1f;兼容性测试侧重哪些方面&#xff1f; 参考答案&#xff1a; 兼容测试主要是检查软件在不同的硬件平台、软件平台上是否可以正常的运行&#xff0c;即是通常说的软件的可移植性。 兼容的类型&#xff0c;如果细分的话&#x…

浦发银行软件测试面试真题(小编面试亲测)

对于想要面试去银行面试的小伙伴们看过来&#xff0c;今天为大家总结了浦发银行的面试题&#xff0c;其实很多面试题很多公司都差不多。主要看自己如何灵活回答。以下的问题小编盆友之前面试非浦发银行的银行项目也有被问到过&#xff0c;是在一面的时候被问到的。答案仅作为参…

金融银行软件测试超大型攻略,最受欢迎的金融银行大揭 秘附面试题

零、为什么做金融类软件测试 举个栗子&#xff0c;银行里的软件测试工程师。横向跟互联网公司里的测试来说&#xff0c;薪资相对稳定&#xff0c;加班少甚至基本没有&#xff0c;业务稳定。实在是测试类岗位中的香饽饽&#xff01; 一、什么是金融行业 金融业是指经营金融商…

银行软件测试简历模板,找工作的小伙伴看过来了

目录 目录 个人简历 教育背景 工作经历 自我评价 专业技能 ​编辑 个人简历 姓  名&#xff1a; 性  别&#xff1a; 学  历&#xff1a; 经  验&#xff1a; 手  机&#xff1a; 邮  箱&#xff1a; 政治面貌&#xff1a; 意向岗位&#xff1a; 软…

7个银行的软件测试项目实战,别再说简历项目不知道怎么写了

目录 前言 项目描述1&#xff1a;上海华瑞银行直连票据项目 项目描述2&#xff1a;平安金服风控中台系统 项目描述3&#xff1a;众安创展APP项目 项 目4&#xff1a; 招商银行VTM自助渠道服务系统 项 目5&#xff1a;招商银行票据支付 项 目6&#xff1a; 平安银行风控系…

银行测试——软件测试永远的神

为什么要做金融类软件测试 举个例子&#xff0c;比如银行的软件测试工程师&#xff0c;横向和互联网公司的测试人员比较来说&#xff0c;工资比较稳定&#xff0c;加班很少甚至没有&#xff0c;业务稳定。 实在是测试类岗位中的香饽饽&#xff01;【点击文末小卡片免费领取软件…

软件测试之银行项目重点测试点

在我们的日常在金融或银行软件测试工作中都有哪些内容需要测试&#xff1f;在这些测试的内容中如何去更好的掌握测试技能保证测试质量&#xff0c;一起来学习探讨交流。 下面为银行测试点的概括&#xff1a; 根据上图&#xff0c;我们可以从以下几个方面重点关注&#xff1a; …

【软件测试】银行项目,银行测试业务测试,有哪些侧重点?(总结)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 银行的软件测试是…