• 今日份学习: 多线程


    多线程原理

    为什么需要多线程?

    • CPU/内存/IO的巨大性能差异

    • 多核CPU的发展



    • 一个线程表现的本质就是多出来一套完整的方法栈

      • 优点:多个执行流,并发执行

      • 缺点:

        • 占用资源:每个线程有独立的方法栈
        • 慢,切换上下文
      • 能不能让上下文切换尽可能少?

        • 协程 - 用户态线程

    Thread

    • Thread类的每一个实例代表一个JVM中的线程

    只有Thread是线程,其他的都不是线程(注释中提到),Runnable和Callable是一个任务,一小段代码

    Runnable / Callable

    • Runnable代表一个任务
      • 可以被任何一个线程执行
    • Callable解决了Runnable的一些问题
      • Runnable不能返回值
      • Runnable不能抛出checked exception

    Runnable jdk1.0

    Callable jdk1.5

    笔记

    • 多线程缺点:

      1. 代码复杂,变得很难理解
      2. 线程的互相交互和访问共享变量会引发各种问题,包括死锁等
    • try-catch只会捕获当前线程的异常

    • 异常的抛出是顺着方法栈往外抛的

    • 线程的底部要么是main()方法,要么是Thread.run()方法

    “在可计算性理论里,如果一系列操作数据的规则(如指令集、编程语言、细胞自动机)可以用来模拟单带图灵机,那么它是图灵完备的。

    • 当两个线程运行的时候,方法栈的所有东西都是私有的,其他的都是公有的。

    Volatile

    • 线程模型:

    每一个线程可以有一个主内存的变量的副本,cpu定期同步回内存。

    volatile的保证

    • 可见性,并非原子性
      • 写入volatile变量会直接写入主内存
      • 从volatile变量读取会直接读取主内存
      • 非常弱的同步机制
    • 禁止指令重排
      • 编译器和处理器都可能堆指令进行重排,导致问题
    • 有同步的时候无需volatile
      • synchronized/Lock/AtomicInteger




    JUC:java.util.concurrent 并发工具包

    • 同步:synchronized
    • 协同:wait() / notify() / notifyAll()

    synchronized

    • synchronized

      • Java语言级的支持,1.6之后性能及大提高
        • 字节码层面的实现:monitorenter / monitorexit
      • 锁住的是?
        • 对象
        • 如果是静态方法,那么锁住的是该类的class
        • 如果是非静态方法,那么锁住的是this对象
    • 缺点

      • 死板。比如:不能查看是否有别人拿到了锁对象。

      • 只有悲观锁、排他锁,没有乐观锁、共享锁

    一定要和一个对象(monitor,监视器)协同

    wait和sleep有什么区别?

    • wait() 之后锁对象就会被释放
    • 如果已经获取了锁对象,那么sleep() 之后不会释放锁对象。如果没获取锁对象,那么不关sleep() 什么事。。。

    JUC包AtomicXXX

    • AtomicInteger / AtomicBoolean / AtomicLong / Atomic Reference..
    • 全都是Compare And Swap,提高性能
    CAS是一种有名的无锁(lock-free)算法。也是一种现代 CPU 广泛支持的CPU指令级的操作,只有一步原子操作,所以非常快。而且CAS避免了请求操作系统来裁定锁的问题,不用麻烦操作系统,直接在CPU内部就搞定了。
    
    CAS有三个操作参数:
    
    内存位置V(它的值是我们想要去更新的)
    预期原值A(上一次从内存中读取的值)
    新值B(应该写入的新值)
    CAS的操作过程:将内存位置V的值与A比较(compare),如果相等,则说明没有其它线程来修改过这个值,所以把内存V的的值更新成B(swap),如果不相等,说明V上的值被修改过了,不更新,而是返回当前V的值,再重新执行一次任务再继续这个过程。
    
    所以,当多个线程尝试使用CAS同时更新同一个变量时,其中一个线程会成功更新变量的值,剩下的会失败。失败的线程可以重试或者什么也不做。
    

    Lock / Condition

    Condition.java中的注释:

    {@code Condition} factors out the {@code Object} monitor
    methods ({@link Object#wait() wait}, {@link Object#notify notify}
    and {@link Object#notifyAll notifyAll}) into distinct objects to
    give the effect of having multiple wait-sets per object, by
    combining them with the use of arbitrary {@link Lock} implementations.
    Where a {@code Lock} replaces the use of {@code synchronized} methods
    and statements, a {@code Condition} replaces the use of the Object
    monitor methods.
    
    • Lock/Condition与synchronized/wait/notify机制
    • 更加灵活
      • 同一个锁可以有多个条件
      • 读写分离
      • tryLock:尝试获得锁(相比synchronized的不能查看别人是否已经获得了锁,更加的方便)
      • 可以方便的实现更加灵活的优先级/公平性?
        • 公平锁和非公平锁

    CountDownLatch

    • 倒数闭锁
    • 用于协调一组线程的工作
    • API:
      • countDown()
      • await()
    public class Juc {
        public static void main(String[] args) throws InterruptedException {
            CountDownLatch latch = new CountDownLatch(10);
            for (int i = 0; i < 10; i++) {
                int finalI = i;
                new Thread(() -> {
                    // 每个工人开始干活
                    int second = new Random().nextInt(10);
                    try {
                        Thread.sleep(second * 1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程" + finalI + "活干完了!");
                    //干完活倒计-1
                    latch.countDown();
                }).start();
            }
            latch.await();
            System.out.println("所有的工人都干完活了!");
        }
    }
    

    CyclicBarrier

    • API:
      • await()
    public class Juc {
        public static void main(String[] args) throws InterruptedException {
            CyclicBarrier barrier = new CyclicBarrier(10);
            for (int i = 0; i < 10; i++) {
                int finalI = i;
                new Thread(() -> {
                    // 每个工人开始干活
                    int second = new Random().nextInt(10);
                    try {
                        Thread.sleep(second * 1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程" + finalI + "活干完了!");
                    try {
                        barrier.await();
                    } catch (InterruptedException | BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                    System.out.println("等待其他人到了才能继续!");
                }).start();
            }
        }
    }
    
    

    Semaphore

    • 信号量:获取和释放

    acquire()获取

    release()释放

    BlockingQueue / BlockingDeque

    • API:
      • 阻塞操作:put / take

    Future与ExecutorService

    • Future代表⼀个「未来才会发⽣的事情」
    • Future本身是⽴即返回的
    • get()会阻塞并返回执⾏结果,并抛出可能的异常

    线程池详解

    • 线程池的参数们
    • corePoolSize 核⼼员⼯数量
    • maximumPoolSize 最⼤招募的员⼯数量
    • keepAliveTime/unit 员⼯闲下来多久之后炒掉他们
    • workQueue 订单队列
    • threadFactory 造⼈的⼯⼚
    • handler 订单实在太多的处理策略
  • 相关阅读:
    AGC007题解
    博弈论学习笔记
    ZROI2019 提高十连测
    男人八题2019
    LOJ 2840「JOISC 2018 Day 4」糖
    CF671D Roads in Yusland
    网络流套路小结
    BZOJ 3729 GTY的游戏
    AGC036C GP 2
    BZOJ 5046 分糖果游戏
  • 原文地址:https://www.cnblogs.com/pipemm/p/12498789.html
Copyright © 2020-2023  润新知