多线程的概念
进程是程序运行的实例。启动一个java程序其实就是启动了一个jvm的进程。进程是程序向操作系统申请资源(内存空间、文件句柄等)
的基本单位。
线程是进程中可以独立执行的最小单位。
进程和线程的关系,可以比喻成一个项目组和组员的关系。项目组完成一个项目需要需求,开发,测试。这些往往都是并行的。需要需求,开发和测试人员协作完成。他们共享项目组的资源,如需求文档,功能代码等。
为什么需要线程
- 提高cpu的利用率。因为cpu的执行速率是远远高于io操作的。如果单线程运行,遇到io操作,系统会一直等待io完成再往下执行。这段io操作的时间内浪费了cpu的性能。
- 提高响应速率。为了用户的体验,在使用GUI软件时,一个慢的IO操作不至于会使软件”冻住”。
- 充分利用多核。对应已经普及的多核计算机,多线程可以重复利用其性能。
java中的线程
java中的线程是通过java.lang.Thread类来实现。一个Thread或其子类就是一个线程。
创建线程
创建执行线程的方式有三种:
Thread类的构造函数大概可以分为要Runnable和不需要Runnable两种。 Thread()和 Thread(Runnable)。所以可以根据这两种方式来创建一个线程。
继承Thread
1 | //1.继承Thread类 |
实现Runnable接口
1 | //1.实现Runnable类 |
实现callable接口
上面两种线程都没有返回值,jdk1.5后出现了callable能创建具有返回值的线程。
1 | //实现Callable接口 |
三种创建线程方式的差异
callable和runnable除了callable可以有返回值,其他的都一样。实际上callable最终会被构造成一个实现Runnable接口的类。例如上面的FutureTask。
主要分析Runnable方式和继承Thread这两种方式。
继承Thread是通过继承的方式,实现Runnable是一种组合的方式。组合相对于继承更加低耦合和灵活。《Effictive java》中也提到复合优先于继承的观点。
实现Runnable的方式,容易线程间实现资源的共享。因为一个Runnable对象可以用来创建多个线程。对象的成员变量被多个线程共享。这也使得我们要注意线程安全问题。
线程的属性
属性 | 作用 |
---|---|
编号(ID) | 用于标识不同线程 |
名称(Name) | 用于区分不同线程 默认:”thread-0” .可以设置。 |
线程类别(Daemon) | 可以设置守护线程。主线程结束,守护线程也会结束 |
优先级(Priority) | 可以设置线程优先级,1-10.默认5. |
线程的状态
状态 | 说明 |
---|---|
创建(New) | 线程被创建未启动。java中就是还没调用start方法之前 |
可运行(Runnable) | 该状态下有两种情况,一种是获取到cpu的时间片 |
阻塞(BLOCKED) | 发起阻塞IO或者争取独占锁资源。该状态不占用cpu |
等待(Waiting) | 执行特定的方法会,等待其他线程唤醒 |
有时限等待(Timed Waiting) | 和Waiting状态类似,但是Timed Waiting是有时限的,指定时间到会结束等待状态 |
消亡(Terminated) | run方法执行完毕或者发生异常。 |