Q:进程和线程的区别?
进程是一个在内存中运行的应用程序。
线程是进程中的一个执行任务(控制单元),负责当前进程中程序的执行。
进程与线程的区别总结
根本区别:进程是操作系统资源分配的基本单位,而线程是CPU任务调度和执行的基本单位。
资源开销:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。
包含关系:一个进程可以有多个线程,线程是进程的一部分。
影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
执行过程:每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行。
从 JVM 角度说进程和线程之间的关系
![](https://i-blog.csdnimg.cn/direct/bbde6a932c564d368b539f59b58b32d6.png)
Q:创建线程的四种方式?
Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。Java可以用四种方式来创建线程,如下所示:
1)继承Thread类创建线程
public class MyThread extends Thread{//继承Thread类
public void run(){
//重写run方法
}
}
public class Main {
public static void main(String[] args){
new MyThread().start();//创建并启动线程
}
}
2)实现Runnable接口创建线程
public class MyThread2 implements Runnable {//实现Runnable接口
public void run(){
//重写run方法
}
}
public class Main {
public static void main(String[] args){
//创建并启动线程
MyThread2 myThread=new MyThread2();
Thread thread=new Thread(myThread);
thread().start();
}
}
3)使用Callable创建线程
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("MyCallable...call...");
return "OK";
}
public static void main(String[] args) throws
ExecutionException, InterruptedException {
// 创建MyCallable对象
MyCallable mc = new MyCallable() ;
// 创建F
FutureTask<String> ft = new FutureTask<String>(mc) ;
// 创建Thread对象
Thread t1 = new Thread(ft) ;
Thread t2 = new Thread(ft) ;
// 调用start方法启动线程
t1.start();
// 调用ft的get方法获取执行结果
String result = ft.get();
// 输出
System.out.println(result);
}
}
4)使用线程池例如用Executor框架
优点:把任务的提交和执行解耦。
public class MyExecutors implements Runnable{
@Override
public void run() {
System.out.println("MyRunnable...run...");
}
public static void main(String[] args) {
// 创建线程池对象
ExecutorService threadPool = Executors.newFixedThreadPool(3);
threadPool.submit(new MyExecutors()) ;
// 关闭线程池
threadPool.shutdown();
}
Q:runnable 和 callable 有什么区别?
- Runnable 接口run方法没有返回值;Callable接口call方法有返回值,是个泛型。和Future、FutureTask配合可以用来获取异步执行的结果。(Callable接口的执行结果需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。 )
- Callable接口的call()方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛。
Q:线程的 run()和 start()有什么区别?
start(): 用来启动线程,通过该线程调用run方法执行run方法中所定义的逻辑代码。start方法只能被调用一次。
run(): 封装了要被线程执行的代码,可以被调用多次。
Q:线程包括哪些状态,状态之间是如何变化的?
NEW:当一个线程对象被创建,但还未调用 start 方法时处于新建状态。
RUNNABLE:调用了 start 方法,就会由新建进入可运行。
BLOCKED:当获取锁失败后,由可运行进入 Monitor 的阻塞队列阻塞,此时不占用 cpu 时间。
WAITING:当获取锁成功后,但由于条件不满足,调用了 wait() 方法,此时从可运行状态释放锁进入 Monitor 等待集合等待,同样不占用 cpu 时间。
TIMED_WAITING:当获取锁成功后,但由于条件不满足,调用了 wait(long) 方法,此时从可运行状态释放锁进入 Monitor 等待集合进行有时限等待,同样不占用 cpu 时间;还有一种情况是调用 sleep(long) 方法也会从可运行状态进入有时限等待状态,但与 Monitor 无关,不需要主动唤醒,超时可自然恢复为可运行状态。
Q:新建 T1、T2、T3 三个线程,如何保证它们按顺序执行?
可以用线程类的join()方法。
public class JoinTest {
public static void main(String[] args) {
// 创建线程对象
Thread t1 = new Thread(() -> {
System.out.println("t1");
}) ;
Thread t2 = new Thread(() -> {
try {
t1.join(); // 加入线程t1,只有t1线程执行完毕以后,再次执行该线程
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t2");
}) ;
// 启动线程
t1.start();
t2.start();
}
}
Q:notify()和 notifyAll()有什么区别?
notifyAll:唤醒所有wait的线程。
notify:只随机唤醒一个 wait 线程。
Q:在 java 中 wait 和 sleep 方法的不同?
方法归属不同:
- sleep(long) 是 Thread 的静态方法。
- wait(),wait(long) 都是 Object 的成员方法,每个对象都有。
醒来时机不同:
- 执行 sleep(long) 和 wait(long) 的线程都会在等待相应毫秒后醒来
- wait(long) 和 wait() 还可以被 notify 唤醒,wait() 如果不唤醒就一直等下去。
锁特性不同(重点):
- wait 方法的调用必须先获取 wait 对象的锁,而 sleep 则无此限制。
- wait 方法执行后会释放对象锁,允许其它线程获得该对象锁。
- 而 sleep 如果在 synchronized 代码块中执行,并不会释放对象锁。
Q:如何停止一个正在运行的线程?
- 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
- 使用stop方法强行终止(不推荐,方法已作废)。
- 使用interrupt方法中断线程。