进程和线程的区别
- 进程
- 进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的,系统运行一个程序即是一个进程从创建,运行到消亡的过程
- 在 Java 中,当我们启动 main 函数时其实就是启动了一个 JVM 的进程,而 main 函数所在的线程就是这个进程中的一个线程,也称主线程
- 线程
- 线程与进程相似,但线程是一个比进程更小的执行单位
- 一个进程在其执行的过程中可以产生多个线程
- 与进程不同的是同类的多个线程共享进程的堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程
并发和并行
- 并发: 同一时间段,多个任务都在执行
- 并行: 单位时间内,多个任务同时执行
线程的状态
- 线程状态
- new:初始状态,线程被构建,但是还没有调用 start() 方法
- runnable:运行状态,java 线程将操作系统中的就绪和运行两种状态成为 运行中
- blocked:阻塞状态,表示线程阻塞与锁
- waiting:等待状态,表示线程进入等待状态,进入该状态需要其他线程做出一些特定动作(通知或中断)
- time_waiting:超时等待状态,和 waiting 类型,可以在指定时间内返回
- terminated:终止状态,表示线程已经执行完毕
线程的常见方法
- start():启动一个线程
- yield():线程让步,会使当前线程让出 CPU 执行时间片,与其他线程一起重新竞争 CPU 时间片,一般情况下,优先级高的线程有更大的可能性成功竞争得到 CPU 时间片,但这又不是绝对的,有的操作系统对线程优先级并不敏感
- sleep():让前线程休眠,与 wait 方法不同的是 sleep 不会释放当前占有的锁,sleep(long) 会导致线程进入 TIMED-WATING 状态,而 wait() 方法会导致当前线程进入 WATING 状态
- wait():调用该方法的线程进入 WAITING 状态,只有等待另外线程的通知或被中断才会返回,需要注意的是调用 wait() 方法后,会释放对象的锁,因此,wait 方法一般用在同步方法或同步代码块中
- join():在 线程A 中调用 线程B 的 join 方法,使得 线程A 进入阻塞状态,直到 线程B 完全执行完,线程A才结束阻塞状态
- notify():通知一个线程继续运行
- notifyAll():All 的含义是所有的线程,而不是所有的锁,只能唤醒等待(调用 wait() 方法等待)同一个锁的所有线程,必须在当前线程拥有监视器锁的情况下执行,否则将抛出异常IllegalMonitorStateException,任何对象都可以调用,并且无法重写此方法,因为被final修饰;只能释放一把锁,只有一个线程能得到锁,其他没有得到锁的线程会继续保持在等待状态
- interrupted():没有任何强制线程终止的方法,这个方法只是请求线程终止,而实际上线程并不一定会终止,在调用 sleep() 方法时可能会出现 InterruptedException 异常,你可能会想在异常捕获后(try-catch 语句中的 catch)请求线程终止,而更好的选择是不处理这个异常,抛给调用者处理,所以这个方法并没有实际的用途,还有 isInterrupted() 方法检查线程是否被中断
- setPriority():设置线程的优先级,理论上来说,线程优先级高的线程更容易被执行,但也要结合具体的系统;每个线程默认的优先级和父线程(如 main 线程、普通优先级)的优先级相同,线程优先级区间为 1~10,三个静态变量:MIN_PRIORITY = 1、NORM_PRIORITY = 5、MAX_PRIORITY = 10,使用 getPriority() 方法可以查看线程的优先级
- isAlive():检查线程是否处于活动状态,如果线程处于就绪、运行、阻塞状态,方法返回 true,如果线程处于新建和死亡状态,方法返回 false
- LockSupport
- park():所在的线程调用,暂停当前线程
- unpark():另外的线程调用,恢复某个线程的运行
- park、unpark 与 wait、notify 的区别
- wait、notify 必须在有锁的情况下使用(需要关联 Monitor 对象),park、unpark 没有这个限制条件
- park、unpark 配对使用能够精确的指定具体的线程的阻塞/运行,notify 只能随机唤醒一个线程
- park、unpark 配对使用可以先 unpark,wait、notify 配合使用不能够先 notify
sleep() 方法和 wait() 方法区别和共同点
- 两者最主要的区别在于:sleep() 方法没有释放锁,而 wait() 方法释放了锁
- 两者都可以暂停线程的执行
- wait() 通常被用于线程间交互/通信,sleep() 通常被用于暂停执行
- wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的 notify() 或者 notifyAll() 方法,或者可以使用 wait(long timeout) 超时后线程会自动苏醒
- sleep() 方法执行完成后,线程会自动苏醒
实现 Runnable 接口和 Callable 接口的区别
- Runnable 自 Java 1.0 以来一直存在,但 Callable 仅在 Java 1.5 中引入,目的就是为了来处理 Runnable 不支持的用例
- Runnable 接口不会返回结果或抛出检查异常,但是 Callable 接口可以,所以任务不需要返回结果或抛出异常推荐使用 Runnable 接口,这样代码看起来会更加简洁
执行 execute() 方法和 submit() 方法的区别
- execute() 方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否
- submit() 方法用于提交需要返回值的任务,线程池会返回一个 Future 类型的对象,通过这个 Future 对象可以判断任务是否执行成功,并且可以通过 Future 的 get() 方法来获取返回值,get() 方法会阻塞当前线程直到任务完成,而使用 get(long timeout,TimeUnit unit) 方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完