JUC概述

JUC

进程和线程的概念

进程:资源分配的最小单位。
线程-进程之内的独立执行的一个单元执行流

线程的状态

Thread.State

Wait和sleep

并发和并行

管程

image.png

用户线程和守护线程

image.png

lOCK接口


Synchronized/多线程编程步骤

image.png

image.png

  • 线程间通信

image.png

什么是Lock接口

image.png

可重入锁(见后)

可重入就是说某个线程已经获得某个锁,可以再次获取锁而不会出现死锁。
Synchronized 和 ReenTrantLock都是可重入锁。

ReenTrantLock()

image.png

创建线程的多种方式

image.png

虚假唤醒问题

wait()方法是在哪里睡,就在哪里醒。如下,资源类操作方法中, if语句判断,假如第一判断时,因为不满足条件所以进入wait(),但是下一次被唤醒后,不会再判断条件。造成了虚假唤醒,应该使用while语句判断。

image.png

线程间定制通信

线程执行顺序。 在多线程编程中的condition条件中,增加一个变量来控制线程执行顺序。
image.png
设计线程的执行条件,就可以实现线程的执行顺序等等操作。

ArrayList集合线程不安全

ArrayList,内部没有使用Synchronized的同步方法,它是线程不安全的。

  • 并发修改异常的案例。

image.png

3种解决
image.png
第一种,不常用
第二种,不常用
image.png
第三种,常用
image.png

CopyOnWriteArrayList 写时复制技术

并发读、独立写(写时复制一份新版本,写完后合并新旧版本)。
image.png
源码
image.png

Hashset线程不安全

  • 并发修改问题

image.png

CopyOnWriteArraySet 写时复制技术

image.png

补充:hastset底层就是hashmap
image.png

HashMap线程不安全

CocurrentHashMap — 同步方法

image.png

  1. ....
  2. V oldVal = null;
  3. synchronized (f) {
  4. // 添加<key,value>
  5. ... ;
  6. if (oldVal != null)
  7. return oldVal;
  8. ... } // 1、 同步方法
  9. ...
  10. return null; // 2、 返回null 或者 oldVal(如果存在的话)
  1. final V putVal(K key, V value, boolean onlyIfAbsent) {
  2. if (key == null || value == null) throw new NullPointerException();
  3. int hash = spread(key.hashCode());
  4. int binCount = 0;
  5. for (Node<K,V>[] tab = table;;) {
  6. Node<K,V> f; int n, i, fh; K fk; V fv;
  7. if (tab == null || (n = tab.length) == 0)
  8. tab = initTable();
  9. else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
  10. if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value)))
  11. break; // no lock when adding to empty bin
  12. }
  13. else if ((fh = f.hash) == MOVED)
  14. tab = helpTransfer(tab, f);
  15. else if (onlyIfAbsent // check first node without acquiring lock
  16. && fh == hash
  17. && ((fk = f.key) == key || (fk != null && key.equals(fk)))
  18. && (fv = f.val) != null)
  19. return fv;
  20. else {
  21. V oldVal = null;
  22. synchronized (f) {
  23. if (tabAt(tab, i) == f) {
  24. if (fh >= 0) {
  25. binCount = 1;
  26. for (Node<K,V> e = f;; ++binCount) {
  27. K ek;
  28. if (e.hash == hash &&
  29. ((ek = e.key) == key ||
  30. (ek != null && key.equals(ek)))) {
  31. oldVal = e.val;
  32. if (!onlyIfAbsent)
  33. e.val = value;
  34. break;
  35. }
  36. Node<K,V> pred = e;
  37. if ((e = e.next) == null) {
  38. pred.next = new Node<K,V>(hash, key, value);
  39. break;
  40. }
  41. }
  42. }
  43. else if (f instanceof TreeBin) {
  44. Node<K,V> p;
  45. binCount = 2;
  46. if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
  47. value)) != null) {
  48. oldVal = p.val;
  49. if (!onlyIfAbsent)
  50. p.val = value;
  51. }
  52. }
  53. else if (f instanceof ReservationNode)
  54. throw new IllegalStateException("Recursive update");
  55. }
  56. }
  57. if (binCount != 0) {
  58. if (binCount >= TREEIFY_THRESHOLD)
  59. treeifyBin(tab, i);
  60. if (oldVal != null)
  61. return oldVal;
  62. break;
  63. }
  64. }
  65. }
  66. addCount(1L, binCount);
  67. return null;
  68. }

多线程锁

Synchronized - 锁的范围(谁作为锁)

  • 是否用的同一把锁 ?
  • 锁的范围 ?
  • static Synchronized ?

public Synchronized void sendSMS(){
—> 锁当前对象,即this是争夺的资源
}

public static Synchronized void sendSMS(){
—> 锁当前类,即当前字节码对象this.getClass()是争夺资源
}

public void sendSMS(){
Object t = new Object();
Sysnchronized (t){
—-> 同步代码块,传入的对象t是争夺的资源。
}
}
image.png

ReentrantLock - 公平锁、非公平锁

默认: 非公平锁。
image.png
公平锁:检查前面链表节点中是否有人持有锁。
非公平锁:直接抢占。

ReentrantLock 构造器

  1. public ReentrantLock() {
  2. sync = new NonfairSync();
  3. }
  4. public ReentrantLock(boolean fair) {
  5. sync = fair ? new FairSync() : new NonfairSync();
  6. }

NonfairSync()

initialTryLock() 尝试持有锁;

  1. # state是锁的计数。
  2. if (compareAndSetState(0, 1)): # 原子操作, 意思是 if (state==0 ? ++state==1, false)
  3. 独占线程=当前线程
  4. return true;
  5. else if (独占线程 == 当前线程):
  6. state++;
  7. return true
  8. else
  9. return false;
  1. static final class NonfairSync extends Sync {
  2. private static final long serialVersionUID = 7316153563782823691L;
  3. final boolean initialTryLock() {
  4. Thread current = Thread.currentThread();
  5. if (compareAndSetState(0, 1)) { // first attempt is unguarded
  6. setExclusiveOwnerThread(current);
  7. return true;
  8. } else if (getExclusiveOwnerThread() == current) {
  9. int c = getState() + 1;
  10. if (c < 0) // overflow
  11. throw new Error("Maximum lock count exceeded");
  12. setState(c);
  13. return true;
  14. } else
  15. return false;
  16. }
  17. /**
  18. * Acquire for non-reentrant cases after initialTryLock prescreen
  19. */
  20. protected final boolean tryAcquire(int acquires) {
  21. if (getState() == 0 && compareAndSetState(0, acquires)) {
  22. setExclusiveOwnerThread(Thread.currentThread());
  23. return true;
  24. }
  25. return false;
  26. }
  27. }

FairSync()

  1. # state是锁的计数。
  2. # compareAndSetState(0, 1) 原子操作, 意思是 if (state==0 ? ++state==1, false)
  3. if (hasQueuedThreads() && compareAndSetState(0, 1)):
  4. 独占线程=当前线程
  5. return true;
  6. else if (独占线程 == 当前线程):
  7. state++;
  8. return true
  9. else
  10. return false;
  1. public final boolean hasQueuedThreads() {
  2. for (Node p = tail, h = head; p != h && p != null; p = p.prev)
  3. if (p.status >= 0)
  4. return true;
  5. return false;
  6. }
  1. static final class FairSync extends Sync {
  2. private static final long serialVersionUID = -3000897897090466540L;
  3. /**
  4. * Acquires only if reentrant or queue is empty.
  5. */
  6. final boolean initialTryLock() {
  7. Thread current = Thread.currentThread();
  8. int c = getState();
  9. if (c == 0) {
  10. if (!hasQueuedThreads() && compareAndSetState(0, 1)) {
  11. setExclusiveOwnerThread(current);
  12. return true;
  13. }
  14. } else if (getExclusiveOwnerThread() == current) {
  15. if (++c < 0) // overflow
  16. throw new Error("Maximum lock count exceeded");
  17. setState(c);
  18. return true;
  19. }
  20. return false;
  21. }
  22. /**
  23. * Acquires only if thread is first waiter or empty
  24. */
  25. protected final boolean tryAcquire(int acquires) {
  26. if (getState() == 0 && !hasQueuedPredecessors() &&
  27. compareAndSetState(0, acquires)) {
  28. setExclusiveOwnerThread(Thread.currentThread());
  29. return true;
  30. }
  31. return false;
  32. }
  33. }

可重入锁

可重入就是说某个线程已经获得某个锁,可以再次获取锁而不会出现死锁。
Synchronized 和 ReenTrantLock都是可重入锁。
Synchronized 自动释放锁。
ReenTranLock手动释放锁。不是释放锁的话,其他代码无法拿到锁。

image.png
各个区域加了锁,但是都是同一个锁,所以只要“能进入大门,就能进入小门”。 也叫递归锁。

  1. public synchronized void operation(){
  2. add(); // 在同步方法内部,调用了另一个同步方法
  3. }
  4. public synchronized void add(){
  5. }
  1. public void m() {
  2. lock.lock();
  3. lock.lock();
  4. try {
  5. // ... method body
  6. } finally {
  7. lock.unlock()
  8. lock.unlock()
  9. }
  10. }

死锁

线程间互相等待。
image.png
image.png

  1. public class DeadLock {
  2. // 定义两个资源,作为锁。
  3. static Object obj1 = new Object();
  4. static Object obj2 = new Object();
  5. public static void main(String[] args) {
  6. new Thread(()->{
  7. synchronized (obj1){
  8. System.out.println(Thread.currentThread().getName() +"::"+ "持有锁obj1,等待锁obj2" );
  9. synchronized (obj2){
  10. System.out.println(Thread.currentThread().getName() + "::" + "获取到锁obj2");
  11. }
  12. }
  13. },"A").start();
  14. new Thread(()->{
  15. synchronized (obj2){
  16. System.out.println(Thread.currentThread().getName() +"::"+ "持有锁obj2,等待锁obj1" );
  17. synchronized (obj1){
  18. System.out.println(Thread.currentThread().getName() + "::" + "获取到锁obj1");
  19. }
  20. }
  21. },"B").start();
  22. }
  23. }

创建线程多种方式

image.png

Callable接口-多线程

比较runable

image.png
image.png
image.png

FutureTask : 使用callable接口

image.png
Thread支持runable接口构造,但不支持callable接口构造
解决办法:FutureTask
image.png

  • FutureTask原理 —- 未来任务

image.png

JUC 辅助类

countDown

image.pngimage.png

CyclicBarriar

image.png

Semaphore

image.png

读写锁

乐观锁、悲观锁

image.png
image.png

表锁、行锁

行锁支持并发,表锁不支持
行锁可能有死锁产生。

读锁(独占)、写锁(共享) : 都可能死锁

image.png

读写锁 - 演示