《Java并发编程的艺术》笔记

CAS三大问题
  • ABA问题,可以使用版本号解决,JDK提供了类AtomicStampedReference解决该问题。
  • 循环时间长,开销大。
  • 只能保证一个共享变量的原子操作,JDK提供了类AtomicReference解决该问题。

JVM内部实现了多种锁机制,有偏向锁、轻量级锁和重量级锁(也称为互斥锁)。除了偏向锁,其他都使用了循环CAS,即:使用循环CAS的方式去获取锁和释放锁。

偏向锁:只适用于一个线程访问同步块的场景

轻量级锁:不阻塞线程,如果线程得不到锁,使用自旋去获取锁,消耗CPU

重量级锁:阻塞线程,不使用自旋,响应时间长

强引用、软引用、弱引用、虚引用以及ThreadLocal原理

强引用、软引用、弱引用、虚引用以及ThreadLocal原理

daemon线程

daemon线程时一种支持型线程,因为它主要被用做程序中后台调度以及支持性工作。
当Java虚拟机中不存在非daemon线程时,Java虚拟机将会退出,所以daemon线程中run方法不一定会执行,run方法中finally模块也不一定执行。

public static void main(String[] args) {
    Thread t = new Thread(() -> {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("finally");
        }
    });

    t.setDaemon(true);
    t.start();
}

控制台不一定会打印finally。
线程的中断状态

抛出InterruptedException的方法(例如Thread.sleep(2000)),在抛出该异常之前,会先把线程的中断状态清除,再抛出异常。此时调用isInterrupted()方法会返回false

解决方式:加上Thread.currentThread().interrupt()主动恢复中断状态。

public static void main(String[] args) {
    Thread thread = new Thread(() -> {
        try {
            while (true) {
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println(Thread.currentThread().isInterrupted());//false
            // 恢复中断状态,以避免屏蔽中断
            Thread.currentThread().interrupt();
            System.out.println(Thread.currentThread().isInterrupted());//true
        }
    });
    thread.start();

    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    thread.interrupt();
}
等待/通知经典范式:
// 等待方
synchronized (对象){
    while (条件不满足){
        对象.wait();
    }
    //逻辑代码
    ...
}

// 通知方
synchronized (对象){
    //逻辑代码
    ...
    修改条件 //使等待方跳出while循环
    对象.notify();
}
synchronized

每个对象都有一个监视器,synchronized处理并发时,线程先尝试获取监视器,获取成功(即获取锁)则往下执行。获取失败,则进入队列。当获取锁的线程执行结束释放了锁,同时也会唤醒队列中阻塞的线程,使其重新尝试获取监视器。

ReentrantLock 重入锁

基于队列同步器AbstractQueuedSynchronizer(AQS)实现锁的功能。AQS使用volatileint型变量state表示同步状态,并通过FIFO队列(称为同步队列)来完成线程排队工作。其中运用了happens-before原则中的volatile变量规则:对一个volatile域的写happens-before任意后续对该volatile域的的读。
CountDownLatch、CyclicBarrier、Semaphore也是基于AQS)

了解volatile关键字

跟着源码学信号量Semaphore

每个Condition对象都包含一个队列,称为等待队列。该队列是Condition对象实现等待/通知功能的关键。

Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
lock.lock();
//未获取到锁的线程加入同步队列尾部
private Node addWaiter(Node mode) {
    Node node = new Node(Thread.currentThread(), mode);
    // Try the fast path of enq; backup to full enq on failure
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    enq(node);
    return node;
}
condition.await();
public final void await() throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    //获取到锁的线程加入等待队列尾部
    Node node = addConditionWaiter();
    //释放锁
    int savedState = fullyRelease(node);
    int interruptMode = 0;
    //判断节点是否已转移到同步队列,未转移,则阻塞线程。
    while (!isOnSyncQueue(node)) {
        // 阻塞线程
        LockSupport.park(this);
        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
            break;
    }
    //尝试去获取锁
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
        interruptMode = REINTERRUPT;
    if (node.nextWaiter != null) // clean up if cancelled
        unlinkCancelledWaiters();
    if (interruptMode != 0)
        reportInterruptAfterWait(interruptMode);
}
condition.signal()
public final void signal() {
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    Node first = firstWaiter;//等待队列的头节点
    if (first != null)
        doSignal(first);
}
private void doSignal(Node first) {
    do {
        if ( (firstWaiter = first.nextWaiter) == null)
            lastWaiter = null;
        first.nextWaiter = null;
        // 转移成功,跳出循环
    } while (!transferForSignal(first) &&
             (first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
    /*
     * If cannot change waitStatus, the node has been cancelled.
     */
    if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
        return false;

    /*
     * Splice onto queue and try to set waitStatus of predecessor to
     * indicate that thread is (probably) waiting. If cancelled or
     * attempt to set waitStatus fails, wake up to resync (in which
     * case the waitStatus can be transiently and harmlessly wrong).
     */
    // 转移节点到同步队列
    Node p = enq(node);
    int ws = p.waitStatus;
    if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
        // 唤醒线程
        LockSupport.unpark(node.thread);
    return true;
}

//使用signal()比signalAll()的开销更小(避免将等待队列中的线程全部移动到同步队列中)
ReentrantReadWriteLock

如果在一个整型变量上维护多种状态,就一定需要“按位切割”使用这个变量。

同步状态值:state

WriteLock:取state低16位 state & 0x0000FFFF(state & (1 << 16) - 1),由于是低16位,获取锁时,state + 1

ReadLock:取state高16位 state >>> 16,由于是高16位,获取锁时,state + 1 << 16

判断当前线程获取的是读锁还是写锁:

state != 0state & 0x0000FFFF == 0时,线程获取的是读锁。反之,则为写锁。

ConcurrentHashMap

Hashtable、HashMap、ConcurrentHashMap对比

线程池

理解Java线程池ThreadPoolExecutor

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,000评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,745评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,561评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,782评论 1 298
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,798评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,394评论 1 310
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,952评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,852评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,409评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,483评论 3 341
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,615评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,303评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,979评论 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,470评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,571评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,041评论 3 377
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,630评论 2 359

推荐阅读更多精彩内容