线程池启动线程的方式(java启动线程的方法)
admin
2023-09-17 19:04:41
0

我们在学习软件开发时,多线程,高并发是一个必不可少的知识点,也是在面试时必会问到的内容,为了让大家对多线程,高并发编程有个清晰认识,特地组织了一个专栏来专门介绍一下,希望能对大家有一些帮助。

线程简介

线程是程序运行的基本执行单元。当操作系统(不包括单线程的操作系统,如微软早期的DOS)在执行一个程序时,会在系统中建立一个进程,而在这个进程中,必须至少建立一个线程(这个线程被称为主线程)来作为这个程序运行的入口点。因此,在操作系统中运行的任何程序都至少有一个主线程。

进程和线程是现代操作系统中两个必不可少的运行模型。在操作系统中可以有多个进程,这些进程包括系统进程(由操作系统内部建立的进程)和用户进程(由用户程序建立的进程);一个进程中可以有一个或多个线程。进程和进程之间不共享内存,也就是说系统中的进程是在各自独立的内存空间中运行的。而一个进程中的线可以共享系统分派给这个进程的内存空间。

在进一步介绍之前,我们先来了解一些基本概念,以帮助大家更快速的理解。

基本概念

进程

进程是操作系统中正在执行的不同的应用程序,例如:我们可以同时打开微信和QQ,甚至更多的程序。

在操作系统中运行的程序就是进程,进程就是执行程序的一次执行过程,它是一个动态的概念式系统资源分配的单位

通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义,线程是CPU调度和执行的单位

线程

线程是一个应用程序进程中不同的执行路径,例如:我们的WEB服务器,能够为多个用户同时提供请求服务。

进程是不活泼的,进程从来不执行任何东西,它只是线程的容器。线程总是在某个进程环境中创建的,而且它的整个生命周期都在该进程中。

在一个进程中,如果创建了多个线程,线程的运行是由调度器安排调度的,调度器是与操作系统紧密相关的,先后顺序是不能人为干预的

多线程

多线程拥有多条执行路径,「主线程与子线程并行交替执行」(普通方法只有主线程一条路径),对同一份资源操作时,会存在资源抢夺的问题,这时就需要加入并发控制了。

一个Java应用程序,至少有三个线程: main()主线程, gc()垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。

多线程程序的优点:

  • 提高应用程序的响应。对图形化界面更有意义,可增强用户体验。同时做多个事情。比如:一边听歌、一边写代码。
  • 提高计算机系统CPU的利用率。不过线程也会带来额外的开销,如CPU调度时间,并发控制带来的系统开销。
  • 改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改。

何时需要多线程?

程序需要同时执行两个或多个任务。

需要一些后台运行的程序时,比如:Java后台运行的GC线程。

创建线程

Java中创建线程有四种方式,我们下面依次介绍一下。

1、继承 Thread 类

(1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。

(2)创建Thread子类的实例对象,即创建了一个线程对象。

(3)调用该线程对象的start()方法来启动该线程。

示例代码:

public class MyThread extends Thread { // 总票数 public int count = 10; @Override public void run() { // 当还有票时就继续售卖 while (count > 0) { // 剩余票数 count--; System.out.println( Thread.currentThread().getName() + "售卖第 " + (10 - count) + " 张票,当前剩余票数: " + count); } } public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); } }

2、实现Runnable接口

(1)定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。

(2)创建 Runnable实现类的实例对象,并将该实例作为Thread的target来创建一个Thread对象,该Thread对象才是真正的线程运行对象。

(3)调用该线程对象的start()方法来启动线程。

示例代码:

public class MyRunableThread implements Runnable { // 总票数 public int count = 10; @Override public void run() { // 当还有票时就继续售卖 while (count > 0) { // 剩余票数 count--; System.out.println( Thread.currentThread().getName() + "售卖第 " + (10 - count) + " 张票,当前剩余票数: " + count); } } public static void main(String[] args) { MyRunableThread myRunableThread = new MyRunableThread(); Thread myThread = new Thread(myRunableThread); myThread.start(); } }

「Thread 和 Runnable 的区别」

上述两种方法是大家最常见到的两种创建线程的方法,也常常会被问到两种方式创建线程的区别,下面简单总结了一下:

「继承 Thread 类」

子类继承 Thread 类具备多线程能力

启动线程:子类线程对象调用 .start()方法

不建议使用:避免 OOP 单继承局限性

「实现接口 Runnable」

  • 具有多线程能力
  • 启动线程:传入目标对象 + Thread对象调用.start()方法
  • 推荐使用:避免单继承局限性,方便同一个对象被多个线程使用

另外,在使用线程池时只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类

3、实现Callable接口

(1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。

(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。

(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。

(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。

示例代码:

public class MyCallableThread implements Callable { // 总票数 private int count = 10; @Override public String call() throws Exception { // 当还有票时就继续售卖 while (count > 0) { // 剩余票数 count--; System.out.println( Thread.currentThread().getName() + "售卖第 " + (10 - count) + " 张票,当前剩余票数: " + count); } return "票已售完"; } public static void main(String[] args) throws InterruptedException, ExecutionException { Callable callable = new MyCallableThread(); FutureTask futureTask = new FutureTask<>(callable); Thread myThread = new Thread(futureTask); myThread.start(); // 打印返回结果 System.out.println(futureTask.get()); } }

「Runnable和Callable的区别:」

Callable规定的方法是call(),Runnable规定的方法是run()。

Callable的任务执行后可返回值,而Runnable的任务是不能有返回值。

call方法可以抛出异常,run方法不可以。

4、线程池

Java默认提供了五种线程池,通过Executors创建,分别为:

  • 「newCachedThreadPool」 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
  • 「newFixedThreadPool」 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
  • 「newScheduledThreadPool」 创建一个定长线程池,支持定时及周期性任务执行。
  • 「newSingleThreadExecutor」 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
  • 「newWorkStealingPool」 创建一个具有抢占式操作的线程池,由于能够合理的使用CPU进行对任务操作(并行操作),所以适合使用在很耗时的任务中。

示例代码:

public class MyThreadPool implements Runnable { // 总票数 public int count = 10; @Override public void run() { // 当还有票时就继续售卖 while (count > 0) { // 剩余票数 count--; System.out.println( Thread.currentThread().getName() + "售卖第 " + (10 - count) + " 张票,当前剩余票数: " + count); } } public static void main(String[] args) { ExecutorService ex = Executors.newFixedThreadPool(5); MyThreadPool t = new MyThreadPool(); ex.submit(t); ex.shutdown(); } }

关于线程池,后续会有单独文章给大家详细介绍。

通过以上的内容,希望大家可以对线程有个初步的认识,相关示例代码,稍后整理后我会上传到GitHub上,也请大家留意我们的后续文章。

相关内容

热门资讯

最高法发布5起典型案例 规范平... 今天(8月3日),最高人民法院发布5起依法规范平台经营、保护消费者合法权益典型案例。平台经济是数字经...
震感明显!四川宜宾高县发生4.... 据中国地震台网正式测定,2026年8月3日2时21分,在四川宜宾市高县(北纬28.54度,东经104...
局地特大暴雨!中央气象台发布暴... 中央气象台8月3日6时发布暴雨橙色预警和强对流天气蓝色预警。暴雨橙色预警预计,8月3日8时至4日8时...
美国波特兰发生枪击事件 致1死... 央视记者获悉,当地时间8月2日凌晨,美国俄勒冈州波特兰一处住宅发生大规模枪击事件,造成包括一名青少年...
广西灵山县一公司在建楼发生火灾... 2026年8月2日12时许,广西钦州灵山县十里工业园区园园丰食品有限公司一在建楼发生火灾,接报后,灵...
西南、华南等地有强降水 国家防... 8月2日上午,国家防总办公室、应急管理部组织中国气象局、水利部、自然资源部、工业和信息化部、住房城乡...
“欧佩克+”主要产油国宣布9月... 当地时间8月2日,石油输出国组织(欧佩克)发布声明称,沙特阿拉伯、俄罗斯、伊拉克、科威特、哈萨克斯坦...
国家外汇局:将继续稳步扩大外汇... 记者8月2日从国家外汇管理局获悉,今年下半年,外汇管理部门将继续稳步扩大外汇领域制度型开放。国家外汇...
解放军报钧声:中方加强黄岩岛管... 近期,菲律宾在中国领土黄岩岛划设所谓“领海基线”,妄图为2016年那场非法仲裁“续命”,并掀起新一轮...
驻日美军多名士兵涉嫌酒驾和猥亵... 日本冲绳县近日发生多起驻日美军士兵被捕案件,其中两人涉嫌酒驾,一人涉嫌公然猥亵。据冲绳县警方通报,2...
伊朗称重开霍尔木兹海峡计划纯属... 据伊朗方面2日消息,有消息人士称,重开霍尔木兹海峡的计划纯属谣言。一名相关消息人士说,目前尚未就重开...
中东部高温闷热持续 热带低压或... 今天,本轮中东部高温闷热天气持续,多地继续“全天桑拿”模式,动不动就大汗淋漓,户外仿佛大蒸笼,走出空...
看完《八仙!》,来这里“沉浸式... 最近,动画电影《八仙!》热映影片中青绿山水间的蓬莱仙境让无数观众心驰神往而真实的蓬莱比电影里还要“仙...
水利部针对重庆市启动洪水防御Ⅳ... 据预报,8月2日至4日,重庆将有中到大雨,其中重庆中部和东南部将有暴雨,局部将有大暴雨;长江上游干流...
巴基斯坦雪崩致1名中国公民失联... 7月30日,一支国际登山队在喀喇昆仑山脉布洛阿特峰遭遇雪崩后失联。8月1日,中国驻巴基斯坦大使馆领侨...
5年期大额存单“返场”!最高利... 曾经因银行净息差压力过大而一度“绝迹”的5年期大额存单,又悄悄“返场”了。7月份以来,农业银行、中国...
美政府建议中东地区美国公民做好... 当地时间8月1日,美国国务院向身处中东地区的美国公民发出广泛安全警告,建议他们“考虑离开当地,或做好...
日本熊本县地震已致38人死亡 据日本熊本县政府当地时间8月2日消息,包括仍在进行关联性调查的死者在内,熊本县7月28日发生的强震已...
莫斯科餐厅爆炸已致3死21伤 ... 当地时间8月1日,俄罗斯国家反恐委员会表示,莫斯科市中心一家餐厅发生的事故系自制爆炸装置爆炸所致。通...
26人流窜多地盗墓17次 一无... 他说干好了,一人能整个二三百万,我说那好事那还不干?内蒙古自治区通辽市扎鲁特旗,曾孕育过历史上多个民...