java并发编程

0. 思维导图

java并发编程_第1张图片
并行:单位时间多个处理器同时处理多个任务
并发:一个处理器处理多个任务,按时间片轮流处理

1. java实现多线程有几种方式♥♥♥

实现接口会更好一些,因为java不支持多重继承,因此继承了Thread类就无法继承其他类,但是可以实现多个接口

  1. 继承Thread类,只需要创建一个类继承Thread类然后重写run方法,在main方法中调用该类实例对象的start方法。
  2. 实现Runnable接口,只需要创建一个类实现Runnable接口然后重写run方法,在main方法中将该类的实例对象传给Thread类的构造方法,然后调用start方法。
  3. 实现Callable接口,只需要创建一个类实现Callable接口然后重写call方法(有返回值),在main方法中将该类的实例对象传给Future接口的实现类FutureTask的构造方法,然后再将返回的对象传给Thread类的构造方法,最后调用start方法。
  4. 线程池,首先介绍它的好处,然后再说它可以通过ThreadPoolExecutor类的构造方法来进行创建。

补充:为什么不能直接调用run方法

  • 调用start方法方可启动线程并使线程进入就绪状态,直接执行run方法的话不会以多线程的方式执行。

补充:多线程的优缺点?

  • 优点:当一个线程进入阻塞或等待状态,cpu可以先去执行其他线程,提高cpu利用率
  • 缺点:
    1. 频繁的上下文切换会影响多线程的执行速度
    2. 容易死锁

2. 线程池相关内容♥♥♥

  • 好处:

    • 降低资源消耗,通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
    • 提高响应速度,当任务到达时,任务可以不需要等待线程就能立即执行
    • 提高线程的可管理性,线程是稀缺资源,如果无限制的创建,不仅消耗资源而且降低系统的稳定性,使用线程池可以进行线程的统一分配,调优和监控。
  • Executor框架的主要成员:ThreadPoolExecutor、ScheduledThreadPoolExecutor、future接口、Runnable接口、Callable接口和Executors

  • 创建线程池的两种方式:

    • 通过ThreadPoolExecutor构造函数(推荐)
    • 通过Executor框架的工具类Executors来实现。我们现在创建三种类型的ThreadPoolExecutor:(不推荐,容量为Integer.MAX_VALUE,所以容易OOM)
      • CachedThreadPool:返回一个可根据实际情况调整线程数量的线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
      • FixedThreadPool:返回一个固定线程数量的线程池,可控制线程最大并发数,超过的线程会在队列中等待。
      • SingleThreadExecutor:返回一个只有一个线程的线程池,它只会用唯一的线程来执行任务,保证所有任务按照执行顺序执行。
        java并发编程_第2张图片
  • ThreadPoolExecutor构造方法的7个参数:

    • corePoolsize:线程池的核心线程数(最小可以同时运行的线程数)

    • maximumPoolSize:线程池的最大线程数(当任务队列存放的任务达到队列容量的时候,当前可以同时运行的线程数量变为最大线程数)

    • keepAliveTime:当线程数大于核心线程数时,多余的空闲线程存活的最长时间(如果此时没有新的任务提交,核心线程外的线程不会立即销毁,而是等到这么长时间,会被回收销毁)

    • unit:时间单位

    • workQueue:任务队列,用来存储等待执行任务的队列(当新的任务来的时候,会先判断当前运行的线程数量是否达到核心线程数,如果达到的话,就会被放入队列中)

    • ThreadFactory:线程工厂,用来创建线程

    • RejectedExecutionHandler:拒绝策略,当提交的任务过多而不能及时处理时,我们可以定制策略来处理任务

      • abortPolicy:抛出RejectedExecutionException来拒绝新任务的处理(默认)
      • CallerRunsPolicy:用调用者的线程运行任务
      • DiscardPolicy:不处理新任务,直接丢弃掉
      • DiscardOldestPolicy:丢弃最早的未处理的任务请求
    • ScheduledThreadPoolExecutor:用来在给定的延迟后运行任务,或者定期执行任务(一般不会使用)

3. 线程有哪几种状态♥♥

  • 线程一共有6种状态,分别是NEW新建、Runnable运行、Blocked阻塞、限期等待、无期限等待、Terminated终止

    • 新建状态表示 线程被创建但是还没有启动
    • 运行状态表示 线程有可能正在执行,也有可能在等待CPU分配资源
    • 阻塞状态表示线程没有获取到monitor锁
    • 限期等待状态表示在一定时间后会有系统自动唤醒【线程在run方法的内部调用了wait()、join()或者sleep(),并且传入了等待时间参数】
    • 无限期等待状态表示 不会分配CPU资源,需要显示唤醒【线程在run方法的内部调用了wait()或者join()】
    • 终止状态 表示 线程执行结束
  • 线程中阻塞和等待的区别

    • 阻塞状态指线程在等待其他线程释放monitor锁,等待状态指线程在等待其他线程执行某些操作,比如wait方法执行完毕或者等待调用notify方法唤醒线程。

4. sleep、wait、notify、yield和join方法的区别♥

多线程下,需要调用这个对象的synchronized方法或synchronized块必须获得对象锁,此时没有获取到线程会进入锁池;而获取到锁的线程如果调用了wait方法,线程就会进入等待池,进入等待池的线程不会竞争该对象的锁。

  • sleep()方法:让当前正在执行的线程在指定的时间内暂停执行

    • 和wait()方法最大的区别:sleep没有释放锁,而wait释放了锁;调用sleep后自动唤醒,而wait不会自动唤醒,需要其他线程调用notify方法
  • wait()方法:释放对象锁,进入等待池(一定是写在synchronized代码块中

  • notify()方法:从等待池中移出任意一个线程放入锁池中(一定是写在synchronized代码块中

  • notifyAll()方法:会将等待池中的所有线程都移动到锁池中(一定是写在synchronized代码块中

  • yield()方法:使当前线程重新回到可执行状态(CPU时间片用完了),所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。

  • join()方法:会使当前线程等待调用join()方法的线程结束后才继续执行。

5. 什么是上下文切换♥

  • 单核处理器也支持多线程执行代码,CPU通过给每个线程分配CPU时间片来实现这个机制,因为时间片非常短(几十毫秒),所以CPU通过不停的切换线程执行让我们感觉多个线程是同时执行的
  • CPU通过时间分片分配算法来循环执行任务,当前任务执行完一个时间片后会切换到下一个任务,但是在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再加载这个任务的状态,所以,任务从保存到再加载的过程就是一次上下文切换

6. 设计一个简单的死锁程序♥♥

  • 解释:两个线程同时开启,线程1先获得obj1对象的锁,然后想要获得对象obj2的锁,这个时候刚好线程2获得了对象obj2的锁,因此线程1会被阻塞,那么线程2也会想要获取对象obj1的锁,显然是无法得到的,也会进入阻塞状态,这就导致两个线程都无法释放自己的锁而结束。

7. ThreadLocal相关内容♥♥

  • 定义:ThreadLocal叫做线程变量,也就是说该变量是当前线程独有的变量。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量,因此就不会有多线程安全问题。
  • 方法:他们可以使用get和set方法来获取默认值或将其值更改为当前线程所存的副本的值。
  • 原理:我们从Thead类源码中可以看到有一个threadLocals变量,它是ThreadLocalMap类型的变量,而ThreadLocalMap是ThreadLocal的静态内部类,当我们调用set或者get方法的时候,实际上是将变量存放到了当前线程的ThreadLocalMap
  • 问题:内存泄漏ThreadLocalMap中使用的key为ThreadLocal的弱引用,而value是强引用,所以在垃圾回收的时候,key会被清理掉,而value不会被清理掉。这样一来,ThreadLocalMap中就会出现key为null的Entry。假如我们不做任何措施的话,value永远无法被GC回收,这个时候就可能会产生内存泄漏。解决手段:手动调用remove()方法

8. 并发编程的三个问题♥♥

  • 可见性:是指一个线程对共享变量进行修改,另一个线程要立即得到修改后的最新值。
  • 原子性:是指在一次或多次操作中,要么所有的操作都执行并且不会受其他因素干扰而中断,要么所有的操作都不执行
  • 有序性:是指程序中代码的执行顺序,java在编译和运行时会对代码进行优化,会导致程序最终的执行顺序不一定就是编写代码时的顺序(指令重排在单线程下可以提高性能,但在多线程下会出现问题)

9. 介绍一下java内存模型♥♥♥

  • java内存模型是一套规范,描述了java程序中各种变量(线程共享变量)的访问规则,其规则所有的共享变量都存储在主内存中,每一个线程都有自己的工作内存,工作内存中只存储该线程对共享变量的副本,线程对变量的所有操作都必须在工作内存中完成,而不能直接读写主内存的变量。
  • 作用:在多线程读写共享数据时,对共享数据的可见性、有序性、和原子性的保证。
  • 主内存和工作内存之间的数据交互过程:lock->read->load->use->assign->store->write->unlock
  • 注意:
    • 如果对一个变量执行lock操作,将会清除工作内存中此变量的值。
    • 对一个变量执行unlock操作之前,必须先把此变量同步到主内存中

10. synchronized是如何保证三大特性♥♥

java并发编程_第3张图片
java并发编程_第4张图片
java并发编程_第5张图片

11. synchronized的特性♥♥

java并发编程_第6张图片

12. synchronized 的原理♥♥♥

java并发编程_第7张图片
java并发编程_第8张图片

13. ReentrantLock底层原理♥♥♥

java并发编程_第9张图片
java并发编程_第10张图片

14. synchronized与lock的区别♥♥♥

java并发编程_第11张图片

15. volatile的作用♥♥

java并发编程_第12张图片
java并发编程_第13张图片

16. volatile和synchronized的区别♥♥♥

java并发编程_第14张图片

17. CAS介绍一下♥♥

java并发编程_第15张图片

18. 乐观锁和悲观锁的区别♥♥

java并发编程_第16张图片
java并发编程_第17张图片

19. 锁升级的过程♥

java并发编程_第18张图片
java并发编程_第19张图片

20. synchronized优化♥

java并发编程_第20张图片

你可能感兴趣的:(#,java基础,java,数据库,开发语言)