5
21
¥ 99.00购买
开通会员,立省9.90元, 立即开通
立即购买

你将收获

掌握线程安全、线程和线程池的基本概念

掌握核心并发类的实现原理,在工作中能够对不同场景使用不同的类去实现

理解非并发容器到并发容器的实现改变点,可以避免在工作中踩坑

掌握这些知识的积累和组合,对于后续需要设计高并发架构有很大用途

适用人群

本课程适合想要在Java并发上有本质提升的同学,适合对并发了解但不够深入的同学。 对于有了一定基础,但是忘记了又想回过头重新深入的同学同样适合。

课程介绍

课程会讲解Java中并发相关技术的基础、原理和应用,从线程安全、线程(池),
锁实现和并发容器等高并发Java实现,去深入理解在并发编程中,
一些最容易被忽视的点,这些点也是我在多年编程经验中实际用到,
对于每个小节小课均会有实际的场景应用为你呈现。
同时这些也是面试过程中面试官考察的重点,掌握这些点,面试和工作时,做到心中有货,不慌不乱,能够胜任。

本课知识点

java并发应用线程安全面试编程编程语言Java

课程讨论

threadA.join(); threadA.join():主线程需要等待子线程执行完毕才可以继续执行,(不阻塞子线程,只阻塞主线程) 参考:这个https://www.jianshu.com/p/595be9eab056

老师讲的很好,很仔细,推荐其他同学一起来学习!

应该也是get()方法吧,我看submit()方法返回的是Future,Future类中也是使用get()和get(long timeout, TimeUnit unit)来获取返回结果

逻辑清晰~~干货满满,每次打开都能学到东西,看两遍了

课后思考的这个问题答案谁能给我讲下。谢谢

很清晰~谢谢老师让我了解线程这一块的内容

课后思考的答案在哪看 ?

比较值得学习的课,老师会把一些细节讲清,理解起来更加容易,希望自己能力可以快速提高吧

https://github.com/zhuzhenke/com-concurrent-course无法访问呀

1111111111111111111111111

同学笔记

  • weixin_42613487 2020-04-27 17:56:12

    来源:线程安全 查看详情

    1、线程安全:一个类被多个线程以任意的方式同时调用,且不需要外部额外同步和协同的情况下,仍然保持内部数据正确且表现正确的行为,那么这个类就是线程安全的。


    2、线程安全的等级:线程对立,线程兼容,有条件的线程安全,线程安全,不可变。

    (1)不可变类:不可变的对象一定是线程安全的。如:final类修饰的不可变类,String,Integer等;enum枚举类。

    • 编译javac;反编译jad
    • enum底层是一个final类 
    • 是不是final修饰的类就是线程安全的呢?不是。final可以使得另一个线程不会访问处于“部分创建”的对象,除了此种情况,线程不安全是可能会发生的。final只能保证值不被覆盖。

    (2)线程安全的类

    • 线程安全类的任意方法都不会使该对象的数据处于不一致或者数据污染的情况。
    • java.util.concurrent下的类。如ConcurrentHashMap,LinkedBlockingQueue

    (3)有条件的线程安全

    • 单独的访问类的方法,是线程安全的;复合方法时,不保证。

    (4)线程兼容类

    • 使用同步
    • 使用锁或者synchronized包含每一个方法

    (5)线程对立

    • 外部使用同步也不能保证线程安全
    • 比如静态变量

    3、同步异步

     (1)同步:阻塞式调用,调用方必须等待响应方执行完毕才会返回。调用结果返回前,当前线程会被挂起。

     (2)异步:非阻塞式调用,立即返回,调用方无需等待响应方返回实际结果。响应方会通过状态、通知或回调来告知对方。

    • 耗时任务
    • 考虑性能时。比如电商下单链路的非核心链路调用,交易系统通过消息自发自接来驱动后续流程。


    4、并发和并行

     (1)多线程程序:程序由多个线程构成,方法内个代码块执行顺序是不确定的。

     (2)并发:一个处理器通过时间调度,处理多个线程

     (3)并行:实际上多个处理器同时处理

     (4)多核处理器处理能力强于单核处理器,且有更少的系统调度和上下文切换。


    5、线程的状态(6)

    • NEW:线程被创建,但还没有启动(没有调用start)
    • RUNNABLE:可运行状态。在java虚拟机中,等待处理器等资源;正在被执行。(RUNNING和READY)
    • BLOCKED:阻塞状态。等待monitor lock,来进入或重入同步代码块。在被wait()后进入BLOCKED
    • WAITING:等待状态。在调用Object.wait()\Thread.join()\LockSupport.park()后。需要其他线程中断或唤醒。
    • TIMED_WAITING:加了时间限制的等待
    • TERMINATED:终止,线程以执行
    • Thread.yield():释放资源,和其他线程一起抢夺资源
    • Thread.wait():主动让出当前CPU时间,但不会释放当前的锁
    • Thread.join():这个线程执行完后,别的线程才能执行
    • Object.wait():调用前必须拥有锁。调用时会释放锁

    6、线程死锁和避免

     (1)死锁的避免

    • 不使用锁,或不使用2把以上的锁
    • 保证整个应用程序中获取锁的顺序是一致的
    • 尝试获取具有超时释放的锁,例如lock中的tryLock来实现
    • 当发生Java-level的锁时,重启程序来干掉进程/线程

    (2)死锁的定位

    • jps 列举正在运行的饿虚拟机进程并显示虚拟机执行的主类以及这些进程的唯一ID(PID)
    • jstack 用于当前时刻的线程快照,得到JVM当前每一条线程正在执行的栈堆信息,定位线程长时间卡顿问题,如死锁、死循环问题。

    7、JAVA内存模型

    (1)操作系统的内存模型

    • 多级缓存。L1L2是每个CPU自己的高速缓存,L3是多CPU之间的共享缓存。

    (2)Java内存模型

    • 每个线程有自己的工作内存,工作内存包含线程本地局部变量和主内存的副本拷贝
    • 线程之间的共享变量通过主内存在各线程间同步
    • 由于可见性的问题,每个线程拿到的值不一定是最新的
    • 重排序:源代码->编译器重排序->处理器重排序->最终代码执行顺序
    • Happens-before:

    8、synchronized和volatile

    (1)synchronized

    • 控制方法(如private static synchronized boolean 函数名(){})和变量(synchronized(Object))的加锁
    • 只有非公平锁(放锁后别的所有等待的线程去竞争)
    • 可重入锁

    (2)volatile

    • 用来保证多线程变量间对变量的内存可见性,将最新变量值及时通知给其他线程(让旧值失效)
    • 禁止volatile前后的程序指令进行重排序
    • 不保证线程安全,不可用于数字的线程安全(比如a++原子性无法保证)
    • 使用场景:
      • 修饰状态变量,用于线程间访问该变量,保证各线程可以看到最新的内存值
      • 单实例对象构造:避免多线程下由于内存不可见导致重复构造对象


    9、创建线程的几种方式

     (1)通过Runnable接口创建

    • 重写Runnable的run方法
    • 使用runnable对象构造Thread对象
    • 启动线程

    (2)继承Thread类创建线程

    • 继承Thread类,重写run方法
    • 构造这个Thread子类
    • 调用start启动线程

    (3)使用Callable和FutureTask创建线程

    • 实现Callable接口,重写call方法
    • 传入Callable对象,构造FutureTask(Runnable子类)对象
    • 传入FutureTask对象构造Thread对象,启动线程

    (4)将Runnable或Callable放到线程池ExecutorService中执行

    • 实现Callable/Runnable接口,重写call/run方法
    • 构建ExecutorService线程池对象,调用线程池execute或者submit执行线程
    • 对于submit方式提交,使用Future来获取线程执行结果

    (5)start()源码


    10、ThreadLocal

     (1)ThreadLocal并不是一个新的Thread,而是Thread的局部变量。

     (2)java的四种引用关系


    11、线程池

    (1)

    • corePoolSize:核心线程数,保持在线程池中线程的数量
    • maxinumPoolSize:线程池中允许的最大线程数
    • keepAliveTime/timeUnit:线程池中线程空闲时不被释放的最长时间,配合timeUnit使用,为0则表示永远不被释放。
    • workQueue:BlockingQueue<Runnable>,工作线程任务的阻塞队列,用来存放等待执行的任务,默认实现:LinkedBlockingQueue
    • threadFactory:线程池创建工厂,子类通过自定义实现接口Thread newThread(Runnable r)
    • handler(RejectedExecutionHandler):无法添加新任务时的处理。默认直接抛出异常。
      • callerRunsPolicy:调用方执行
      • abortPolicy:直接抛出RejectedExecutionException异常
      • discardPolicy:直接忽略
      • discardOldestPolicy:抛弃最早任务。将workQueue中的一个任务抛弃,将当前任务放入workQueue中执行。2

     (2)线程池状态

    •  RUNNING:初始状态,运行中
    • SHUTDOWN:关闭状态,shutdown()后变为该状态,不再接收新的任务,处理已有的任务
    • STOP:停止状态,调用shutdownNow()后会从RUNNING进入这个状态。不在接收新的任务,并会将执行中的线程中断
    • TIDYING:整理状态,此时队列中任务数量已经是0
    • TERMINATED:终结状态。由TIDYING状态后调用terminated()后进入该状态

     

     

     

     

     

     

     

     

     

     

  • qq_39800927 2020-04-09 14:10:53

    来源:线程死锁和避免 查看详情

    1、避免死锁

    • 不使用锁,不使用2把及以上的锁
    • 必须使用2把及以上锁的时候,确保在整个应用程序中对获取锁的顺序是一致的
    • 尝试获取具有超时释放的锁时,例如Locak中的tryLock来获取锁
    • 当发生了java-level锁时,重启程序来干掉进程/线程
      package test;
      
      public class DemoLocak {
      
      	private static final Object lock1 = new Object();
      	private static final Object lock2 = new Object();
      
      	public static void main(String[] args) {
      		newThread("ThreadA", lock1, lock2).start();
      
      		newThread("ThreadB", lock2, lock1).start();
      //改成以下情况不会造成死锁,满足第2种情况
      //newThread("ThreadB", lock1, lock2).start();
      
      	}
      
      	private static Thread newThread(String threadName, Object lockFrist, Object lockSecond) {
      		return new Thread(() -> {
      			synchronized (lockFrist) {
      				System.out.println(Thread.currentThread().getName() + " holiding lockFrist!");
      				System.out.println(threadName + " holiding!");
      				try {
      					Thread.sleep(300L);
      				} catch (InterruptedException e) {
      					// TODO Auto-generated catch block
      					e.printStackTrace();
      				}
      				System.out.println(threadName + " waitting  lockFrist!");
      				synchronized (lockSecond) {
      					System.out.println(threadName + " holiding lockSecond!");
      				}
      			}
      		}, threadName);
      	}
      }
      

      2、定位死锁问题

    • jps 例举正在运行的虚拟机进程并显示虚拟机执行的主类以及这些进程的唯一ID(PID)

    • jstack  用于JVN当前时刻的线程快走,得到JVM当前每一条线程正在执行的堆栈信息,定位线程长时间卡顿问题,如死锁、死循环等问题
    • 运行程序,在terminal输入jps查看目前所有的线程,查看目前是否有阻塞的线程。第二,输入 jstack 进程id,查看当前进程详情的堆栈信息   jstack 进程id(阻塞进程id)

    3、处理数据库的死锁问题

    死锁情景:

    • 针对同一张表
    • 开启2个会话,每个会话调用 start tran
    • 第一个会话先更新id=1的行
    • 第二个会话先更新id=2的行
    • 第一个会话先更新id=2的行,此时等待id=2行的锁
    • 第二个会话更新id=1的行,此时数据库检测死锁,种植触发死锁的事务,第一个会话的等待状态变为正常状态
    • 调用“commit”提交第一个会话事务,数据成功

     

没有更多了