线程和进程
进程: 是执行中一段程序,即一旦程序被载入到内存中并准备执行,它就是一个进程。进程是表示资源分配的的基本概念,又是调度运行的基本单位,是系统中的并发执行的单位。
线程: 单个进程中执行中每个任务就是一个线程。线程是进程中执行运算的最小单位。
进程和线程的区别
进程 | 线程 |
---|---|
资源分配的基本概念,调度运行的基本单位,系统中并发执行的单位 | 执行运算的最小单位,是程序运行的最小单位 |
一个进程可以有很多个线程 | 一个线程只能属于一个进程 |
切换进程系统开销大,创建进程比较消耗性能 | 切换线程系统开销小,创建线程性能消耗较小 |
进程有独立的内存空间和系统资源 | 所有线程共享进程中的内存空间和系统资源 |
父子进程之间使用进程通信机制进行通信 | 同一进程的线程使用进程变量来进行通信 |
进程死亡之后,不会影响其它进程 | 线程死掉,会导致整个进程死亡 |
线程的创建方式
在Java中,线程的创建方式一共有4中。
1、继承Thread类,并重写run方法。
1 | public class MyThread extends Thread{ |
创建线程,并开启线程。
1 | public static void main(String[] args) { |
【注意】在开启线程的时候,我们必须要调用线程的start方法,因为start方法才会开辟一条线程通道,而如果调用run方法的话,那么就只是一个普通方法而已。
2、实现Runnable接口。
1 | public class MyThread2 implements Runnable{ |
创建线程,并开启线程。
1 | public static void main(String[] args) { |
【注意】由于Runnable接口是一个函数式接口,所以我们可以使用Lombda表达式来创建线程。
1 | new Thread(() -> { |
3、实现Callable接口。
1 | public class MyThread implements Callable<String>{ |
创建线程,并开启线程。
1 | public static void main(String[] args) { |
【注意】由于Callable接口也是一个函数式接口,所以我们也可以使用Lombda表达式来创建线程。
1 | public static void main(String[] args) { |
4、线程池
1 | public static void main(String[] args) { |
【注意】这里直接使用的是Lambda表达式,submit的方法参数可以是Runnable接口和Callable接口的实现类。
Java的线程状态
在Java中,java.lang.Thread包中有一个内部枚举类State,里面列举了Java线程的6中状态。
线程状态 | 描述 |
---|---|
NEW(新建) | 刚刚new出来的线程就处于新建状态。 |
RUNNABLE(运行) | 线程调用start方法,并获得CPU的执行时间片。 |
BLOCKED(锁阻塞) | 在使用同步机制的时候,线程没有获取到同步锁,进入锁阻塞。 |
WAITING(等待) | 线程调用wait方法。 |
TIMED_WAITING(定时等待) | 线程调用sleep方法,或者wait和join带时间参数的方法。 |
TERMINATED(终止) | 线程代码执行完毕,线程终止运行。在已终止的线程上调用start方法,会抛出IllegalThreadStateException异常。 |
线程常用方法
Java中的线程常用方法主要在两个类中,分别是Thread类和Object类。
Thread类
方法 | 描述 |
---|---|
String getName() | 返回此线程的名称。 |
void setName(String name) | 将此线程的名称更改为等于参数 name |
long getId() | 返回此线程的标识符 |
Thread.State getState() | 返回此线程的状态 |
static Thread currentThread() | 返回对当前正在执行的线程对象的引用 |
static void sleep(long millis) | 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行) |
void join(long millis) | 等待这个线程死亡最多 millis毫秒 |
void start() | 导致此线程开始执行; Java虚拟机调用此线程的run方法 |
void run() | 如果这个线程使用单独的Runnable运行对象构造,则调用该Runnable对象的run方法; 否则,此方法不执行任何操作并返回 |
Object类
方法 | 描述 |
---|---|
void wait() | 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法。 |
void wait(long timeout) | 导致当前线程等待,直到另一个线程调用 notify()方法或该对象的 notifyAll()方法,或者指定的时间已过。 |
void notify() | 唤醒正在等待对象监视器的单个线程。 |
void notifyAll() | 唤醒正在等待对象监视器的所有线程。 |
sleep和wait方法的区别
sleep | wait |
---|---|
Thread类中的静态方法 | Object类中的普通方法,由锁对象调用 |
sleep方法不会释放锁 | wait方法会释放锁 |
sleep不需要被唤醒 | 无参的wait方法需要被唤醒 |
sleep不依赖synchronized关键字 | wait方法依赖synchronized关键字 |
synchronized关键字
在多线程环境下,会有可以发送线程安全问题,导致一些共享数据产生错误,这个时候,我们为了避免线程安全问题,就需要对线程代码进行同步。
synchronized关键字是Java线程中用来保证代码同步的关键字。
原理: synchronized关键字是JVM层面的同步,可以保证在同一时刻,只有一个被synchronized关键字修饰的代码块或方法进入临界区。
synchronized关键字的锁对象
- 同步方法块:锁对象为括号内的对象。
- 普通方法:锁对象为当前实例对象。
- 静态方法,锁对象为该类的字节码对象。
synchronized和Lock的区别
synchronized | Lock |
---|---|
Java关键字,JVM层面实现 | Java中的一个类,代码实现 |
只有当synchronized关键字修饰的代码全部执行完毕,才会释放锁。代码发生异常,JVM自动释放锁 | 使用unlock释放锁,否则会造成死锁 |
无法判断锁状态 | 可以判断锁状态 |
可重入、不可中断、非公平 | 可重入、可判断、可公平(两者皆可) |
适合少量同步代码 | 适合大量同步代码 |