摘要:多线程基础基本概念进程所谓进程就是运行在操作系统的一个任务,进程是计算机任务调度的一个单位,操作系统在启动一个程序的时候,会为其创建一个进程,就是一个进程。代码就不做过多解释了,没有代码搞个毛的多线程。运行状态拿到的线程开始执行。
多线程基础 基本概念 进程
所谓进程就是运行在操作系统的一个任务,进程是计算机任务调度的一个单位,操作系统在启动一个程序的时候,会为其创建一个进程,JVM就是一个进程。进程与进程之间是相互隔离的,每个进程都有独立的内存空间。
计算机实现并发的原理是:CPU分时间片,交替执行,宏观并行,微观串行。同理,在进程的基础上分出更小的任务调度单元就是线程,我们所谓的多线程就是一个进程并发多个线程。
线程在上面我们提到,一个进程可以并发出多个线程,而线程就是最小的任务执行单元,具体来说,一个程序顺序执行的流程就是一个线程,我们常见的main就是一个线程(主线程)。
想要拥有一个线程,有这样的一些不可或缺的部分,主要有:CPU时间片,数据存储空间,代码。
CPU时间片都是有操作系统进行分配的,数据存储空间就是我们常说的堆空间和栈空间,在线程之间,堆空间是多线程共享的,栈空间是互相独立的,这样做的好处不仅在于方便,也减少了很多资源的浪费。代码就不做过多解释了,没有代码搞个毛的多线程。
继承Thread类,覆盖run方法
实现Runnable接口,覆盖run方法
Runnable并不是线程对象,而是一个任务对象。那么Runnable和Thread有什么样的关系呢?通过查阅API,我们发现创建一个线程除了使用Thread的无参构造方法以外有一个有参构造方法是这样 :Thread(Runnable target),通过这个方法会分配一个新的Thread 对象。
其中的参数是一个类型为Runnable的target属性。
Runnable接口最大的作用就是为非Thread子类的类提供了一种实现线程的方式,只需要实现Runnable接口就可以借助Thread创建一个线程;另一方面,如果只想重写run方法,不想得到其他的Thread的方法,实现Runnable是一个好的选择。
线程池
ExecutorService(线程池 interface)
//通过工具类中的方法能够新建一个线程池,用ExecutorService接受 ExecutorService es = Executors.newFixedThreadPool(2);
Callable对象
类似于Runnable(描述任务的interface)。
//创建一个Callable的实现类 Callabletask1 = new Callable (){ public Integer call() throws Exception{ int result = 0; for(int i=2;i<=100;i+=2){ result += i; Thread.sleep; } return result; } } //用Future对象接收fask1的返回值 将任务提交给线程池 Future f = es.submit(task1); //通过get方法获取Future中的值 在这个时候主线程主动的调取get 如果分支线程还没有结束,主线程会在这里阻塞 int result = f1.get(); //关闭线程池 es.shutdown();
从以上这段代码我们可以看到很多不一样的地方,首先在Callable对象中是可以抛出异常的,其次有返回值,在这个基础上也就引出了一个新的问题,如果接收该线程的对象?JDK1.5中也给出了解决的方法是Future对象.
启动线程
在这里我们需要明白,上面两种方式并不会让我们得到真正的线程,只是得到了线程对象,只有启动线程,才算得到了真正的线程。
通过执行start()方法能够启动一个线程,但是启动线程并不是立即执行,成功启动的线程会处于就绪状态,什么时候执行需要等到拿到时间片之后。
用户线程和守护(Daemon)线程。
守护线程:守护线程会一直运行,直到其他非守护线程都结束的时候,才会结束。有一个典型的守护线程就是:垃圾回收线程,和虚拟机共存亡,直到虚拟机中没有任何线程的时候虚拟机关闭的时候才会终止,简单说就是虚拟机在,它就在,虚拟机亡便亡。
线程的状态上面我们提到过,一个线程在启动之后不会立马执行,而是处于就绪状态(Ready),就绪状态就是线程的状态的一种,处于这种状态的线程意味着一切准备就绪, 需要等待系统分配到时间片。为什么没有立马运行呢,因为同一时间只有一个线程能够拿到时间片运行,新线程启动的时候让它启动的线程(主线程)正在运行,只有等主线程结束,它才有机会拿到时间片运行。
线程的状态:初始状态(New),就绪状态(Ready),运行状态(Running)(特别说明:在语法的定义中,就绪状态和运行状态是一个状态Runable),等待状态(Waitering),终止状态(Terminated)
RUNNABLE),等待状态(Waitering),终止状态(Terminated)
线程对象被创建出来,便是初始状态,这时候线程对象只是一个普通的对象,并不是一个线程。
就绪状态(Ready):执行start方法之后,进入就绪状态,等待被分配到时间片。
运行状态(Running):拿到CPU的线程开始执行。处于运行时间的线程并不是永久的持有CPU直到运行结束,很可能没有执行完毕时间片到期,就被收回CPU的使用权了,之后将会处于等待状态。
等待状态分为有限期等待和无限期等待,所谓有限期等待是线程使用sleep方法主动进入休眠,有一定的时间限制,时间到期就重新进入就绪状态,再次等待被CPU选中。
而无限期等待就有些不同了,无限期并不是指永远的等待下去,而是指没有时间限制,可能等待一秒也可能很多秒。至于进入等待的原因也不尽相同,可能是因为CPU时间片到期,也可能是因为一个比较耗时的操作(数据库),或者主动的调用join方法。
wait和sleep的区别
wait | sleep |
---|---|
wait()方法是Object类里的方法 | sleep()是Thread类的static(静态)的方法 |
wait()睡眠时,释放对象锁 | sleep()睡眠时,保持对象锁,仍然占有该锁 |
常用于线程间通信 | 常用于暂停执行 |
wait和notify/notifyAll是成对出现的, 必须在synchronize块中被调用 |
在我看来,阻塞状态实际上是一种比较特殊的等待状态,处于其他等待状态的线程是在等着别的线程执行结束,等着拿CPU的使用权;而处于阻塞状态的线程等待的不仅仅是CPU的使用权,主要是锁标记,没有拿到锁标记,即便是CPU有空也没有办法执行。(关于锁见下节:线程同步)
等待和阻塞的区别
等待 | 阻塞 |
---|---|
已经拿到锁对象,或者说不存在拿不到执行不了的情况 | 等待拿到锁对象 |
等待被唤醒 | 等待拿到锁对象 |
已经终止的线程会处于该种状态。
总结总体上来说,作为一个线程挺倒霉的,首先,不会知道自己什么时候被选中;其次在执行过程中随时可能被打断让出CPU,最后碰到数据库等耗时的操作也要让出CPU去等待,并且就算数据准备好了, 仍然需要等着被挑选。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/70848.html
摘要:基础知识复习后端掘金的作用表示静态修饰符,使用修饰的变量,在中分配内存后一直存在,直到程序退出才释放空间。将对象编码为字节流称之为序列化,反之将字节流重建成对象称之为反序列化。 Java 学习过程|完整思维导图 - 后端 - 掘金JVM 1. 内存模型( 内存分为几部分? 堆溢出、栈溢出原因及实例?线上如何排查?) 2. 类加载机制 3. 垃圾回收 Java基础 什么是接口?什么是抽象...
摘要:哪吒社区技能树打卡打卡贴函数式接口简介领域优质创作者哪吒公众号作者架构师奋斗者扫描主页左侧二维码,加入群聊,一起学习一起进步欢迎点赞收藏留言前情提要无意间听到领导们的谈话,现在公司的现状是码农太多,但能独立带队的人太少,简而言之,不缺干 ? 哪吒社区Java技能树打卡 【打卡贴 day2...
摘要:线程可以被称为轻量级进程。一个守护线程是在后台执行并且不会阻止终止的线程。其他的线程状态还有,和。上下文切换是多任务操作系统和多线程环境的基本特征。在的线程中并没有可供任何对象使用的锁和同步器。 原文:Java Multi-Threading and Concurrency Interview Questions with Answers 翻译:并发编程网 - 郑旭东 校对:方腾飞 多...
摘要:多线程和并发问题是技术面试中面试官比较喜欢问的问题之一。线程可以被称为轻量级进程。一个守护线程是在后台执行并且不会阻止终止的线程。其他的线程状态还有,和。上下文切换是多任务操作系统和多线程环境的基本特征。 多线程和并发问题是 Java 技术面试中面试官比较喜欢问的问题之一。在这里,从面试的角度列出了大部分重要的问题,但是你仍然应该牢固的掌握Java多线程基础知识来对应日后碰到的问题。(...
阅读 1401·2021-11-24 09:39
阅读 3688·2021-11-24 09:39
阅读 1860·2021-11-16 11:54
阅读 1466·2021-09-30 09:47
阅读 1714·2021-09-26 10:16
阅读 2344·2021-09-22 15:33
阅读 1454·2021-09-14 18:01
阅读 2440·2021-09-07 09:59