进程
进程是程序的一次执行过程,系统程序的基本单位。有自己的main方法,并且主要由主方法运行起来的基本上就是进程。
线程
线程与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程。与进程不同的是同类的多个线程共享堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。
线程创建的方式
-
继承 Thread 类,重写 run 方法
-
实现 Runnable 接口,重写 run 方法
-
实现 Callable 接口,重写 call 方法
这种形式与实现 Runnable 接口很像,但可以返回一个结果
-
线程池创建
之所以要重写 run 方法,是因为 run 方法内根本什么都没有,所以需要重写,赋予具体的功能。
线程的状态
-
新建(New):当线程对象被创建但还没有调用
start()
方法时,线程处于新建状态。在这个状态下,线程还没有开始执行。 -
就绪(Runnable):线程调用了
start()
方法后进入就绪状态。在这个状态下,线程已经准备好运行,等待操作系统分配CPU时间片。 -
运行(Running):当线程获得CPU时间片并开始执行时,它进入运行状态。在这个状态下,线程正在执行它的任务。
-
阻塞(Blocked):线程在等待某个事件(如I/O操作、获取锁、等待信号量等)时进入阻塞状态。在这个状态下,线程暂时停止执行,直到它等待的事件发生。
-
等待(Waiting):线程通过调用
wait()
、join()
或park()
等方法进入等待状态。在这个状态下,线程会无限期地等待,直到另一个线程调用notify()
、notifyAll()
或unpark()
方法来唤醒它。 -
计时等待(Timed Waiting):线程通过调用具有超时参数的方法(如
sleep(long)
、wait(long)
、join(long)
、parkNanos()
或parkUntil()
)进入计时等待状态。在这个状态下,线程会在指定的时间后自动恢复到就绪状态。 -
终止(Terminated):线程完成执行或因异常退出时进入终止状态。在这个状态下,线程不再执行,它的生命周期结束。
线程状态转换
线程的状态可以通过不同的操作进行转换,例如:
-
从新建状态调用
start()
方法,线程进入就绪状态。 -
从就绪状态获得CPU时间片,线程进入运行状态。
-
从运行状态调用
sleep(long)
方法,线程进入计时等待状态。 -
从运行状态调用
wait()
方法,线程进入等待状态。 -
从运行状态调用
join()
方法,线程进入等待状态,等待另一个线程结束。 -
从运行状态调用
park()
方法,线程进入等待状态。 -
从阻塞状态等待的事件发生,线程进入就绪状态。
-
从等待状态被
notify()
或notifyAll()
唤醒,线程进入就绪状态。 -
从计时等待状态超时,线程进入就绪状态。
-
从运行状态任务完成或抛出未捕获的异常,线程进入终止状态
当两个线程分别thread.start()时,只是将线程变成了就绪态,不是运行态,所以运行的顺序,还不一定。
wait(),join(),park()的区别
wait
wait()
方法是Object
类的一部分,因此所有对象都有这个方法。
wait 用于让一个线程进入等待状态,可以做到线程之间的通信,它会释放该对象的锁,并进入等待状态。直到其他线程调用同一个对象的notify()
或notifyAll()
方法来唤醒它。
通常用于生产者-消费者模式中,当生产者生产数据后,生产者调用notify()
或notifyAll()
来唤醒可能正在等待数据的消费者线程。
join
join()
方法是Thread
类的一部分。
join 用于控制线程之间的顺序。当一个线程调用另一个线程的join()
方法时,它会阻塞自己,直到被调用的线程执行完毕。
例子:
public class Main{public static void main(String[] args) {System.out.println("Main 线程 开始运行!");Thread t1 = new Thread(){@Overridepublic void run(){System.out.println("t1 开始运行!");System.out.println("t1 结束运行!");}};try{t1.start();t1.join();}catch(Exception e){}System.out.println("Main 线程 结束运行!");}
}
/*
Main 线程 开始运行!
t1 开始运行!
t1 结束运行!
Main 线程 结束运行!
*/
park
park()
方法是java.util.concurrent.locks.LockSupport
类的一部分。
它更自由,park()
方法用于暂停当前线程的执行。不需要像 wait 一样,需要持有特定的锁,它直接使用就可以。
线程与进程的关系,区别及优缺点?
图解关系
下图是 Java 内存区域,通过下图我们从 JVM 的角度来说一下线程和进程之间的关系。
从上图可以看出:一个进程中可以有多个线程,多个线程共享进程的堆和方法区 (JDK1.8 之后的元空间)*资源,但是每个线程有自己的*程序计数器、虚拟机栈 和 本地方法栈。
总结
线程是进程划分成的更小的运行单位。线程和进程最大的不同在于基本上各进程是独立的,而各线程则不一定,因为同一进程中的线程极有可能会相互影响。线程执行开销小,但不利于资源的管理和保护;而进程正相反。