一、线程与进程的区别
1、进程是程序的一次执行过程,是程序运行的基本单位
2、进程是比线程更小的执行单位,一个进程可以产生多个线程,多个线程共享进程的堆和方法区,每个线程都有自己独立的栈和程序计数器,因此线程之间切换的开销比进程切换小

二、并发与并行
并发:同一时间段内,多个线程交替执行
并行:同一时刻多个线程同时执行
三、同步与异步
- 同步:发出一个调用之后,在没有得到结果之前, 该调用就不可以返回,一直等待。
- 异步:调用在发出之后,不用等待返回结果,该调用直接返回
四、线程的生命周期和状态(6个)
- NEW: 初始状态,线程被创建出来但没有被调用
start()。 - RUNNABLE: 运行状态,线程被调用了
start()等待运行的状态。 - BLOCKED:阻塞状态,需要等待锁释放。
- WAITING:等待状态,表示该线程需要等待其他线程做出一些特定动作(通知或中断)。比如调用wait(),或者thread.join()等方法
- TIME_WAITING:超时等待状态,可以在指定的时间后自行返回而不是像 WAITING 那样一直等待。thread.sleep(long millis)、wait(long millis)、join(long millis)
- TERMINATED:终止状态,表示该线程已经运行完毕。

sleep与wait对比:
-
sleep()方法没有释放锁,而wait()方法释放了锁 。wait()通常被用于线程间交互/通信,sleep()通常被用于暂停执行。wait()方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的notify()或者notifyAll()方法。sleep()方法执行完成后,线程会自动苏醒,或者也可以使用wait(long timeout)超时后线程会自动苏醒。sleep()是Thread类的静态本地方法,wait()则是Object类的本地方法
join()方法是用于线程之间的协作的一种机制。它是Thread类的一个方法,用于等待调用join()方法的线程(称为子线程)执行完毕后再继续执行当前线程,可以确保线程的执行顺序和协调线程之间的执行。
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// 子线程执行的任务
}
});
thread.start(); // 启动子线程
try {
thread.join(); // 主线程调用子线程的join()方法,等待子线程执行完毕
} catch (InterruptedException e) {
e.printStackTrace();
}
// 子线程执行完毕后,主线程继续执行后面的代码
多线程之间通信的方式:
1、共享变量:使用synchronized关键字或volatile关键字修饰共享变量来确保共享变量在多线程之间的可见性和一致性。
2、等待/通知机制:通过wait()、notify()和notifyAll()等方法,配合synchronized关键字,实现线程之间的等待和唤醒操作。
3、阻塞队列:通过使用BlockingQueue接口的实现类(如LinkedBlockingQueue、ArrayBlockingQueue等),通过阻塞对列,多个线程可以安全地共享队列,并通过队列的put()和take()方法进行数据的传递和阻塞等待。
4、信号量:通过Semaphore类实现多线程之间的信号传递和资源控制。线程可以通过acquire()方法获取信号,而其他线程可以通过release()方法释放信号。
5、条件变量:通过Condition接口和ReentrantLock或ReadWriteLock等类配合使用,实现线程之间的条件等待和唤醒操作。
五、进程的生命周期和状态
-
新建状态(New):该状态表示进程已经被创建,但尚未开始执行或分配资源。
-
就绪状态(Ready):该状态表示进程已经被分配了所有必要的资源,并已经准备好在处理器上运行,只等待处理器的调度。
-
运行状态(Running):该状态表示进程当前正在处理器上执行指令。
-
阻塞状态(Blocked):该状态表示进程暂时无法运行,因为它正在等待某些事件的发生,例如 I/O 操作或者信号量等。
-
终止状态(Terminated):该状态表示进程已经完成了它的任务,并且被操作系统回收了所有占用的资源。
六、线程死锁
多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。
1、四个必要条件
- 互斥条件:该资源任意一个时刻只由一个线程占用。
- 占有并等待条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放。
- 不可抢占条件:线程已获得的资源在未使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
- 循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系
2、如何预防和避免死锁
1、预防死锁
- 破坏请求与保持条件:一次性申请所有的资源。
- 破坏不剥夺条件:占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
- 破坏循环等待条件:靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件。
public class DeadLockDemo {
private static Object resource1 = new Object();//资源 1
private static Object resource2 = new Object();//资源 2
public static void main(String[] args) {
new Thread(() -> {
synchronized (resource1) {
System.out.println(Thread.currentThread() + "get resource1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + "waiting get resource2");
synchronized (resource2) {
System.out.println(Thread.currentThread() + "get resource2");
}
}
}, "线程 1").start();
new Thread(() -> {
synchronized (resource2) {
System.out.println(Thread.currentThread() + "get resource2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + "waiting get resource1");
synchronized (resource1) {
System.out.println(Thread.currentThread() + "get resource1");
}
}
}, "线程 2").start();
}
}