您的位置:首页 > 编程语言 > Java开发

java多线程(1):基础知识

2018-03-29 22:39 387 查看
前言
        Java 给多线程编程提供了内置的支持。 
        一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
        使用多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。

线程与进程

        线程:
        进程中负责程序执行的执行单元,线程必须依靠程序进行运行,线程是程序中的顺序控制流,只能使用分配给程序的资源和环境。
        进程:
        一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。
        单线程:
        程序中只存在一个线程,实际上主方法就是一个主线程
        多线程:
        在一个程序中运行多个任务

并行与并发    

        并行:
        多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
        并发:
        通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力。


线程的状态

        新建状态(New):新创建了一个线程对象。
        就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
        运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
        阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池。    
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)
        死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。



        当需要新起一个线程来执行某个子任务时,就创建了一个线程。但是线程创建之后,不会立即进入就绪状态,因为线程的运行需要一些条件(比如内存资源,像程序计数器、Java栈、本地方法栈都是线程私有的,所以需要为线程分配一定的内存空间),只有线程运行需要的所有条件满足了,才进入就绪状态。
        当线程进入就绪状态后,不代表立刻就能获取CPU执行时间,也许此时CPU正在执行其他的事情,因此它要等待。当得到CPU执行时间之后,线程便真正进入运行状态。
        线程在运行状态过程中,可能有多个原因导致当前线程不继续运行下去,比如用户主动让线程睡眠(睡眠一定的时间之后再重新执行)、用户主动让线程等待,或者被同步块给阻塞,此时就对应着多个状态:time waiting(睡眠或等待一定的事件)、waiting(等待被唤醒)、blocked(阻塞)。

        当由于突然中断或者子任务执行完毕,线程就会被消亡。

上下文切换

        对于单核CPU来说(对于多核CPU,此处就理解为一个核),CPU在一个时刻只能运行一个线程,当在运行一个线程的过程中转去运行另外一个线程,这个叫做线程上下文切换(对于进程也是类似)。
        由于可能当前线程的任务并没有执行完毕,所以在切换时需要保存线程的运行状态,以便下次重新切换回来时能够继续切换之前的状态运行。举个简单的例子:比如一个线程A正在读取一个文件的内容,正读到文件的一半,此时需要暂停线程A,转去执行线程B,当再次切换回来执行线程A的时候,我们不希望线程A又从文件的开头来读取。

        概括的说就是:对于线程的上下文切换实际上就是存储和恢复CPU状态的过程,它使得线程执行能够从中断点恢复执行。
        虽然多线程可以使得任务执行的效率得到提升,但是由于在线程切换时同样会带来一定的开销代价,并且多个线程会导致系统资源占用的增加,所以在进行多线程编程时要注意这些因素。

线程的优先级

        Java线程有优先级,优先级高的线程会获得较多的运行机会。
        Java线程的优先级用整数表示,取值范围是1~10,Thread类有以下三个静态常量:
        static int MAX_PRIORITY
        线程可以具有的最高优先级,取值为10。
        static int MIN_PRIORITY
        线程可以具有的最低优先级,取值为1。
        static int NORM_PRIORITY
        分配给线程的默认优先级,取值为5。
 
        Thread类的setPriority()和getPriority()方法分别用来设置和获取线程的优先级。
 
        每个线程都有默认的优先级。主线程的默认优先级为Thread.NORM_PRIORITY。
        线程的优先级有继承关系,比如A线程中创建了B线程,那么B将和A具有相同的优先级。

        线程优先级特性:
        •       继承性
        比如A线程启动B线程,则B线程的优先级与A是一样的。
        • 规则性
        高优先级的线程总是大部分先执行完,但不代表高优先级线程全部先执行完。
        • 随机性
        优先级较高的线程不一定每一次都先执行完。

守护线程

        在Java线程中有两种线程,一种是User Thread(用户线程),另一种是Daemon Thread(守护线程)。
        Daemon的作用是为其他线程的运行提供服务,比如说GC线程。其实User Thread线程和Daemon Thread守护线程本质上来说去没啥区别的,唯一的区别之处就在虚拟机的离开:如果User Thread全部撤离,那么Daemon Thread也就没啥线程好服务的了,所以虚拟机也就退出了。

参考

Java多线程学习(吐血超详细总结)
Java 多线程编程
Java中的多线程你只要看这一篇就够了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  thread 多线程