并发编程之第三篇(synchronized)

2020-02-10  本文已影响0人  小小一技术驿站

并发编程之第三篇(synchronized)

3. 自旋优化

重量级锁竞争的时候,还可以使用自旋来进行优化,如果当前线程自旋成功(即这时候持锁线程已经退出了同步块,释放了锁),这时当前线程就可以避免阻塞。
自旋重试成功的情况


在这里插入图片描述

自旋重试失败的情况


在这里插入图片描述

4. 偏向锁

轻量级锁在没有竞争时(就自己这个线程),每次重入任然需要执行CAS操作。
Java6中引入了偏向锁来做进一步优化 :只有第一次使用CAS将线程ID设置到对象的Mark Word头,之后发现这个线程ID是自己的就表示没有竞争,不用重新CAS。以后只要不发生竞争,这个对象就归该线程所有
例如 :


在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

偏向状态
回忆一下对象头格式

在这里插入图片描述

一个对象创建时 :

撤销-其它线程使用对象

当有其它线程使用偏向锁对象时,会将偏向锁升级为轻量级锁


在这里插入图片描述 在这里插入图片描述

撤销-调用wait/notify

批量重偏向

如果对象虽然被多个线程访问,但没有竞争,这时偏向了线程T1的对象仍有机会重新偏向T2,重新偏向重置对象的Thread ID
当撤销偏向锁阈值超过20次后,jvm会这样觉得,我是不是偏向错了,于是会在给这些对象加锁时重新偏向至加锁线程


在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

批量撤销

当撤销偏向锁阀值超过40次后,jvm会这样觉得,自己确实偏向错了,根本就不该偏向。于是整个类的所有对象都会变为不可偏向的,新建的对象也是不可偏向的


在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

5. 锁消除

锁消除


在这里插入图片描述 在这里插入图片描述

4.7 wait/notify

在这里插入图片描述

API介绍

4.8 wait notify的正确姿势

sleep(long n)和wait(long n)的区别
1)sleep是Thread方法,而wait是Object的方法
2)sleep不需要强制和synchronized配合使用,但wait需要和synchronized一起用
3)sleep在睡眠的同时,不会释放对象锁的,但wait在等待的时候会释放对象锁
4)它们状态都是TIMED_WAITING


在这里插入图片描述

wait和notify正确使用姿势


在这里插入图片描述

同步模式之保护性暂停

即Guarded Suspension,用在一个线程待得另一个线程的执行结果
要点

异步模式之生产者/消费者

要点

package com.example.demo;

import lombok.extern.slf4j.Slf4j;

import java.util.LinkedList;

@Slf4j()
public class Test1 {

    public static void main(String[] args) {
        MessageQueue messageQueue = new MessageQueue(2);

        for (int i = 0; i < 3; i++) {
            int id = i;
            new Thread(() -> {
                messageQueue.put(new Message(id, "值" + id));
            }, "生产者" + i).start();
        }

        new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Message take = messageQueue.take();
            }
        }, "消费者").start();
    }

}

/**
 * 消息队列类,java线程之间通信
 */
@Slf4j(topic = "cn.Message")
class MessageQueue {

    /**
     * 消息的队列集合
     */
    private LinkedList<Message> linkedList = new LinkedList<>();

    /**
     * 队列容量
     */
    private int capcity;

    public MessageQueue(int capcity) {
        this.capcity = capcity;
    }

    /**
     * 获取消息
     */
    public Message take() {
        // 检查对象是否为空
        synchronized (linkedList) {
            while (linkedList.isEmpty()) {
                try {
                    log.info("队列为空,消费者线程等待");
                    linkedList.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 从队列头部获取消息并返回
            Message message = linkedList.removeFirst();
            log.info("已消费消息 : {}", message);
            linkedList.notifyAll();
            return message;
        }
    }

    /**
     * 存入消息
     */
    public void put(Message message) {
        synchronized (linkedList) {
            // 检查对象是否已满
            while (linkedList.size() == capcity) {
                try {
                    log.info("队列已满,生产者线程等待");
                    linkedList.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 将消息添加到队列的尾部
            linkedList.addLast(message);
            log.info("已生产消息 : {}", message);
            linkedList.notifyAll();
        }
    }
}

class Message {
    private int id;
    private Object value;

    public Message(int id, Object value) {
        this.id = id;
        this.value = value;
    }

    public int getId() {
        return id;
    }

    public Object getValue() {
        return value;
    }

    @Override
    public String toString() {
        return "Message{" +
                "id=" + id +
                ", value=" + value +
                '}';
    }
}

4.9 Park & Unpark

基本使用
它们是LockSupport类中的方法


在这里插入图片描述

先看park再unpark


在这里插入图片描述

输出


在这里插入图片描述

特点
与Object的wait&notify相比

原理之park & unpark

每个线程都有自己的一个Parker对象,由三部分组成_counter,_cond和_mutex打个比喻

  1. 当前线程调用Unsafe.park()方法

  2. 检查_counter,本情况为0,这时,获得_mutex互斥锁

  3. 线程进入_cond条件变量阻塞

  4. 设置_counter = 0


    在这里插入图片描述
    1. 调用Unsafe.unpark(Thread_0)方法,设置_counter为1

    2. 唤醒_cond条件变量中的Thread_0

    3. Thread_0恢复运行

    4. 设置_counter为0


      在这里插入图片描述

      1.调用Unsafe.unpark(Thread_0)方法,设置_counter为1
      2.当前线程调用Unsafe.park()方法
      3.检查_counter,本情况为1,这时线程无需阻塞,继续运行
      4.设置_counter为0

4.10 重新理解线程状态转换

在这里插入图片描述

假设有线程Thread t

情况1 New --》RUNNABLE

情况2 RUNNABLE < – > WAITING
t 线程用synchronized(obj)获取了对象锁后

情况3 RUNNABLE < – > WAITING

情况4 RUNNABLE < – > WAITING

情况5 RUNNABLE < – > TIMED_WAITING
t线程用synchronized(obj)获取了对象锁后

情况6 RUNNABLE < – > TIMED_WAITING

情况7 RUNABLE < – > TIMED_WAITING

情况8 RUNNABLE < – > TIMED_WAITING

情况9 RUNNABLE <–>BLOCKED

情况10 RUNNABLE < – > TERMINATED
当前线程所有代码运行完毕,进入TERMINATED

上一篇 下一篇

猜你喜欢

热点阅读