先上总结:

1.使用实现多线程有四种方式:①继承Thread类;②实现Runnable接口;③使用Callable和FutureTask实现有返回值的多线程;④使用ExecutorService和Executors工具类实现线程池(如果需要线程的返回值,需要在线程中实现Callable和Future接口)

2.继承Thread类的优点:简单,且只需要实现父类的run方法即可(start方法中含有run方法,会创建一个新的线程,而run是执行当前线程)。缺点是:Java的单继承,如果对象已经继承了其他的类则不能使用该方法。且不能获取线程的返回值

3.实现Runnable接口优点:简单,实现Runnable接口必须实现run方法。缺点:创建一个线程就必须创建一个Runnable的实现类,且不能获取线程的返CallabTask优点:可以获取多线程的返回值。缺点:每个多线程都需要创建一个Callable的实现类

4.线程池ExecutorService和工具类Executors优点:可以根据实际情况创建线程数量,且只需要创建一个线程池即可,也能够通过Callable和Future接口得到线程的返回值,程序的执行时间与线程的数量紧密相关。缺点:需要手动销毁该线程池(调用shutdown方法)。

尽量不要使用 继承Thread类 和 实现Runnable接口;尽量使用线程池。否则项目导出都是线程。

在上代码:

package com.swain.programmingpearls.thread;import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;/*** thread 的几种实现*/
public class threadTest {public static void main (String[] args) {//继承threadExtendsThread extendsThread = new ExtendsThread();extendsThread.start();//实现runnableThread runThread = new Thread(new AchieveRunnable());runThread.start();//调用callable 可以有返回值 可以捕获异常Callable<String> tc = new TestCallable();FutureTask<String> task = new FutureTask<String>(tc);new Thread(task).start();try {System.out.println(task.get());//获取返回值} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}//runable 匿名内部类方式new Thread(new Runnable() {@Overridepublic void run() {System.out.println("实现Runnable 匿名内部类方式:" + Thread.currentThread().getName());}}).start();//runnable Lamda表达式new Thread(()->{for (int i = 0; i < 5; i++) {System.out.println("Lamda表达式:" + i);}}).start();System.out.println("主线程");//创建线程池ExecutorService executorService = Executors.newFixedThreadPool(2);for(int i = 0; i<5; i++){AchieveRunnable achieveRunnable = new AchieveRunnable();try {Thread.sleep(1000);} catch (InterruptedException e) {}executorService.execute(achieveRunnable);}//关闭线程池executorService.shutdown();}/*** 继承thread类*/public static class ExtendsThread extends Thread {public void run(){System.out.println("方法一 继承Thread线程:" + Thread.currentThread().getName());}}/*** 实现runnable*/public static class AchieveRunnable implements Runnable {@Overridepublic void run() {System.out.println("方法二 实现Runnable:" + Thread.currentThread().getName());}}/*** 通过Callable和FutureTask创建线程*/public static class TestCallable implements Callable<String> {@Overridepublic String call() throws Exception {System.out.println("方法三 实现callable:" + Thread.currentThread().getName());return "我是callable的返回";}}
}运行结果:
方法一 继承Thread线程:Thread-0
方法二 实现Runnable:Thread-1
方法三 实现callable:Thread-2
我是callable的返回
实现Runnable 匿名内部类方式:Thread-3
主线程
Lamda表达式:0
Lamda表达式:1
Lamda表达式:2
Lamda表达式:3
Lamda表达式:4
方法二 实现Runnable:pool-1-thread-1
方法二 实现Runnable:pool-1-thread-2
方法二 实现Runnable:pool-1-thread-1
方法二 实现Runnable:pool-1-thread-2
方法二 实现Runnable:pool-1-thread-1

关于Concurrent包

concurrent包是在AQS的基础上搭建起来的,AQS提供了一种实现阻塞锁和一系列依赖FIFO等待队列的同步器的框架。

线程池参数

我们常用的主要有newSingleThreadExecutor、newFixedThreadPool、newCachedThreadPool、调度等,使用Executors工厂类创建。

newSingleThreadExecutor可以用于快速创建一个异步线程,非常方便。而newCachedThreadPool永远不要用在高并发的线上环境,它用的是无界队列对任务进行缓冲,可能会挤爆你的内存。

我习惯性自定义ThreadPoolExecutor,也就是参数最全的那个。

假如我的任务可以预估,corePoolSize,maximumPoolSize一般都设成一样大的,然后存活时间设的特别的长。可以避免线程频繁创建、关闭的开销。I/O密集型和CPU密集型的应用线程开的大小是不一样的,一般I/O密集型的应用线程就可以开的多一些。

threadFactory我一般也会定义一个,主要是给线程们起一个名字。这样,在使用jstack等一些工具的时候,能够直观的看到我所创建的线程。

监控

高并发下的线程池,最好能够监控起来。可以使用日志、存储等方式保存下来,对后续的问题排查帮助很大。

通常,可以通过继承ThreadPoolExecutor,覆盖beforeExecute、afterExecute、terminated方法,达到对线程行为的控制和监控。

阻塞队列

阻塞队列会对当前的线程进行阻塞。当队列中有元素后,被阻塞的线程会自动被唤醒,这极大的提高的编码的灵活性,非常方便。在并发编程中,一般推荐使用阻塞队列,这样实现可以尽量地避免程序出现意外的错误。阻塞队列使用最经典的场景就是socket数据的读取、解析,读数据的线程不断将数据放入队列,解析线程不断从队列取数据进行处理。

ArrayBlockingQueue对访问者的调用默认是不公平的,我们可以通过设置构造方法参数将其改成公平阻塞队列。

LinkedBlockingQueue队列的默认最大长度为Integer.MAX_VALUE,这在用做线程池队列的时候,会比较危险。

SynchronousQueue是一个不存储元素的阻塞队列。每一个put操作必须等待一个take操作,否则不能继续添加元素。队列本身不存储任何元素,吞吐量非常高。对于提交的任务,如果有空闲线程,则使用空闲线程来处理;否则新建一个线程来处理任务”。它更像是一个管道,在一些通讯框架中(比如rpc),通常用来快速处理某个请求,应用较为广泛。

DelayQueue是一个支持延时获取元素的无界阻塞队列。放入DelayQueue的对象需要实现Delayed接口,主要是提供一个延迟的时间,以及用于延迟队列内部比较排序。这种方式通常能够比大多数非阻塞的while循环更加节省cpu资源。

另外还有PriorityBlockingQueue和LinkedTransferQueue等,根据字面意思就能猜测它的用途。在线程池的构造参数中,我们使用的队列,一定要注意其特性和边界。比如,即使是最简单的newFixedThreadPool,在某些场景下,也是不安全的,因为它使用了无界队列。

CountDownLatch

假如有一堆接口A-Y,每个接口的耗时最大是200ms,最小是100ms。

我的一个服务,需要提供一个接口Z,调用A-Y接口对结果进行聚合。接口的调用没有顺序需求,接口Z如何在300ms内返回这些数据?

此类问题典型的还有赛马问题,只有通过并行计算才能完成问题。归结起来可以分为两类:

  • 实现任务的并行性
  • 开始执行前等待n个线程完成任务

CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1。当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。

CyclicBarrier与其类似,可以实现同样的功能。不过在日常的工作中,使用CountDownLatch会更频繁一些。

信号量

Semaphore虽然有一些应用场景,但大部分属于炫技,在编码中应该尽量少用。

信号量可以实现限流的功能,但它只是常用限流方式的一种。其他两种是漏桶算法、令牌桶算法

Lock && Condition

在Java中,对于Lock和Condition可以理解为对传统的synchronized和wait/notify机制的替代。concurrent包中的许多阻塞队列,就是使用Condition实现的。

End

不管是wait、notify,还是同步关键字或者锁,能不用就不用,因为它们会引发程序的复杂性。最好的方式,是直接使用concurrent包所提供的机制,来规避一些编码方面的问题。

多线程的几种实现方式相关推荐

  1. Java多线程的4种实现方式

    ** Java多线程的4种实现方式 ** 1:继承Thread并重写run方法,并调用start方法 /*** Java实现多线程的方式1* 继承Thread类,重写run方法* @author ho ...

  2. 多线程python实现方式_python多线程的两种实现方式(代码教程)

    本篇文章给大家带来的内容是关于python多线程的两种实现方式(代码教程),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 线程是轻量级的进程,进程中可划分出多个线程,线程可独立的调度 ...

  3. Qt多线程的几种实现方式

    Qt多线程的几种实现方式 在Qt中经常会遇到耗时操作,需要并发执行,这个时候就要用到多线程.Qt的多线程有多种实现形式,这里介绍3种方式. 类实现形式 用类实现多线程是最传统的实现形式,思想是写一个继 ...

  4. Java多线程的11种创建方式以及纠正网上流传很久的一个谬误

    创建线程比较传统的方式是继承Thread类和实现Runnable,也可以用内部类,Lambda表达式,线程池,FutureTask等. 经常面试会问到继承Thread类和实现Runnable的区别,然 ...

  5. Java多线程的三种实现方式(重点看Collable接口实现方式)

    1.通过继承Thread类来实现多线程 在继承Thread类之后,一定要重写类的run方法,在run方法中的就是线程执行体,在run方法中,直接使用this可以获取当前线程,直接调用getName() ...

  6. java多线程的6种实现方式详解

    多线程的形式上实现方式主要有两种,一种是继承Thread类,一种是实现Runnable接口.本质上实现方式都是来实现线程任务,然后启动线程执行线程任务(这里的线程任务实际上就是run方法).这里所说的 ...

  7. java多线程实现表复制_Java多线程的三种实现方式

    今天简单说一下Java三种多线程实现方式和区别,主要有实现Runnable.Callable和继承Thread三种方式. 实现Runnable的方式 这种方式比较常用,当我们的线程类有继承其他的类的情 ...

  8. Java创建多线程的8种代码方式

    1.继承Thread类,重写run()方法 //方式1 package cn.itcats.thread.Test1;public class Demo1 extends Thread{//重写的是父 ...

  9. python多线程实现方法_Python3 多线程的两种实现方式

    最近学习 Python3 ,希望能掌握多线程的使用,在此做个笔记.同时也希望Python 牛人指点错误.关于线程的概念,前面简单总结了一下 java 的多线程,传送门:java 多线程概念,三种创建多 ...

最新文章

  1. Reason not to use LINQ and reason to use
  2. python3 asyncio 协程模块
  3. Bochs调试及相关仿真工具的使用方法
  4. 【web】Good ad ~
  5. array DEMO
  6. google天气预报接口_将天气预报添加到谷歌浏览器
  7. 一探即将到来的 C# 10
  8. 程序员面试金典 - 面试题 17.17. 多次搜索(Trie树)
  9. Linux下编译环境及Makefile的学习笔记
  10. 2022年7月深圳地区CPDA数据分析师认证
  11. 邮件合并保存为一个个单独的文档_巧用WPS“邮件合并”功能,让工作更加高效...
  12. vue.config.js跨域配置
  13. 使用pycharm出现黄色框的情况
  14. Shiro RememberMe 1.2.4 反序列化命令执行漏洞复现 kali docker
  15. 单商户商城系统功能拆解47—应用中心—自定义海报
  16. Spring @Scheduled定时任务的fixedRate,fixedDelay,cron的作用和不同
  17. Python 1~500 求和(循环遍历求和,高斯求和,定义函数求和)
  18. chtMultiRegionSimpleFoam求解器的热源不在边界上【翻译】
  19. 连锁店管理系统如何助力零售业
  20. HTML中地图根据数据变色,echarts中国地图根据数据对省份渲染不同的颜色

热门文章

  1. 权限系统(前后端分离)
  2. eclipse创建servlet,filter产生classnotfound错误
  3. 如何用Zabbix监控OpenWrt路由器-Zabbix-Agent安装篇
  4. git clone no matching host key type found. Their offer: ssh-rsa,ssh-dss... 报错
  5. 浅析私有化即时通讯软件的用处有哪些
  6. Bonjour手把手搭建一:mDNS(apple multicastdns.org)
  7. 使用python生成微信好友个性签名词云图
  8. 廖雪峰python3高阶函数部分理解
  9. 基于BP神经网络的车牌识别系统的设计
  10. 2018php最新面试题之PHP核心技术