线程和进程的区别
进程是一个独立的运行环境,而线程是进程中执行的一个任务
他们的本质区别是:是否单独占用内存地址空间及其他系统资源
进程是操作系统进行资源分配的基本单位,而线程是操作系统进行调度的基本单位,即CPU分配时间的单位
线程的实现
Java中,JDK提供了Thread类和Runnable接口来让我们实现自己的线程类
- 继承Thread类,并重写run方法
- 实现Runnable接口的run方法
继承Thread类
public class Demo {
public static class MyThread extends Thread {
@Override
public void run() {
System.out.println("MyThread");
}
}
public static void main(String[] args) {
Thread myThread = new MyThread();
myThread.start();
}
}
我们在程序中调用start()方法后,虚拟机会先为我们创建一个线程,然后等到这个线程第一次得到时间片时再调用run()方法
注意:start()方法不可重复调用,重复调用会抛出java.lang.IllegalStateException
异常
实现Runnable接口
public class Demo {
public static class MyThread implements Runnable {
@Override
public void run() {
System.out.println("MyThread");
}
}
public static void main(String[] args) {
new Thread(new MyThread()).start();
// Java 8 函数式编程,可以省略MyThread类
new Thread(() -> {
System.out.println("Java 8 匿名内部类");
}).start();
}
}
Thread类的几个常用方法
- currentThread():静态方法,返回对当前正在执行的线程对象的引用
- start():开始执行线程的方法,java虚拟机会调用线程内的run()方法
- yield():当前线程愿意让出对当前处理器的占用。这里,就算当前线程调用了yield()方法,程序在调度的时候,也还有可能继续运行这个线程的
- sleep():静态方法,使当前线程睡眠一段时间
- join():使当前线程等待另一个线程执行完毕之后再继续执行,内部调用的是Object类的wait方法实现的
面试题
线程的状态有哪些,它是如何工作的
线程的状态以枚举的方式被定义在Thread的源码中,有6个状态:
// Thread.State 源码
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
-
NEW:新建未启动,只是创建了线程而并没有调用start()方法,此时线程处于NEW状态
-
RUNNABLE:表示当前线程正在运行中。处于RUNNABLE状态的线程在Java虚拟机中运行,也有可能在等待CPU分配资源
-
BLOCKED,阻塞等待锁的线程状态,表示处于阻塞状态的线程正在等待监视器锁,比如等待执行 synchronized 代码块或者使用 synchronized 标记的方法
-
WAITING,等待状态,一个处于等待状态的线程正在等待另一个线程执行某个特定的动作,唤醒:Object.notify() 或 Object.notifyAll()
调用如下三个方法会使线程进入等待状态:
- Object.wait():使当前线程处于等待状态直到另一个线程唤醒它;
- Thread.join():等待线程执行完毕,底层调用的是Object实例的wait方法;
- LockSupport.park():除非获得调用许可,否则禁用当前线程进行线程调度
-
TIMED_WAITING,计时等待状态,和等待状态(WAITING)类似,它只是多了超时时间,比如调用了有超时时间设置的方法 Object.wait(long timeout) 和 Thread.join(long timeout) 等这些方法时,它才会进入此状态
-
TERMINATED,终止状态,表示线程已经执行完成
**线程的工作模式:**首先先要创建线程并指定线程需要执行的业务方法,然后再调用线程的 start() 方法,此时线程就从 NEW(新建)状态变成了 RUNNABLE(就绪)状态,此时线程会判断要执行的方法中有没有 synchronized 同步代码块,如果有并且其他线程也在使用此锁,那么线程就会变为 BLOCKED(阻塞等待)状态,当其他线程使用完此锁之后,线程会继续执行剩余的方法
当遇到 Object.wait() 或 Thread.join() 方法时,线程会变为 WAITING(等待状态)状态,如果是带了超时时间的等待方法,那么线程会进入 TIMED_WAITING(计时等待)状态,当有其他线程执行了 notify() 或 notifyAll() 方法之后,线程被唤醒继续执行剩余的业务方法,直到方法执行完成
BLOCKED和WAITING的区别
BLOCKED可以理解为线程还处于活跃状态,只是在阻塞等待其他线程使用完某个锁资源
WAITING主要是自身调用了Object.wait() 或着是 Thread.join() 又或者是 LockSupport.park() 而进入等待状态,只能扽带其他线程执行某个特定动作才可被唤醒
Start()和run()的区别
Start()源码
public synchronized void start() {
// 状态验证,不等于 NEW 的状态会抛出异常
if (threadStatus != 0)
throw new IllegalThreadStateException();
// 通知线程组,此线程即将启动
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
// 不处理任何异常,如果 start0 抛出异常,则它将被传递到调用堆栈上
}
}
}
run()源码
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
start() 方法属于 Thread 自身的方法,并且使用了 synchronized 来保证线程安全
run() 方法为 Runnable 的抽象方法,必须由调用类重写此方法,重写的 run() 方法其实就是此线程要执行的业务方法
从执行的效果来说,start() 方法可以开启多线程,让线程从 NEW 状态转换成 RUNNABLE 状态,而 run() 方法只是一个普通的方法。
start() 方法不能被多次调用,否则会抛出 java.lang.IllegalStateException;而 run() 方法可以进行多次调用,因为它只是一个普通的方法而已
线程优先级有什么用
// 线程可以拥有的最小优先级
public final static int MIN_PRIORITY = 1;
// 线程默认优先级
public final static int NORM_PRIORITY = 5;
// 线程可以拥有的最大优先级
public final static int MAX_PRIORITY = 10
线程的优先级为抢占式,即线程抢占CPU时间片的概率,优先级越高的线程优先执行的概率就越大,注意是有一定可能优先执行,并不是必然
程序中可通过方法:Thread.setPriority(int newPriority)
来设置优先级