1.进程和线程的区别?
-
进程
- 是操作系统分配资源的基本单位。
- 一个程序运行就会创建一个进程。
- 进程通常是独立存在的,拥有自己独立的资源。
- 多个进程不能共享资源,每个进程都有自己的堆、栈、虚存空间等,进程之间不会互相干扰
-
线程
- 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。
- 一个进程至少包含一个线程,也可以多个,线程属于进程。
- 一个线程可以创建和撤销另一个线程,同一进程中的多个线程之间可以并发执行。
-
区别
-
进程是由操作系统管理,线程是由编程的人员编写管理。
-
进程是正在运行的程序,进程中包含线程,线程属于进程。
-
进程是操作系统分配资源的基本单位,而线程是操作系统调度的基本单位。
-
多个进程间不能共享资源,而线程可以共享进程资源文件(堆和方法区)。
-
2.实现线程有哪些方式?
- 继承Thread类
- 继承 Thread 类 extends Thread
- 重写run()方法
- 调用线程类的start()方法
public class Thread1 extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
//sleep()让程序暂停一段时间,单位是毫秒
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + ": " + i);
}
}
}
public static void main(String[] args) {
Thread1 thread1 = new Thread1("线程一");
thread1.start();
}
- 实现Runnable接口
- 实现Runnable接口, 重写run方法
- 创建一个Thread对象,构造方法参数是实现了Runnable接口类的对象
- 调用Thread对象的start方法启动线程
public class Thread2 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
}
public static void main(String[] args) {
Thread2 t1 = new Thread2("线程一");
Thread t = new Thread(t1);
t.start();
}
- 实现Callable接口
- 实现Callable接口,重写call方法
- 创建FutureTask对象,构造方法是实现了Callable接口的对象
- 创建Thread的对象, 构造方法的参数是FutureTask的对象
- 调用start()方法启动线程
import java.util.concurrent.Callable;
public class Thread3 implements Callable<String> {
@Override
public String call() throws Exception {
for (int i = 0; i < 10; i++) {
Thread.sleep(1);
System.out.println(Thread.currentThread().getName() + ": " + i);
}
return Thread.currentThread().getName();
}
}
public static void main(String[] args) {
Thread3 call1 = new Thread3();
//创建FutureTask对象,构造方法的参数是实现了Callable接口的对象
FutureTask<String> futureTask1 = new FutureTask<>(call1);
//创建Thread对象,构造方法的参数FutereTask对象
Thread t1 = new Thread(futureTask1);
//启动线程
t1.start();
}
3.简述线程的生命周期?
线程的生命周期分为五个阶段:创建、就绪、运行、阻塞、结束。
-
创建
新创建了一个线程对象,还未调用start()方法。 如 Thread thread = new Thread();。
-
就绪
线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中 获取cpu 的使用权 。
-
运行
运行状态,当线程被激活执行,这时候线程就是运行状态,执行run()方法。
也有可能因为cpu时间片用完,或是其他方法被调用,回到就绪状态。 -
阻塞
阻塞状态,暂停运行, 正在运行的线程遇到wait(),sleep(),或者是同步资源被占用时会被阻塞。
被wait()阻塞的线程需要被特定的方法唤醒才能重新回到就绪状态。而被sleep()阻塞的线程只需要等待sleep时间结束就可以重新回到就绪状态。
-
结束
终止状态,run()正常执行完毕,就进入到终止状态,线程生命周期就结束。
4.什么是并发?并发会导致什么问题?怎么解决并发的问题?
-
并发
多个线程在执行任务的时候,同时(并行)访问同一个资源(方法或者属性)。
-
并发产生的问题
可能导致数据的不一致,错乱。
-
怎么解决并发问题
给属性或方法添加synchronized(同步)关键字,就是给资源上锁,有线程使用时锁住,使用完就解锁。
5.同步和异步的区别?
-
同步
同步是线程按顺序执行,就是当A请求一个资源的时候,这个资源正在被B使用,那么A就必须要等待,等到B使用完了A才能请求到。
-
异步
异步是多个线程同时进行,就是当A请求一个资源的时候,这个资源正在被B使用,A不需要等也可以请求到。
-
区别
- 同步需要等待,异步不需要等待。
- 同步是安全的,异步不安全可能会导致产生并发问题。
6.死锁形成的条件是什么? 怎样避免死锁?
-
死锁
两个线程相互等待对方释放它锁定的资源,两个线程都没有办法继续执行,程序就一直处于等待状态,不会结束,也不能成功运行,这种现象就是死锁。
-
死锁形成的条件
- 互斥条件:一个资源只能被一个线程占有,当这个资源被占有后其他线程就只能等待
- 不可剥夺条件:当一个线程不主动释放资源时,此资源一直被拥有线程占有
- 请求并持有条件:线程已经拥有了一个资源后,又尝试请求新的资源
- 环路(循环)等待条件:产生死锁一定是发生了线程资源环形链,就是A线程持有资源1等待资源2, B线程持有资源2等待资源1。
-
避免死锁
- 以相同的顺序去所锁定资源
- 另外建立一个锁的对象,只锁一个对象,不要锁多个对象。