java多线程与锁

java并发学习

并发相关知识

并发相关知识

并发(Concurrency),并行(Parallelism)

并发:多项任务,交替执行

并行:多项任务,同时执行

同步(Synchronous),异步(Asynchronous)

描述的是针对某个调用 获取返回结果的方式:是同步等待,还是异步通知

同步:调用某项方法时,等待方法返回结果

异步:调用后马上返回,结果计算完后,通知调用者

阻塞(blocking),非阻塞(non-blocking)

描述的是多线程之间的相互影响

阻塞:一个线程占用了临界资源,其他线程必须等待这个线程释放资源

非阻塞:访问被其他线程占用的临界资源时, 不会阻塞等待,而立即返回

临界区

表示公共资源,多个线程访问或修改同一个资源

多线程竞争锁导致会问题
  1. 死锁:所有线程都不能动
  2. 饥饿锁:某个线程一直无法获取所需的资源
  3. 活锁:线程秉承谦让的原则,主动释放给他人使用,这样可能会导致资源在两个线程中跳动,而没有一个线程正常执行

并发级别

阻塞

一个线程会阻塞在 获取资源的步骤中,直到其他线程释放该资源,synchronized的锁为阻塞级别

无饥饿

如果获取锁是公平的,各个线程排队获取锁,则该锁是无饥饿的

无障碍
  1. 最弱的非阻塞调度
  2. 两个线程访问同一个临界区,都不会被对方所阻塞,一旦检测到某一方把数据改动了,则所有线程操作全部回滚
  3. 阻塞的控制方式是 悲观策略,假定两个线程之间很可能发生冲突,而非阻塞的调度是乐观的策略,认为多个线程不会发生冲突,或者概率不大,一旦发生冲突,就应该回滚
无锁
  1. 要求有一个线程可以在有限步内完成操作
  2. 当所有线程都能尝试对临界区访问,但只有一个线程能 进入临界区,其他的线程会不断尝试
无等待
  1. 要求所有线程必须在有限步内完成
  2. 典型的无等待结构是 RCU(read-copy-update),读无等待,更新时,先取得副本更新,然后适时写回

并行的两个重要定律

Amdahl定律
  1. 定义了串行系统并行化的加速比的计算公式,和理论上限

加 速 比 = 优 化 前 系 统 耗 时 / 优 化 后 系 统 耗 时 F : 为 系 统 串 行 比 例 T 1 : 为 一 个 处 理 器 的 耗 时 T n : 为 n 个 处 理 器 优 化 后 的 耗 时 T n = T 1 ( F + 1 n ∗ ( 1 − F ) ) 加 速 比 = T 1 T n = 1 F + 1 n ∗ ( 1 − F ) 加速比 = 优化前系统耗时 / 优化后系统耗时\\F:为系统串行比例\\T_1:为一个处理器的耗时\\T_n:为n个处理器优化后的耗时\\T_n = T_1(F+\frac{1}{n}*(1-F))\\加速比 = \frac{T_1}{T_n} = \frac{1}{F+\frac{1}{n}*(1-F)} =/F:T1:Tn:nTn=T1(F+n1(1F))=TnT1=F+n1(1F)1

  1. 由公式可分析出
    1. CPU处理器数量趋近于无穷,那么加速比与系统串行率成反比
    2. 如果系统串行率为50%,则系统最大加速比为2
Gustafson定律

a : 串 行 时 间 , b : 并 行 时 间 , n 处 理 器 个 数 实 际 执 行 时 间 = a + b 总 执 行 时 间 = a + n ∗ b 加 速 比 = a + n ∗ b a + b 串 行 比 例 = F = a a + b 加 速 比 = a + n ∗ b a + b = a a + b + n ∗ ( a + b − a ) a + b = F + n ∗ ( 1 − F ) = n − F ∗ ( n − 1 ) a:串行时间,b:并行时间,n处理器个数\\ 实际执行时间 = a+b\\ 总执行时间 = a+n*b\\ 加速比= \frac{a+n*b}{a+b}\\ 串行比例 = F = \frac{a}{a+b}\\ 加速比 = \frac{a+n*b}{a+b} = \frac{a}{a+b}+\frac{n*(a+b-a)}{a+b}=F+n*(1-F)=n-F*(n-1) a:,b:,n=a+b=a+nb=a+ba+nb=F=a+ba=a+ba+nb=a+ba+a+bn(a+ba)=F+n(1F)=nF(n1)

  1. 两个定律的不同点

    1. Amdahl定律侧重于 当 总任务一定时, 当串行比例一定时,加速比是有上线的
    2. Gustafson定律侧重于 不管F的值有多高,只要 n足够大,有足够的时间和 工作量,就能达到某个加速比

java多线程并发原则

原子性 Atomicity

函数调用过程中 不可被其他线程打断,要么成功,要么失败

可见性 visibility

对某一线程修改了某一个共享变量,其他线程能够立刻知道

有序性 ordering
  1. 在程序编译时可能 有指令重排:通过指令重排 减少CPU流水线指令的停顿
  2. 线程重排原则
    1. 程序顺序原则:一个线程内保证语义的串行性,不保证并行性
    2. volatile变量的写 先发生于读
    3. 锁规则:解锁必然发生在 加锁前
    4. 传递性: a 先于b,b先于c,a必然先于c
    5. 线程start方法优先于它的每一个动作
    6. 所有操作先于 线程的终结
    7. 中断先于 被中断线程的代码
    8. 对象的构造函数执行,结束先于finalize方法

java并行程序基础

线程状态变更图

线程状态详细图

在这里插入图片描述

java线程设计状态图

在这里插入图片描述

线程基本操作

新建线程:start

Thread.start()

线程停止:stop
  1. Thread.stop() :线程放弃一切工作,马上退出,这样会导致很多隐患
  2. 在线程内部设置停止标识:有线程自己决定在哪地方退出
线程中断:interrupt
  1. java已经实现中断标识,用于线程自行决定在哪里退出

    1. 判断是否中断:Thread.isInterrupted()
    2. 判断是否中断并清除中断标记:static Thread.interrupted()
    3. 发出中断:Thread.interrupt()
  2. Thread.sleep() 捕捉到中断之后,会清除中断标记

  3. code

    package com.weisanju;
    public class InterruptedTest {
        public static class AThread implements  Runnable{
            @Override
            public void run() {
                while(true){
                    if(Thread.currentThread().isInterrupted()){
                        System.out.println("已被中断");
                        break;
                    }
                    System.out.println(1);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        Thread.currentThread().interrupt();
                    }
                }
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            Thread thread = new Thread(new AThread());
            thread.start();
            Thread.sleep(1000);
    
            thread.interrupt();
        }
    }
    
线程等待:wait,notify
  1. 当线程执行到该行代码时, 且该对象为持有锁的对象,则线程进入等待状态,等待其他线程调用该对象的notify
  2. 这样就实现了多线程的简单通信
  3. code
package com.weisanju;

public class WaitNotifyTest {
    private  static  Object obj = new Object();

    public static class Athread implements  Runnable{
        @Override
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            synchronized (obj){
                System.out.println("A acquire lock");
                System.out.println("A通知B");
                obj.notify();
                System.out.println("通知完毕");
            }
        }
    }
    public static class Bthread implements  Runnable{
        @Override
        public void run() {
            synchronized (obj){
                System.out.println("B acquire lock");
                try {
                    obj.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("B被唤醒");
            }
        }
    }

    public static void main(String[] args) {
        new Thread(new Athread()).start();
        new Thread(new Bthread()).start();
    }
}

挂起与继续执行:suspend,resume
  1. suspend 挂起当前线程,但不释放锁,与资源, 直到调用resume才恢复执行
  2. 不推荐使用,推荐使用wait,notify
等待线程结束:join and yield
  1. join : 线程join 实际调用 Thread.wait() 方法,
  2. 当线程结束时,会通知所有等待在线程对象的其他线程
  3. yield 让出CPU,重新与其他线程竞争CPU调度

volatile关键字

  1. 修饰变量
  2. 告知各个线程,取变量值时,从主内存中取,不要从副本取

线程组

package com.weisanju;

public class ThreadGroupTest {
    public static class AThread implements  Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
    }

    public static void main(String[] args) {
        ThreadGroup threadGroup = new ThreadGroup("xjq");
        Thread t1 = new Thread(threadGroup,new AThread(),"t1");
        Thread t2 = new Thread(threadGroup,new AThread(),"t2");
        t1.start();
        t2.start();
        threadGroup.list();
        System.out.println(threadGroup.activeCount());
        System.out.println(threadGroup.activeGroupCount());
    }
}

守护线程

  1. 线程分为用户线程 ,守护线程

  2. 当用户线程执行完毕之后, 守护线程会自行退出

  3. 守护线程一般完成系统性服务,例如垃圾回收,JIT线程

  4. 代码

    package com.weisanju;
    
    public class DeamonTest {
        public static class  Athread implements  Runnable{
    
            @Override
            public void run() {
                while(true){
                    System.out.println(1);
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            Thread t = new Thread(new Athread());
            t.setDaemon(true);
            t.start();
            Thread.sleep(2000);
        }
    }
    
    

线程优先级

  1. Thread.MAX_PRIORITY = 10
  2. Thread.NORM_PRIORITY = 5
  3. Thread.MIN_PRIORITY = 1

jdk并发包

可重入锁

  1. 重入锁可以完全替代synchronized 关键字, jdk1.5 之间重入锁性能远远好于 synchronized 从1.6开始,jdk在synchronized 做了大量优化,使得两者性能差距并不大

  2. 特性

    1. 可重入性质: 一个线程可以连续两次获得锁, 但相应的得释放两次锁
    ReentryantLock lock1 = new ReentryantLock();
    lock1.lock()
    lock1.lock()
    lock1.unlock()	
    lock1.unlock()	
    
    1. 可中断性质
      1. 线程在尝试获取锁时,可被打断,并被打断后,释放相应的锁,让其他线程获取锁
      2. 案例 : 线程a, 线程b ,a先得到锁1,然后请求锁2,b先得到锁2,然后请求锁1
      3. 代码
    package com.weisanju;
    
    import java.util.concurrent.locks.ReentrantLock;
    
    public class DeadLock {
        private static ReentrantLock lock1= new ReentrantLock();
        private static ReentrantLock lock2= new ReentrantLock();
    
        public static class  ThreadTest implements  Runnable{
            private char name;
    
            public ThreadTest(char name) {
                this.name = name;
            }
    
            @Override
            public void run() {
                if(name == 'A'){
                    try {
                        lock1.lockInterruptibly();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    try {
                        Thread.sleep(1000);
                        lock2.lockInterruptibly();
                        System.out.println("A 得到锁了");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }finally {
                        if(lock1.isHeldByCurrentThread()){
                            lock1.unlock();
                        }
                        if(lock2.isHeldByCurrentThread()){
                            lock2.unlock();
                        }
                    }
    
                }else{
                    try {
                        lock2.lockInterruptibly();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    try {
                        Thread.sleep(1000);
                        lock1.lockInterruptibly();
                        System.out.println("B 得到锁了");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }finally {
                        if(lock1.isHeldByCurrentThread()){
                            lock1.unlock();
                        }
                        if(lock2.isHeldByCurrentThread()){
                            lock2.unlock();
                        }
                    }
                }
            }
        }
    
        public static void main(String[] args) {
            Thread ta = new Thread(new ThreadTest('A'));
            Thread tb = new Thread(new ThreadTest('B'));
    
            ta.start();
            tb.start();
    
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            tb.interrupt();
    
        }
    }
    
    
    1. 超时性质
      1. tryLock():尝试获取锁,获取不成功则马上返回
      2. tryLock(long mili):尝试获取锁,并等待指定时间段
    2. 公平锁
      1. 锁的申请遵循 先到先到,支持排队
      2. public ReentrantLock(boolean fair)
      3. 实现公平锁,系统需要维护一个有序队列,实现成本较高,性能太低
      4. 根据系统的调度,一个线程会倾向于再次获取已经持有的锁,这种锁分配是高效的

Conditional条件等待

  1. 与 synchronized 配合 wait,notify使用类似 , condition 配合与 Reentryant锁使用实现线程间通信
  2. 代码
package com.weisanju;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class ConditionalTest {
    private  static  int flag =0;
    private static ReentrantLock lock = new ReentrantLock();
    private static Condition condition= lock.newCondition();
    private  static class  AThread implements Runnable{
        @Override
        public void run() {
            lock.lock();
            System.out.println("正等待条件发生");
            try {
                condition.await();
                System.out.println(flag);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }

        }
    }

    public static void main(String[] args) throws InterruptedException {
        new Thread(new AThread()).start();
        flag = 666;
        Thread.sleep(200);
        lock.lock();
        System.out.println("已经获取锁");
        Thread.sleep(1000);

        condition.signal();
        lock.unlock();
    }
}

信号量

  1. API
    1. 构造函数:public Semaphore(int premits)
    2. 逻辑方法
      1. acquire|acquireUninterruptible()|tryAcquire()
      2. release
  2. 例子:省略

读写锁

  1. 读写操作互斥表

    不阻塞阻塞
    阻塞阻塞
  2. API

    1. ReentrantReadWriteLock
    2. lock.readLock(),lock.writeLock()

倒计时

  1. API
    1. 构造函数:public CountDownLatch(int count)
    2. 逻辑操作
      1. 计时器减1:CountDownLatch.countDown()
      2. 等待计时器归0:``CountDownLatch.await();`
      3. 获取计数器:CountDownLatch.getCount()

CyclicBarrier循环栅栏

  1. 每当有 parties 个 到达 wait 点时, 则执行barrierAction

  2. APi

    1. 构造函数:public CyclicBarrier(int parties, Runnable barrierAction)

    2. await:等待

    3. 一个线程在等待时被打断, 则其他线程抛出BrokenBarrierException,该线程抛出:InterruptedException

    4. code

      package com.weisanju;
      
      import java.util.concurrent.BrokenBarrierException;
      import java.util.concurrent.CyclicBarrier;
      
      public class CyclicBarrierTest {
          private static CyclicBarrier barrier = new CyclicBarrier(5,new BarrierRun(false));
          public  static  class Solider implements  Runnable{
              private int i;
      
              public Solider(int i) {
                  this.i = i;
              }
      
              @Override
              public void run() {
                  try {
                      barrier.await();
      
                      Thread.sleep(1000);
                      System.out.println("士兵"+i+"完成任务");
                      barrier.await();
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  } catch (BrokenBarrierException e) {
                      e.printStackTrace();
                  }
              }
          }
          public  static  class BarrierRun implements  Runnable{
              private boolean flag ;
      
              public BarrierRun(boolean flag) {
                  this.flag = flag;
              }
      
              @Override
              public void run() {
                  if (flag) {
                      System.out.println("任务完成");
                  }else{
                      System.out.println("集合完毕");
                      flag = true;
                  }
              }
          }
      
          public static void main(String[] args) {
              int n = 5;
      
              for (int i = 0; i < n; i++) {
                  System.out.println("士兵报数:"+i);
                  new  Thread(new Solider(i)).start();
              }
          }
      }
      
      

线程阻塞工具类

  1. API

    1. LockSupport.unpack(Object),LockSupport.pack(Thread)
    2. 类似于 值为1的信号量 操作
    3. unpack操作发生在pack操作之前,unpack使得许可可用,pack消耗许可
    4. 不需要获取锁
    5. 为每一个线程都拥有一个许可证
    6. 被打断之后正常返回,可以通过 Thread.isInterrputed
    7. unpack(Object):object 为日志打印时的对象
  2. code

    package com.weisanju;
    
    import java.util.concurrent.locks.LockSupport;
    
    public class LockSupportTest {
        public static  class AThread implements Runnable{
            @Override
            public void run() {
                LockSupport.park();
                if(Thread.currentThread().isInterrupted()){
                    System.out.println("被打断了");
                    return;
                }
                System.out.println("正常运行");
            }
        }
    
        public static void main(String[] args) {
            Thread t1 = new Thread(new AThread());
            Thread t2 = new Thread(new AThread());
    
            t1.start();
            t2.start();
            t1.interrupt();
            LockSupport.unpark(t2);
        }
    }
    

线程池

  1. API

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) 
    
  2. 参数解释

    1. corePoolSize:线程活跃数量
    2. maximumPoolSize:最大线程数量
    3. keepAliveTime:超过corePoolSize部分,空闲的线程存活时间
    4. TimeUnit:时间单位
    5. BlockingQueue:任务队列,提交但未运行
    6. ThreadFactory:创建线程的工厂
    7. handler:任务太多来不及处理的处理策略
  3. blockingQueue分三种

    1. 直接提交的队列
      1. 新任务提交给线程池时,如果线程数量<maximumPoolSize,则直接创建,否则拒绝
      2. SynchronousQueue
    2. 有界任务队列
      1. ArrayBlockingQueue
      2. 若已有线程数量 小于 corePoolSize ,则创建新的线程,直接运行
      3. 若大于corePoolSize ,则加入等待队列
      4. 若等待队列已满,且当前线程数量小于maximumPoolSize则新建线程
      5. 若当前线程数量已等于maximumPoolSize,则执行拒绝策略
    3. 无界任务队列
      1. LinkedBlockingQueue
      2. 若已有线程数量 小于 corePoolSize ,则创建新的线程,直接运行
      3. 若大于corePoolSize ,则加入等待队列
      4. 无界队列会一直增长 直到内存耗尽
    4. 优先任务队列:特殊的无界队列
      1. PriorityBlockingQueue:
  4. 内置四种拒绝策略

    1. AbortPolicy: 直接抛出异常
    2. CallerRunsPolicy:直接在调用者线程中运行当前被丢弃的任务
    3. DiscardOldestPolicy:丢弃最老的请求,也就是即将被执行的,并尝试再次提交当前任务
    4. DiscardPolicy:丢弃该任务
  5. ThreadFactory:自定义线程创建

    1. ThreadFactory是一个接口,只有 Thread newThread(Runnable r)接口
  6. 扩展线程池

    1. ThreadPoolExecutor 可扩展线程池
    2. code
    package com.weisanju;
    
    import java.util.concurrent.ArrayBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class ThreadPoolTest {
        public static void main(String[] args) {
            ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 100, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(20)) {
                protected void beforeExecute(Thread t, Runnable r) {
                    System.out.println("线程" + t.getName() + "开始运行");
                }
                protected void afterExecute(Runnable r, Throwable t) {
                    System.out.println( r.toString()+ "结束运行");
                }
    
                protected void terminated() {
                    System.out.println("线程池退出");
                }
            };
            executor.execute(()->{
                System.out.println("helloWorld");
            });
            executor.shutdown();
        }
    }
    
    
  7. 线程池的大小 引申自<< Java Concurrency in practice>>

N c p u = C p u 数 量 U c p u = c p u 的 使 用 率 , 0 < < U c p u < < 1 w c = 等 待 时 间 计 算 时 间 N t h r e a d s = N c p u ∗ U c p u ∗ ( 1 + w c ) Ncpu = Cpu数量\\ Ucpu = cpu的使用率, 0 << Ucpu << 1\\ \frac{w}{c} = \frac{等待时间}{计算时间}\\ Nthreads = Ncpu * Ucpu * (1+\frac{w}{c}) Ncpu=CpuUcpu=cpu使,0<<Ucpu<<1cw=Nthreads=NcpuUcpu(1+cw)

forkAndJoin库

并发容器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值