进程和线程的区别:
进程是程序运行的基本单位,启动一个程序就有对应的一个进程 ,例如每次运行一个Java程序就对应一个JVM进程。
线程表示程序中需要执行的任务,一个进程可以创建多个线程,对线程操作的开销比进程小的多。线程间可以共享数据,而每个进程拥有自己的一整套变量。
线程API
java.lang.Thread
线程的创建、启动(继承Thread
类)
Thread
类中有一个run
的方法,继承Thread
类,重写run
方法,实例化继承类,调用start
方法,即可向JVM申请开启一个线程。
java.lang.Thread::run
|
|
java.lang.Thread::start
|
|
可以看到不能直接调用run方法,这样还是单线程的调用。
Runnable
接口
另一种实现多线程的方法是实现Runnable
接口,并使用Thread
类的构造器,结合Lambda可以写成如下格式:
|
|
这种方法是更优的,因为Java中实现一个接口比继承一个类更加便于扩展。
线程属性
属性 | 属性类型及用途 | 只读属性 | 注意事项 |
---|---|---|---|
编号(ID) | long,不同线程有不同的编号 | √ | 线程编号的唯一性只在JVM的一次运行中有效,类比Socket的连接编号 |
名称(Name) | String,面向开发者的属性,默认值格式为”Thread-0,1,2…” | × | 名称可以相同 |
线程类别(Daemon) | boolean,是否为守护线程 | × | 必须在调用start 之前设置,负责处理关键任务处理的不适宜设置为守护线程 |
优先级(Priority) | int,1-10 默认为5,对于具体的某个线程,其优先级和父线程优先级相同 | × | 一般使用默认即可 |
Thread
类常用方法
Thread.currentThread()
返回当前线程。
join()
如果线程A调用线程B的join方法,那么A的会暂停,直到线程B运行结束。
Thread.yield()
使当前线程主动放弃对其处理器的占用,可能导致当前线程被暂停
Thread.sleep()
线程暂停指定的时间。
线程关系
Java程序在启动时总是找到Main方法,JVM在这个时候会创建一个main线程,所以之后所有的线程都是main线程的子线程。
线程状态
一个线程的状态可以通过Thread.getState()
来获取,该方法返回一个枚举类型:
|
|
所以以上就是线程的所有状态:
NEW
已创建但未启动。因为线程只能被启动一次,所以NEW状态只有一次。
RUNNABLE
可以被线程调度器调度或者正在运行。
BLOCKED
发起阻塞式I/O或者等待其他线程占有的资源时,解除此状态会回到RUNNABLE。
WAITING
执行了某些特定方法如:Object.wait()
Thread.join()
LockSupport.park(Object)
就会到此状态。
执行了如Object.notify()/notifyAll()
LockSupport.unPark(Object)
会回到RUNNABLE状态。
TIMED_WAITING
有限的WAITING,到时间了如果其他线程没有执行该线程所期望的特定操作时,该线程会转为RUNNABLE。
TERMINATED
终止。 run正常执行或者异常而提前终止都会到该状态,仅能在该状态一次。
多线程编程的优势和风险
优势
- 提高系统的吞吐率。
- 提高响应性。
- 充分利用多核。
- 最小化系统资源使用。线程在进程内可以共享其所在进程所申请的资源。
- 简化程序的结构。
风险
- 线程安全。数据一致性问题。
- 线程活性问题。死锁、活锁、线程饥饿。
- 上下文切换。在一个线程转向另一个线程的时候叫做上下文切换,这个步骤增加了系统的消耗。
- 可靠性。如果进程因为内存泄漏而意外终止,那么该进程中的所有线程也会终止。