Java多线程相关基础知识

Q:进程和线程的区别?

进程是一个在内存中运行的应用程序。

线程是进程中的一个执行任务(控制单元),负责当前进程中程序的执行。

进程与线程的区别总结

根本区别:进程是操作系统资源分配的基本单位,而线程是CPU任务调度和执行的基本单位。

资源开销:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。

包含关系:一个进程可以有多个线程,线程是进程的一部分。

影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。

执行过程:每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行。

从 JVM 角度说进程和线程之间的关系

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 有什么区别?

  1. Runnable 接口run方法没有返回值Callable接口call方法有返回值,是个泛型。和Future、FutureTask配合可以用来获取异步执行的结果。(Callable接口的执行结果需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。 )
  2. 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方法中断线程。
  • 28
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值