并发和并行

  • 并发:同一时刻,多个指令同时进行
  • 并行:同一时刻,多个指令交替进行

    线程和进程

  • 进程:是操作系统上的一个程序

  • 线程:是进程的一个实体,是cup调度和分派的单位

    线程实现的三种方式

    继承Thread方法

    ```java public class ThreadDemo extends Thread{ @Override public void run() {
    1. for (int i = 0; i < 10; i++) {
    2. System.out.println("线程:"+i);
    3. }
    } }

class MyThread{ public static void main(String[] args) { ThreadDemo demo1 = new ThreadDemo(); demo1.start(); } }

  1. **为什么重写run方法?**<br />run方法是封装被线程执行的方法<br />**run()方法和start()方法的区别?**<br />run():封装线程执行的代码,直接调用,相当于普通方法的调用<br />start():启动线程;然后由JVM调用此线程的run()方法
  2. <a name="RpbNQ"></a>
  3. #### 实现Runnable接口
  4. ```java
  5. //1.实现Runnable接口
  6. public class RunnableDemo implements Runnable{
  7. //2.重写run方法
  8. @Override
  9. public void run() {
  10. for (int i = 0; i < 10; i++) {
  11. System.out.println("线程:"+i);
  12. }
  13. }
  14. }
  15. class RunnableTest{
  16. public static void main(String[] args) {
  17. //3.创建Runnable的实现类
  18. RunnableDemo runnableDemo = new RunnableDemo();
  19. //4.创建Thread对象并把runnable的实现对象传递进去
  20. //5.调用start方法
  21. new Thread(runnableDemo).start();
  22. }
  23. }


实现Callable接口

  1. //1.实现Callable接口
  2. public class CallableDemo implements Callable<String> {
  3. //2.重写call方法,并给出返回值
  4. @Override
  5. public String call() throws Exception {
  6. for (int i = 0; i < 10; i++) {
  7. System.out.println("线程执行:"+i);
  8. }
  9. return "结束";
  10. }
  11. }
  12. class CallableTest{
  13. public static void main(String[] args) throws ExecutionException, InterruptedException {
  14. //3.创建Callable实现类对象
  15. CallableDemo demo = new CallableDemo();
  16. //4.创建FutureTask对象并将Callable实现类对象传递进去
  17. FutureTask<String> task = new FutureTask<>(demo);
  18. //5.创建Thread线程对象,把FutureTask对象作为构造方法的参数
  19. Thread thread = new Thread(task);
  20. //6.开始线程
  21. thread.start();
  22. //可以获取到线程执行后的返回结果
  23. String result = task.get();
  24. System.out.println(result);
  25. }
  26. }

三种实现方式的对比

  • 实现Runnable、Callable接口
    • 好处: 扩展性强,实现该接口的同时还可以继承其他的类
    • 缺点: 编程相对复杂,不能直接使用Thread类中的方法
  • 继承Thread类
    • 好处: 编程比较简单,可以直接使用Thread类中的方法
    • 缺点: 可以扩展性较差,不能再继承其他的类
  • Runnable 接口 run 方法无返回值;Callable 接口 call 方法有返回值,支持泛型
  • Runnable 接口 run 方法只能抛出运行时异常,且无法捕获处理;Callable 接口 call 方 法允许抛出异常,可以获取异常信息

    同步方法和静态同步方法

    同步方法锁对象是this
    静态同步方法锁对象是类名.class

    Lock锁

    JDK1.5之后推出了Lock锁,可以人为的去加锁及解锁
    使用:创建RenntrantLock对象,在要加锁的代码前面使用lock方法加锁,在解锁地方使用unlock方法解锁

    生产者消费者

    | 方法名 | 说明 | | —- | —- | | void wait() | 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法 | | void notify() | 唤醒正在等待对象监视器的单个线程 | | void notifyAll() | 唤醒正在等待对象监视器的所有线程 |

线程的生命周期

  1. 新建状态,生成线程对象,此时并没有调用对象的start方法,线程处于创建状态
  2. 就绪状态,当线程调用了start方法后,线程进入了就绪状态,此时有执行资格没有执行权,需要抢到cpu资源才能到达运行状态
  3. 运行状态,此时抢到cpu资源,开始运行run方法中的代码
  4. 阻塞状态,阻塞状态是因为某些原因,放弃cpu使用权,暂时停止运行直到线程进入就绪状态,才会转到运行状态,阻塞分为三种
    1. 阻塞:无法获得锁,获得锁有阻塞结束进入就绪状态再次抢夺cpu使用
    2. 等待:wait(),让线程等待某项完成,使用notify()或notifyAll()再次唤醒线程结束阻塞状态
    3. 计时等待:sleep(),当sleep完成后,线程结束阻塞状态
  5. 死亡:线程结束run方法执行,或者某些异常原因退出run方法,线程结束

image.png

线程池

手动创建线程池

  1. public static void main(String[] args) {
  2. ThreadPoolExecutor executor = new ThreadPoolExecutor(
  3. //核心线程数量
  4. 2,
  5. //最大线程数
  6. 5,
  7. //空闲存活时间
  8. 3,
  9. //时间单位
  10. TimeUnit.SECONDS,
  11. //任务队列
  12. new ArrayBlockingQueue<>(10),
  13. //创建线程工厂
  14. Executors.defaultThreadFactory(),
  15. //任务拒绝策略
  16. // AbortPolicy 丢弃任务并抛出 DiscardPolicy 丢弃任务,但是不抛出异常 不推荐
  17. // DiscardOldestPolicy 抛弃队列中等待最久的任务 然后把当前任务加入队列中 CallerRunsPolicy 调用任务的run()方法绕过线程池直接执行
  18. new ThreadPoolExecutor.AbortPolicy()
  19. );
  20. executor.submit(() -> {
  21. System.out.println(Thread.currentThread().getName() + "线程执行");
  22. });
  23. }

线程池原理

image.png

线程池关闭

关闭线程池,可以通过 shutdown 和 shutdownNow 两个方法
原理:遍历线程池中的所有线程,然后依次中断

  1. shutdownNow 首先将线程池的状态设置为 STOP,然后尝试停止所有的正在执行和未执 行任务的线程,并返回等待执行任务的列表;
  2. shutdown 只是将线程池的状态设置为 SHUTDOWN 状态,然后中断所有没有正在执 行任务的线程;

    原子性

    定义: 即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
    在多线程中
    1. 堆内存是唯一的,每一个线程都有自己的线程栈。
    2. 每一个线程在使用堆里面变量的时候,都会先拷贝一份到变量的副本中。
    3. 在线程中,每一次使用是从变量的副本中获取的。
    Volatile关键字 : 强制线程每次在使用的时候,都会看一下共享区域最新的值

    多线程面试

    wait()和 sleep()的区别?(必会)

  3. 来自不同类

    1. wait来自Object类
    2. sleep来自Thread类
  4. 关于锁的释放
    1. wait在等待过程中会释放锁
    2. sleep在等待过程中不会释放
  5. 适用范围
    1. wait必须在同步代码块中用
    2. sleep可以在任何地方
  6. 是否需要捕获异常
    1. wait不用
    2. sleep用

      线程基本方法

      线程相关的基本方法有 wait,notify,notifyAll,sleep,join,yield 等