JAVA多线程并发编程学习笔记2——线程同步和通信

JAVA并发编程

3. 线程同步

3.1 相关概念

3.1.1 含义

线程同步机制是一套用于协调线程之间的数据访问的机制,用来保证线程安全。

3.1.2 方法
  • volatile关键字
  • final, static关键字
  • 相关API,如wait(), sleep(), notify()等

3.2 锁

3.2.1 含义

锁可以理解为对共享数据进行保护的一个许可证,线程在访问共享数据前必须先获得锁,一个锁只能同时被一个线程持有,线程在结束访问之后释放锁。

3.2.2 作用
  • 保障原子性:通过互斥保障原子性
  • 保障可见性:获得锁和释放锁会刷新处理器缓存
  • 保障有序性:
3.2.2 重进入
  • 可重入锁:线程可以继续成功申请已经占有的锁。
  • 内部锁(synchronized)是可重进入的,属于非公平锁;显示锁既支持公平锁又支持非公平锁。
3.3.3 粒度
  • 一个锁可以保护的共享数据的数量大小称为锁的粒度。
  • 锁保护共享数据量大,称该锁的粒度粗, 否则就称该锁的粒度细。
  • 锁的粒度过粗会导致线程在申请锁时会进行不必要的等待,锁的粒度过细会增加锁调度的开销 。
3.3.4 减少锁的竞争

减少锁的竞争能够改进性能和可伸缩性,有3种方式减少锁的竞争

  • 减少持有锁的时间
  • 减少持有锁的频率
  • 用协调机制取代独占锁

3.3 synchronized

3.3.1 含义
  • synchronized块是Java提供的内置锁机制,包括锁对象的引用和锁保护的代码块。
  • 每个对象都可以隐式地作为同步锁,称作内部锁或监视器锁。内部锁是互斥锁(mutex)。
3.3.2 同步代码块
  • 代码实现

    synchronized(anObject){
        //do something synchronized
    }
    
  • 只有使用同一个锁对象才能实现同步

3.3.3 同步方法
  • 代码实现

    synchronized returnType aMethod{
        //do something synchronized
    }
    
  • synchronized修饰实例方法,默认this作为锁对象

  • synchronized修饰实例方法,默认运行时类对象(.class)作为锁对象

3.4 volatile

3.4.1 概念
  • 作用:使共享变量在多个线程之间可见

  • 原理:当域被声明为volatile之后,编译器会随时监控这个变量,且对其操作不会被重排序,变量不会缓存在工作内存(对其他线程隐藏)而是在公共内存中。

  • 典型用途:标识重要的生命周期事件

3.4.2 对比
性质volatilesynchronized
特点轻量级的同步机制,开销小开销较大
修饰对象变量方法,代码块
阻塞不会
可见性
原子性不能

3.5 硬件对并发的支持

3.5.1 比较并交换CAS
  • CAS有三个操作数,内存位置V、旧的预期值A和新值B,当且仅当V符合预期值A时,CAS用新值原子化地更新V的值。

  • 代码模拟

    @ThreadSafe
    public class SimulatedCAS {
        @GuardedBy("this") private int value;
    
        public synchronized int get() {
            return value;
        }
    
        public synchronized int compareAndSwap(int expectedValue,
                                               int newValue) {
            int oldValue = value;
            if (oldValue == expectedValue)
                value = newValue;
            return oldValue;
        }
    
        public synchronized boolean compareAndSet(int expectedValue,
                                                  int newValue) {
            return (expectedValue
                    == compareAndSwap(expectedValue, newValue));
        }
    }
    
  • 不足:不能避免ABA问题。即共享变量的当前值与当前线程提供的期望值相同, 就认为这个变量没有被其他线程修改过。

    ​ (可以引入修订号判断是否被其他线程修改过,例如AtomicStampedReference类)

3.6 原子变量类

3.6.1 含义
  • 原子变量类基于CAS操作实现了volatile变量的原子性
3.6.2 种类
类型类名
数据类型AtomicInteger, AtomicLong, AtomicBoolean
数组AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray
字段更新器AtomicIntegerFieldUpdater, AtomicLongFieldUpdater, AtomicReferenceFieldUpdater
引用型AtomicReference, AtomicStampedReference, AtomicMarkableReference
3.6.3 使用方法

3.7 活跃度危险

3.7.1 死锁
  • 含义:多个线程因为环路的锁依赖关系而永远等待。

  • 原因:多线程程序同步时可能需要使用多个锁,如果获得锁的顺序不一致可能会导致死锁 。(锁顺序死锁)

  • 类别

    类型描述
    锁顺序死锁多线程获得锁的顺序不一致
    动态锁顺序死锁获得锁的顺序与参数有关
    协作对象间的死锁持有锁的时候调用外部方法,外部方法可能会获得其他锁
    资源死锁多个线程因为环路的资源依赖关系而永远等待
  • 解决方法

    • 使用定时锁,例如显示锁Lock类的tryLock方法
    • 通过线程转储分析死锁
3.7.2 饥饿
  • 含义:线程被永久拒绝访问所需的资源
  • 原因:不当使用线程优先级;在锁中无休止地循环或等待。
3.7.3 活锁
  • 含义:线程未被阻塞,但不断失败地重试相同的操作。
  • 原因:过渡的错误恢复代码
  • 解决:对重试机制引入随机性、

4 线程间通信

4.1 等待通知

4.1.1 概念
  • 线程中某一条件暂时没有满足便先等待进入阻塞,稍后其他线程通知此线程条件满足,原线程恢复。
4.1.2 wait()
  • wait()方法只能在同步代码块中由锁对象调用

  • 调用wait()方法,当前线程会释放锁 ,直到被另一线程调用该锁的notify()或notifyAll()。

  • 带参方法

    • wait(long):就算未被通知,在指定毫秒时间内也会恢复
    • wait(long, int):指定毫秒和纳秒
  • 代码实现

    synchronized(aLock){
        aLock.wait();
    }
    
  • 当线程处于等待状态时, 调用线程对象的interrupt()方法会中断线程的等待状态, 并产生InterruptedException异常。

4.1.3 notify()
  • notify()方法只能在同步代码块中由锁对象调用,否则会抛出异常。
  • 调用notify()方法,会唤醒某一个等待的线程,并且要等到同步代码块执行完才会释放锁对象。
  • notifyAll()可以唤醒所有线程

4.2 ThreadLocal

4.2.1 概念
  • 作用:使每个线程与持有数值的对象关联在一起。
  • 优点:可以避免每次都重新分配对象
4.4.2 实现
  • 方法

    • get()
    • set(obj)
    • initialValue():在子类中重写改方法,可设置get()初值
  • 代码

    //example_1
    ThreadLocal threadLocal = new ThreadLocal();
    threadLocal.set(t1);
    T t2 = threadLocal.get();
    
    //example_generic
    ThreadLocal<T> threadLocal = new ThreadLocal()<>;
    
    //example_setInitialValue
    class MyLocal extends ThreadLocal<T>{
        @override
        protected T initialValue(){
            return new T();
        }
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
嗨!很高兴回答你关于Java并发编程的问题。请问你想知道什么方面的内容呢?我可以分享一些学习笔记和建议给你。 1. 并发编程基础:了解并发编程的基本概念,如线程、进程、锁、同步等。学习Java中的并发编程模型以及相关的API,如Thread、Runnable、Lock、Condition等。 2. 线程安全性:学习如何保证多线程环境下的数据安全性,了解共享资源的问题以及如何使用同步机制来防止数据竞争和并发问题。 3. 线程通信:掌握线程通信方式,如使用wait/notify机制、Lock/Condition等来实现线程的协调与通信。 4. 并发容器:学习并发容器的使用,如ConcurrentHashMap、ConcurrentLinkedQueue等。了解它们的实现原理以及在多线程环境下的性能特点。 5. 并发工具类:熟悉Java提供的并发工具类,如CountDownLatch、CyclicBarrier、Semaphore等,它们可以帮助你更方便地实现线程的协作。 6. 并发编程模式:学习一些常见的并发编程模式,如生产者-消费者模式、读者-写者模式、线程池模式等。了解这些模式的应用场景和实现方式。 7. 性能优化与调试:学习如何分析和调试多线程程序的性能问题,了解一些性能优化的技巧和工具,如使用线程池、减少锁竞争、避免死锁等。 这些只是一些基本的学习笔记和建议,Java并发编程是一个庞大而复杂的领域,需要不断的实践和深入学习才能掌握。希望对你有所帮助!如果你有更具体的问题,欢迎继续提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值