多线程基础

本文详细解析了线程和进程的区别,包括它们的生命周期、并发和并行概念,展示了创建线程的不同方式,如继承Thread类、实现Runnable接口和Callable接口,以及线程池的使用。文章还对比了Runnable和Callable接口、run()方法与start()方法的区别,介绍了线程状态及其控制手段,如join、notify、notifyAll、wait和sleep。最后讨论了如何打断正在执行的线程。
摘要由CSDN通过智能技术生成

1.线程和进程的区别

当一个程序被运行,从磁盘加载这个程序到内存的过程,叫做开启了一个进程

一个进程可以包含一到多个线程

  • 进程是正在运行程序的实例,进程中包含了线程,每个线程执行不同的任务

  • 不同的进程使用不同的内存空间,在当前进程下的所有线程可以共享内存空间

  • 线程更轻量,线程上下文切换成本一般要比进程上下文切换低

2. 并行和并发的区别

并发:同一时间应对多件事情的能力

并行:同一时间动手多件事情的能力

  • 在多核CPU下,并发是同一时间多个线程轮流使用一个或多个CPU

  • 并行是4核CPU同时执行4个线程

3. 创建线程的方式有那些

  • 继承Thread类实现

    • public class MyThread extends Thread{
      ​
          @Override
          public void run() {
              System.out.println("继承Thread类");
          }
      ​
          public static void main(String[] args) {
              //创建线程
              MyThread t1 = new MyThread();
              MyThread t2 = new MyThread();
              //启动线程
              t1.start();
              t2.start();
          }
      ​
      ​
      }
  • 实现Runnable接口

    • public class MyRunnable implements Runnable{
          @Override
          public void run() {
              System.out.println("实现Runnable接口");
          }
      ​
          public static void main(String[] args) {
              MyRunnable myRunnable = new MyRunnable();
              Thread t1 = new Thread(myRunnable);
              Thread t2 = new Thread(myRunnable);
              
              t1.start();
              t2.start();
              
          }
          
      }
  • 实现Callable接口,这种方法创建线程有返回值

    • public class MyCallable implements Callable<String> {
          @Override
          public String call() throws Exception {
              System.out.println("实现Callable接口");
              return "Callable";
          }
      ​
          public static void main(String[] args) throws ExecutionException, InterruptedException {
              MyCallable mc = new MyCallable();
              FutureTask<String> futureTask = new FutureTask<String>(mc);
              Thread t1 = new Thread(futureTask);
              t1.start();
              String result = futureTask.get();
              System.out.println(result);
              
          }
      ​
      ​
      }
      ​
  • 线程池创建

    • 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();
          }
      }
3.1 Runnable 和 Callable接口的区别
  1. Runnable接口的run方法没有返回值

  2. Callable接口的call方法有返回值,是个泛型,和Future、FutureTask配合使用

  3. Callable接口的call方法允许抛出异常,而Runnable的run方法不能抛出异常,只能使用try...catch,不能向上抛出

3.2 run() 方法和 start() 方法有什么区别
  1. run() 方法是一个普通的方法,没有开启一个线程,是串行的,可以执行多次

  2. start() 方法是开启一个线程,不能开启多次

4. 线程中有那些状态

  1. 尚未启动状态(NEW)

  2. 有执行资格没有执行权(就绪状态)

  3. 有执行资格有执行权(运行)

  4. 线程死亡,变成垃圾(死亡状态)

  5. 阻塞状态

  6. 等待状态(wait)

  7. 计时等待(sleep)

  • 创建线程对象是新建状态

  • 调用了start() 方法转变为可执行状态

  • 线程获取到了CPU的执行权,执行结束是终止状态

  • 在可执行状态的过程中,如果没有获取CPU的执行权,可能会切换为其他状态

    • 如果没有获取到锁,进入阻塞状态,获取到锁之后切换为可执行状态

    • 线程调用wait()方法进入等待状态,其他线程调用notify()方法唤醒后切换为可执行状态

    • 线程调用sleep()方法进入计时等待状态,到时间后切换为可执行状态

5. 新建T1、T2、T3三个线程,怎样保证线程按顺序执行

使用线程中的join方法

只要调用join方法的线程执行完毕后才可往下执行

public static void main(String[] args) {
​
        Thread t1 = new Thread(()->{
            System.out.println("t1");
        });
​
        Thread t2 = new Thread(()->{
            try {
                t1.join(); //只有t1线程执行完毕才可往下执行
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t2");
        });
​
        Thread t3 = new Thread(()->{
            try {
                t2.join();//只要t2线程执行完毕才可往下执行
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t3");
        });
        t1.start();
        t2.start();
        t3.start();
        
    }

6. notify() 和 notifyAll() 的区别

  • notify:随机唤醒一个线程

  • notifyAll:唤醒所有线程

7. java中的wait方法和sleep方法有什么不同

共同点:

wait() wait(long) sleep(long) 的效果都是让当前线程暂时放弃CPU的使用权,进入阻塞状态

不同点:

  1. 方法归属不同

    1. sleep是Tread的静态方法

    2. wait是Object的成员方法,每个对象都有

  2. 醒来时机不同

    1. 执行sleep和wait的线程都会在等待相应毫秒后醒来

    2. wait(long) wait()可以被notify唤醒,wait() 如果不唤醒就会一直等待下去

    3. 都可以被打断唤醒

  3. 锁特性不同

    1. wait方法的调用必须先获取wait对象的锁,而sleep则无此限制(wait方法必须放到synchronized代码块中)

    2. wait方法执行后会释放对象锁,允许其他线程获得该对象锁(我放弃CPU,但你们还能用)

    3. sleep如果在synchronized代码块中执行,并不会释放对象锁(我放弃CPU,但你们也用不了)

8. 如何打断正在执行的线程

  1. 使用标志位打断,在主线程中的更改子线程执行的条件标志,依次来使子线程结束

  2. 使用stop方法强行终止,此方法现在已经作废,不推荐使用

  3. 使用Interrupt方法打断线程,本质上也是使用标志位打断,不过这个标志位使用的是current.isInterrupted()方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值