今日份学习: 多线程

多线程原理

为什么需要多线程?

  • 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 订单实在太多的处理策略
posted @ 2020-03-15 17:39  带了1个小才艺  阅读(169)  评论(0编辑  收藏  举报