Java知识点整理-并发

178 阅读7分钟

线程

www.cnblogs.com/NullPointEx…

Thread状态

一般来说,线程包括以下这几个状态:创建(new)、就绪(runnable)、运行(running)、阻塞(blocked)、time waiting、waiting、消亡(dead) image.png

Thread常用方法

  1. start方法:start()用来启动一个线程,当调用start方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,会为相应的线程分配需要的资源。
  2. run方法:run()方法是不需要用户来调用的,当通过start方法启动一个线程之后,当线程获得了CPU执行时间,便进入run方法体去执行具体的任务。注意,继承Thread类必须重写run方法,在run方法中定义具体要执行的任务。
  3. sleep方法:sleep相当于让线程睡眠,交出CPU,让CPU去执行其他的任务。但是有一点要非常注意,sleep方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象。
  4. yield方法:调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。但是yield不能控制具体的交出CPU的时间,另外,yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会。
  5. join方法:假如在main线程中,调用thread.join方法,则main方法会等待thread线程执行完毕或者等待一定的时间。如果调用的是无参join方法,则等待thread执行完毕,如果调用的是指定了时间参数的join方法,则等待一定的时间。join内部调用wait方法,会让线程进入阻塞状态,并且会释放线程占有的锁,并交出CPU执行权限。
  6. interrupt方法:interrupt,顾名思义,即中断的意思。单独调用interrupt方法可以使得处于阻塞状态的线程抛出一个异常,也就说,它可以用来中断一个正处于阻塞状态的线程。中断运行状态的线程通常通过标志位来实现

线程属性的几个方法

  1. getId:用来得到线程ID
  2. getName和setName:用来得到或者设置线程名称。
  3. getPriority和setPriority:用来获取和设置线程优先级。
  4. setDaemon和isDaemon:用来设置线程是否成为守护线程和判断线程是否是守护线程。

什么是守护线程

守护线程和用户线程的区别在于:守护线程依赖于创建它的线程,而用户线程则不依赖。举个简单的例子:如果在main线程中创建了一个守护线程,当main方法运行完毕之后,守护线程也会随着消亡。而用户线程则不会,用户线程会一直运行直到其运行完毕。在JVM中,像垃圾收集器线程就是守护线程。

wait()和sleep()的区别

zhuanlan.zhihu.com/p/45666264

三个线程,如何顺序打印ABC

volatile

volatile关键字干了什么?(什么叫指令重排)

保证可见性和禁止指令重排,不保证原子性 www.zhihu.com/question/32…

volatile的使用场景

blog.csdn.net/hzau_itdog/…

volatile在DCL上的作用是什么?

instance=new Singleton();创建了一个对象。这一行代码可以分解为如下的3行伪代码。

memory = allocate();  // 1:分配对象的内存空间
ctorInstance(memory); // 2:初始化对象
instance = memory;  // 3:设置instance指向刚分配的内存地址

上面3行伪代码中的2和3之间,可能会被重排序。 所以当instance被定义为 private volatile static Instance instance的话就会保证在创建对象的时候的执行顺序一定是1-2-3的步骤,从而在线程B访问第2处代码时候,instance要么为null 要么是已经完全初始化好的对象。

synchronized

sychronized修饰在不同的地方有什么区别?

  1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
  2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
  3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
  4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

synchronized的工作原理

synchronized 在 JVM 的实现原理是基于进入和退出管程(Monitor)对象来实现同步。但 synchronized 关键字实现同步代码块和同步方法的细节不一样,代码块同步是使用 monitorenter 和 monitorexit 指令实现的,方法同步通过调用指令读取运行时常量池中方法的 ACC_SYNCHRONIZED 标志来隐式实现的。

synchronized的锁实际是保存在哪里

对象头

synchronized在JDK1.6之后做了哪些优化

synchronized 锁的优化:锁的四种状态-无锁,偏向锁、轻量级锁,重量级锁

ReentrantLock

可重入锁,加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。还可以实现公平锁机制

AtomicInteger

AtomicInteger的出现只是为了解决integer在多线程下线程安全的问题,方法都是原子操作。 AtomicInteger是块地址,可变的,拿来改的。

重点:www.cnblogs.com/jyroy/p/113…

对象锁和类锁是否会互相影响

不会

同一个锁为什么效率有差别?

阻塞或唤醒一个Java线程需要操作系统切换CPU状态来完成,这种状态转换需要耗费处理器时间。如果同步代码块中的内容过于简单,状态转换消耗的时间有可能比用户代码执行的时间还要长。

CAS无锁编程的原理

CAS算法涉及到三个操作数:

  • 需要读写的内存值 V。
  • 进行比较的值 A。
  • 要写入的新值 B。 当且仅当 V 的值等于 A 时,CAS通过原子方式用新值B来更新V的值(“比较+更新”整体是一个原子操作),否则不会执行任何操作。一般情况下,“更新”是一个不断重试的操作。

ReentrantLock的实现原理

AQS

AQS原理

www.cnblogs.com/zofun/p/122… AQS内部实现了两个队列,一个同步队列,一个条件队列。 同步队列:当线程获取资源失败之后,就进入同步队列的尾部保持自旋等待,不断判断自己是否是链表的头节点,如果是头节点,就不断参试获取资源,获取成功后则退出同步队列。
条件队列:为Lock实现的一个基础同步器,并且一个线程可能会有多个条件队列,只有在使用了Condition才会存在条件队列。
有点难说不清,以后填坑

线程池

设计一个线程池,怎么设计线程池。(线程池参数)

  1. 线程池最大大小:IO密集型为2N,计算密集型为N+1
  2. 线程池维护最小线程数量:5
  3. 线程允许空闲时:2s
  4. 缓冲队列:大小
  5. 拒绝策略:缓冲队列满了怎么办

使用过哪些线程池

blankj的线程工具

getFixedPool            : 获取固定线程池
getSinglePool           : 获取单线程池
getCachedPool           : 获取缓冲线程池
getIoPool               : 获取 IO 线程池
getCpuPool              : 获取 CPU 线程池

还有定时和周期性线程池

线程池的原理,有哪些拒绝策略

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

任务有优先级怎么办,知道优先级反转吗?

  • 设置多个任务队列,按照任务优先级来处理。
  • 优先级继承和优先级极限,不想多讲,老八股文了