多线程问题

144 阅读5分钟

多线程问题

进程和线程

1. 多线程是干什么的?

1)发挥多核cpu的作用,真正的让多段逻辑同时工作 (2)防止阻塞 (3)便于设计(建模)

2. 什么是进程和线程(进程和线程的区别)

进程就是一整个程序,例如,打开某个app,相当于开启了一个进程 线程是技术实现的,在代码实现中开启多个线程

区别: 调度:线程是进行调度和分配的基本单位,进程是拥有资源的基本单位 (内存为一个程序分配一部分空间,由真正执行程序的线程去调度) 并发性:进程和线程都可以并发执行 拥有资源:进程是拥有资源的独立单位,线程可以访问隶属于进程的资源 系统开销:在创建和撤销进程时,系统都要为进程分配和回收资源,导致系统的开销明显大于创建和撤销线程时的开销

3.创建线程的方式有哪些?

(1)继承thread类

step 1 定义一个类继承自 Thread 类,然后重写该类的 run 方法,这个方法的内容表示线程要完成的任务 step 2 创建线程对象,即创建 Thread 类子类的实例 step 3 调用步骤二中创建出来的对象的 start 方法来启动线程

/**
 * @author mghio
 * @date: 2019-12-07
 * @version: 1.0
 * @description: 通过继承 Thread 类的方式创建线程
 * @since JDK 1.8
 */
 public class CreateThreadByExtendsThread extends Thread {

  @Override
  public void run() {
    IntStream.rangeClosed(1, 10).forEach(i -> System.out.println(Thread.currentThread().getName() + " " + i));
  }

  public static void main(String[] args) {
    CreateThreadByExtendsThread threadOne = new CreateThreadByExtendsThread();
    CreateThreadByExtendsThread threadTwo = new CreateThreadByExtendsThread();
    CreateThreadByExtendsThread threadThree = new CreateThreadByExtendsThread();
    threadOne.start();
    threadTwo.start();
    threadThree.start();
  }

}

(2)实现Runnable接口

step 1 定义一个类实现 Runnable 接口,然后实现该接口的 run 方法,这个方法的内容同样也表示线程要完成的任务 step 2 创建 Runnable 接口实现类的实例,并使用该实例作为 Thraed 构造方法的参数创建 Thread 类的对象,该对象才是真正的线程对象 step 3 调用线程对象的 start 方法来启动该线程

 /**
 * @author mghio
 * @date: 2019-12-07
 * @version: 1.0
 * @description: 通过实现 Runnable 接口的方式创建线程
 * @since JDK 1.8
 */public class CreateThreadByImplementsRunnable implements Runnable {

  @Override
  public void run() {
    IntStream.rangeClosed(1, 10).forEach(i -> System.out.println(Thread.currentThread().getName() + " " + i));
  }

  public static void main(String[] args) {
    CreateThreadByImplementsRunnable target = new CreateThreadByImplementsRunnable();
    new Thread(target, "thread-one").start();
    new Thread(target, "thread-two").start();
    new Thread(target, "thread-three").start();
  }

}

(3).实现Callable接口

step 1 定义一个类实现 Callable 接口,然后实现该接口的 call 方法,这个方法的内容同样也表示线程要完成的任务,并且有返回值 step 2 创建 Callable 接口实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了 Callable 对象的 call 方法的返回值 step 3 并使用 FutureTask 对象作为 Thraed 构造方法的参数创建 Thread 对象,并调用该对象的 start 方法启动线程 step 4 调用 FutureTask 对象的 get 方法获取线程执行结束后的返回值

/**
 * @author mghio
 * @date: 2019-12-07
 * @version: 1.0
 * @description: 通过实现 Callable 接口的方式创建线程
 * @since JDK 1.8
 */public class CreateThreadByImplementsCallable implements Callable<Integer> {

  @Override
  public Integer call() {
    AtomicInteger count = new AtomicInteger();
    IntStream.rangeClosed(0, 10).forEach(i -> {
      System.out.println(Thread.currentThread().getName() + " " + i);
      count.getAndIncrement();
    });
    return count.get();
  }

  public static void main(String[] args) {
    CreateThreadByImplementsCallable target = new CreateThreadByImplementsCallable();
    FutureTask<Integer> futureTask = new FutureTask<>(target);
    IntStream.rangeClosed(0, 10).forEach(i -> {
      System.out.println(Thread.currentThread().getName() + " 的循环变量 i 的值" + i);
      if (i == 8) {
        new Thread(futureTask, "有返回值的线程").start();
      }
    });
    try {
      System.out.println("有返回值线程的返回值:" + futureTask.get());
    } catch (InterruptedException | ExecutionException e) {
      e.printStackTrace();
    }
  }

}
4、Runnable接口和Callable接口的区别

Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。

5.Thread 类中的start() 和 run() 方法有什么区别?

1.start()方法来启动线程,真正实现了多线程运行。这时无需等待run方法体代码执行完毕,可以直接继续执行下面的代码;通过调用Thread类的start()方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运行。然后通过此Thread类调用方法run()来完成其运行操作的, 这里方法run()称为线程体,它包含了要执行的这个线程的内容, Run方法运行结束, 此线程终止。然后CPU再调度其它线程。 2.run()方法当作普通方法的方式调用。程序还是要顺序执行,要等待run方法体执行完毕后,才可继续执行下面的代码;程序中只有主线程——这一个线程, 其程序执行路径还是只有一条, 这样就没有达到写线程的目的。

7.java中Lock接口比synchronized块的优势
8.java中的wait和sleep方法的不同

wait会释放锁,常被用于线程间的交互 sleep会一直持有锁,sleep通常被用于暂停执行

9.java多线程基础概念

java的多线程锁是挂在对象上的,并不是在方法上的,即每个对象都有 一个锁,当遇到synchronized的同步需要时,就会监视每个想使用本对象的线程按照一定的规则来访问,规则就是同一时间内只能有一个线程能访问此对象

java中获取锁的单位是线程,当线程a获取对象b的锁,也就是对象b的持有标记