Java—多线程创建详解
关注微信公众号:CodingTechWork,一起学习进步。
多线程介绍
线程和进程
进程
- 定义:进程是一块包含了某些资源的内存区域,操作系统利用进程把它的工作划分为一些功能单元。(应用程序是由一个或多个相互协作的进程组成)
- 从资源看:进程是资源分配的最小单位;
- 从基本单位看:进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位;
- 从操作系统资源管理方式看:进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其他进程产生影响,所以多进程的程序要比多线程的程序健壮;在进程切换时,耗费资源较大,效率要差一些;
- 独立性:进程是系统中独立存在的实体,拥有独立的资源,每个进程私有地址空间,一个进程不可直接访问另一个进程的地址空间。隔离性较强。
- 动态性:进程和程序的区别在于,程序只是一个静态的指令集合,进程是一个正在系统中活动的指令集合。进程拥有自己的生命周期和不同的状态。
- 并发性:单个处理器上可以并发执行多个进程,互不干扰。对于一个CPU而言,在某个时间点上只能执行一个程序,即只能运行一个进程,CPU不断地交织执行这些进程,由于CPU的执行速度相对比较快,多个进程轮换执行给用户感觉是多个进程在同时执行。
线程
- 定义:进程中所包含的一个或多个执行单元就称为线程(thread)。
- 从调度看:线程是CPU调度的最小单位;
- 从基本单位看:线程是进程的一个实体,只能归属于一个进程且只能访问该进程所拥有的资源。线程是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位;
- 从资源看:线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源且访问该进程所包含的地址空间;
- 从操作系统资源管理方式看:线程只是一个进程的不同执行路径,线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮;对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程;
- 性能:使用大量的线程会引起过多的上下文切换,影响性能;
- 轻量级进程:线程是进程的执行单元,在一个程序(进程)中,线程是独立的、并发的执行流。进程初始化后,主线程就会被创建(绝大部分引应用程序只需要一个主线程),当有场景需求多线程时,可以在该进程中创建多条顺序执行流,每个线程也是相互独立的。
- 线程资源:线程是进程的组成部分,线程拥有自己的堆栈、程序计数器和局部变量,但不拥有系统资源,与父进程的其他线程共享了该进程的所有系统资源。
- 独立运行:线程是抢占式执行,是独立运行的,当前运行的线程在任何时刻都可能被挂起,便于另一个线程运行。
进程和线程的关系
- 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。
- 资源分配给进程,同一进程的所有线程共享该进程的所有资源。
- 处理机分给线程,即真正在处理机上运行的是线程。
- 线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。
多线程的优势
- 性能高:进程之间不能够共享内存,同一进程下的多线程之间可以共享内存,极大提高程序的运行效率。多线程共享同一个进程的虚拟空间,线程共享环境主要包括进程代码段和进程的公有数据,便于实现线程间的通信。
- 开销小:系统创建进程是需要为该进程重新分配系统资源,创建进程的开销比较大。相对而言,创建线程的代价就小很多,使用的多线程并发多任务比多进程效率高。
- 编程简易:Java内置多线程功能,编程简易。
多线程实现方式
多线程实现的三种方式
1)继承Thread类
创建线程类;
2)实现Runnable接口
创建线程类;
3)使用Callable
和Future
创建线程。
继承Thread类创建线程类
继承Thread类步骤
- 定义
Thread类
的子类,并重写该类的run()
方法,该run()
方法的方法体就代表线程需要完成的任务。run()
即为线程执行体; - 创建
Thread子类
的实例,即创建线程对象; - 调用线程对象的
start()
方法来启动该线程。
常用方法
Thread currentThread()
:Thread类的静态方法,返回当前正在执行的线程对象。void setName()
:Thread类的实例方法,重命名线程名称。String getName()
:Thread类的实例方法,返回调用该方法的线程名称。void start()
:启动线程。run()
:start()方法调用后,执行Thread的run()体。
示例
代码
public class ThreadTest extends Thread {private int i;@Overridepublic void run() {for(; i < 2; i++) {System.out.println("继承Thread启动线程:" + getName() + " : " + i);}setName("Thread-new");for(; i < 4; i++) {System.out.println("重命名后的新线程名:" + Thread.currentThread().getName() + " : " + i);}}public static void main(String[] args) {System.out.println("main线程:" +Thread.currentThread().getName());new ThreadTest().start();}
}
运行结果
main线程:main
继承Thread启动线程:Thread-0 : 0
继承Thread启动线程:Thread-0 : 1
重命名后的新线程名:Thread-new : 2
重命名后的新线程名:Thread-new : 3
实现Runnable接口创建线程类
实现Runnable接口步骤
- 定义
Runnable接口
的实现类,并重写该接口的run()
方法,该run()
是线程执行体; - 创建
Runnable实现类
的实例,并以此实例作为Thread
的target来创建Thread对象
,该Thread对象才是真正的线程对象; - 调用线程对象的
start()
方法来启动该线程。
注意:
实现Runable接口和继承Thread的方式区别:继承Thread创建的线程是创建的Thread子类即可代表线程对象;而实现Runable接口的创建的Runnable对象只能作为线程对象的target。
示例
代码
public class RunnableTest implements Runnable {private int i;@Overridepublic void run() {//不能直接调用getName()和setName()方法,Runnable只有run方法for(; i < 5; i++) {System.out.println("实现Runnable接口创建线程:" + Thread.currentThread().getName() + " : " + i);}}public static void main(String[] args) {System.out.println("main线程:" +Thread.currentThread().getName());RunnableTest runnableTest = new RunnableTest();new Thread(runnableTest).start();//指定线程名称RunnableTest runnableTestWithNewName = new RunnableTest();new Thread(runnableTestWithNewName, "Runnable-Thread-new").start();}
}
常用方法
public abstract void run();
:Runnable接口中只包含一个抽象方法,Runnable接口是函数式接口,可使用Lambda表达式创建Runnable对象。
运行结果
main线程:main
实现Runnable接口创建线程:Thread-0 : 0
实现Runnable接口创建线程:Thread-0 : 1
实现Runnable接口创建线程:Thread-0 : 2
实现Runnable接口创建线程:Runnable-Thread-new : 0
实现Runnable接口创建线程:Thread-0 : 3
实现Runnable接口创建线程:Runnable-Thread-new : 1
实现Runnable接口创建线程:Thread-0 : 4
实现Runnable接口创建线程:Runnable-Thread-new : 2
实现Runnable接口创建线程:Runnable-Thread-new : 3
实现Runnable接口创建线程:Runnable-Thread-new : 4
使用Callable和Future创建线程
使用Callable和Future步骤
- 创建
Callable接口
的实现类,并实现call()
方法,该call()方法即为线程执行体,且该call()
方法有返回值,再创建Callable
实现类的实例; - 使用
FutureTask类
包装Callable对象
,该FutureTask对象
封装了该Callable对象
的call()
方法的返回值; - 使用FutureTask对象作为Thread对象的target创建并通过线程对象的start()方法启动线程;
- 调用FutureTask对象的get()方法获得子线程执行结束后的返回值。
示例
代码
public class CallableFutureTest implements Callable<Integer>{private int i;@Overridepublic Integer call(){for (i = 0; i < 2; i++) {System.out.println("实现Callable接口创建线程: " + Thread.currentThread().getName() + " : " + i);}return i;}public static void main(String[] args) {System.out.println("main线程:" +Thread.currentThread().getName());long begin = System.currentTimeMillis();ExecutorService executorService = Executors.newCachedThreadPool();CallableFutureTest callableFutureTest1 = new CallableFutureTest();CallableFutureTest callableFutureTest2 = new CallableFutureTest();FutureTask<Integer> futureTask1 = new FutureTask<>(callableFutureTest1);FutureTask<Integer> futureTask2 = new FutureTask<>(callableFutureTest2);executorService.submit(futureTask1);executorService.submit(futureTask2);try {System.out.println("futureTask1: "+ futureTask1.get() + "-futureTask2: " + futureTask2.get());} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}finally {executorService.shutdown();}System.out.println("executor pool: " + executorService.isShutdown());System.out.println("time: " + (System.currentTimeMillis() - begin));}}
运行结果
main线程:main
实现Callable接口创建线程: pool-1-thread-1 : 0
实现Callable接口创建线程: pool-1-thread-2 : 0
实现Callable接口创建线程: pool-1-thread-1 : 1
实现Callable接口创建线程: pool-1-thread-2 : 1
futureTask1: 2-futureTask2: 2
executor pool: true
time: 5
Q&A
并行和并发的区别
并行:parallelism,物理上同时执行;多个处理器同时处理多条指令;(单线程永远无法达到并行状态)
并发:concurrency,逻辑上多个任务交织执行;多个进程指令交替执行,同一时刻只有一条指令执行。(宏观上给人一种错觉是多个进程同时执行)
进程与线程的区别
- 调度:线程作为调度的基本单位;进程是资源分配的基本单位。
- 并发性:不仅进程之间可以并发执行;同一个进程的多个线程之间也可并发执行。
- 拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源;
- 系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。但是进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响。
线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个进程死掉就等于所有的线程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。 - 作用:多进程作用是提高CPU的使用率,而不是提高执行速度;
多线程作用是提高应用程序的使用率,而不是提高执行速率。
结论:
- 线程是进程的一部分。
- CPU调度的是线程。
- 系统为进程分配资源,不对线程分配资源。
三种实现线程方式的区别
- 返回值:继承Thread类,run()方法没有返回值;实现Runnable接口和Callable接口方式基本相同,但Callable接口定义的call()方法具有返回值,可以声明抛出异常。
- 继承:继承Thread类,不能再继承其他类;线程类实现Runnable和Callable接口可以继承其他类。
- 访问当前线程:继承Thread类,需要访问当前线程,无须使用Thread.currentThread()方法,直接使用this即可获得当前线程;线程类实现Runnable和Callable接口,若访问当前线程,必须使用Thread.currentThread()方法。多个线程共享同一个target对象,适合多个相同线程来处理同一份资源的情况,从而将CPU、代码和数据分开,形成清晰的模型,较好地体现面向对象思想。
综合:推荐使用线程类实现Runnable和Callable接口方式创建多线程。
参考书籍
《疯狂Java》
《并发编程实战》
Java—多线程创建详解相关推荐
- java多线程设计模式详解[推荐]
java多线程设计模式详解[推荐] java多线程设计模式详解之一 线程的创建和启动 java语言已经内置了多线程支持,所有实现Runnable接口的类都可被启动一个新线程,新线程会执行该实例的run ...
- Java多线程进阶详解
文章目录 1.卖票案例引入数据不安全问题 2.同步代码块 深入理解synchronized关键字 3.同步方法与静态同步方法 同步方法 静态同步方法 内置锁 静态同步方法与同步代码块共同使用 为什么要 ...
- 40个Java多线程问题详解复习
点击上方"朱小厮的博客",选择"设为星标" 后台回复"加群",加入新技术 来源:8rr.co/vXmW 1.多线程有什么用? 一个可能在很多 ...
- java多线程设计模式详解
Java多线程设计模式 线程的创建和启动 Java语言已经内置了多线程支持,所有实现Runnable接口的类都可被启动一个新线程,新线程会执行该实例的run()方法,当run()方法执行完毕后,线程就 ...
- Java多线程编程详解
文章目录 1.多线程的相关概念 2.Java中创建线程 2.1 继承Thread类 2.2 实现Runable接口 2.3 实现Callable接口 3.线程的状态 3.1 线程的5大状态介绍 3.2 ...
- java多线程使用详解与案例,超详细
文章目录 线程lamda表达式方式启动(简单.常用) java使用多线程的三种方式: 继承Thread 实现Runnable 实现Callable 线程池的使用: 守护线程: 使用lamda表达式简化 ...
- Java多线程 - AQS详解
介绍 AQS是java.util.concurrent.locks下类AbstractQueuedSynchronizer的简称,是用于 通过Java源码来构建多线程的锁和同步器的一系列框架,用于Ja ...
- Java多线程超详解
引言 随着计算机的配置越来越高,我们需要将进程进一步优化,细分为线程,充分提高图形化界面的多线程的开发.这就要求对线程的掌握很彻底. 那么话不多说,今天本帅将记录自己线程的学习. 程序,进程,线程的基 ...
- Java 多线程 —— AQS 详解
引言 AQS 是AbstractQuenedSynchronizer 的缩写,抽象的队列式同步器,它是除了java自带的synchronized关键字之外的锁机制.是 JUC 下的重要组件. 相关产物 ...
最新文章
- Where art thou
- MySQL:行锁、表锁、乐观锁、悲观锁、读锁、写锁
- 树还没有叶子的飞鸽传书
- Linux 查看ERROR日志方法
- 通过Ajax异步请求数据
- 开启事物_用一支洁面慕斯,开启精致生活
- php将pdf保存文件到本地,将生成的PDF文件存储在服务器上
- DevOps看起来很美,实现起来却很难?
- TinyXML2 入门教程
- python安装及运行环境_Python 安装及环境搭建
- CAD如何求曲线的交点?
- 集丰照明|如何深度解读 LED 标准?
- MATLAB地图工具箱学习总结(一)从地图投影说起
- 目前最新android处理器排行榜,2017年最新安卓处理器排行榜 骁龙竟然输给了他
- 计算机硬盘电源接口,硬盘电源接口图解
- Batch Normalization详解(原理+实验分析)
- iPhoneX APP界面设计尺寸图-庞姿姿
- 官方示例(十):网页开发3D粒子系统实现降雨效果 ThingJS
- 自动机器学习-AutoGluon: AutoML for Text, Image, and Tabular Data
- iconfont的使用,阿里矢量图库的引用,配置,改变图标大小和图标颜色
热门文章
- OpenGL:glMatrixMode()
- linux那些事之page table
- Scala的异常操作
- tsc - error TS2304 Cannot find name 'Set'/'Promise'
- robotframework自动化测试修炼宝典_自动化测试之框架Cucumber和RobotFramework的实战对比...
- bootstrap带有下拉按钮的输入框_Bootstrap的输入框组样式
- 指点聚源码论坛全站源码 WordPress内核
- 【WP主题】仿下载吧全开源无加密wordpress主题模板内含newzhan2.60无授权版本
- 星辰网址缩短源码支持二维码
- 365赚钱养猫小程序