Java 多线程、Queue学习,CAS学习

主题一:Queue:

Java并发(10)- 简单聊聊JDK中的七大阻塞队列
解读 Java 并发队列 BlockingQueue
Java多线程总结之线程安全队列Queue
并发队列ConcurrentLinkedQueue和阻塞队列LinkedBlockingQueue用法
Java多线程总结之聊一聊Queue ---- 棒棒的!
Core Java 并发:理解并发概念

  • 并行和并发区别:

1、并行是指两者同时执行一件事,比如赛跑,两个人都在不停的往前跑;
2、并发是指资源有限的情况下,两者交替轮流使用资源,比如一段路(单核CPU资源)同时只能过一个人,A走一段后,让给B,B用完继续给A ,交替使用,目的是提高效率

在Java多线程应用中,队列的使用率很高,多数生产消费模型的首选数据结构就是队列(先进先出)。
Java提供的线程安全的Queue可以分为阻塞队列和非阻塞队列。

阻塞队列(同步队列)--典型BlockingQueue: -- 线程安全

  • 1. ArrayBlockQueue:
    一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。创建其对象必须明确大小,像数组一样。

  • 2、LinkedBlockingQueue:
    由于LinkedBlockingQueue实现是线程安全的,实现了先进先出等特性,是作为生产者消费者的首选,LinkedBlockingQueue 可以指定容量,也可以不指定,不指定的话,默认最大是Integer.MAX_VALUE,其中主要用到put和take方法,put方法在队列满的时候会阻塞直到有队列成员被消费,take方法在队列空的时候会阻塞,直到有队列成员被放进来。
    LinkedBlockingQueue是一个线程安全的阻塞队列,它实现了BlockingQueue接口,BlockingQueue接口继承自java.util.Queue接口,并在这个接口的基础上增加了take和put方法,这两个方法正是队列操作的阻塞版本。

  • 3. PriorityBlockingQueue:
    类似于LinkedBlockingQueue,但其所含对象的排序不是FIFO,而是依据对象的自然排序顺序或者是构造函数所带的Comparator决定的顺序。

  • 4. SynchronousQueue:
    同步队列。同步队列没有任何容量,每个插入必须等待另一个线程移除,反之亦然。

非阻塞队列(并发队列)--典型ConcurrentLinkedQueue: -- 线程安全

  • 1、ConcurrentLinkedQueue:
    ConcurrentLinkedQueue,它是一个无锁的并发线程安全的队列,是Queue的一个安全实现.Queue中元素按FIFO原则进行排序.采用CAS操作,来保证元素的一致性。

主题二:线程:

Java 多线程、Queue学习,CAS学习_第1张图片
实现Runnable接口比继承Thread类所具有的优势:
  • 适合多个相同的程序代码的线程去处理同一个资源
  • 可以避免java中的单继承的限制
  • 增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
    【Java并发编程】之六:Runnable和Thread实现多线程的区别(含代码)
线程里存在两个队列:
  • 阻塞队列:wait、sleep、join
  • 就绪队列:notify唤醒、wait时间过了、
线程的不安全体现:
  • 内存可见性
  • 操作原子性

Java并发编程:Thread类的使用
Synchronized 关键字使用、底层原理、JDK1.6 之后的底层优化以及 和ReenTrantLock 的对比
一份针对于新手的多线程实践
Java并发编程:同步容器
深入理解线程通信
java自带线程池和队列详细讲解

interrupt(线程中断):

调用Thread的interrupt结束线程:

其实调用Thread对象的interrupt函数并不是立即中断线程,只是将线程中断状态标志设置为true,当线程运行中有调用其阻塞的函数(Thread.sleep,Object.wait,Thread.join等)时,阻塞函数调用之后,会不断地轮询检测中断状态标志是否为true,如果为true,则停止阻塞并抛出InterruptedException异常,同时还会重置中断状态标志;如果为false,则继续阻塞,直到阻塞正常结束。

interrupt()方法有两个作用:

  • 一个是将线程的中断状态置位(中断状态由false变成true);
  • 另一个则是让被中断的线程抛出InterruptedException异常。

这是很重要的。这样,对于那些阻塞方法(比如 wait() 和 sleep())而言,当另一个线程调用interrupt()中断该线程时,该线程会从阻塞状态退出并且抛出中断异常。这样,我们就可以捕捉到中断异常,并根据实际情况对该线程从阻塞方法中异常退出而进行一些处理。

比如说:线程A获得了锁进入了同步代码块中,但由于条件不足调用 wait() 方法阻塞了。这个时候,线程B执行 threadA.interrupt()请求中断线程A,此时线程A就会抛出InterruptedException,我们就可以在catch中捕获到这个异常并进行相应处理(比如进一步往上抛出)

关于Thread的静态函数interrupted与Thread的对象函数isInterrupted:

  • 2函数都是调用了Native函数private native boolean isInterrupted(boolean ClearInterrupted);
  • 前者调用传的参数为true,所以,调用interrupted函数,会在检测线程中断状态标志是否为true后,还会将中断状态标志重置为false。
  • 而isInterrupted函数只是检测线程中断状态标志。

如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法过程中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException。 我们可以捕获该异常,并且做一些处理。
另外,Thread.interrupted()方法是一个静态方法,它是判断当前线程的中断状态,需要注意的是,线程的中断状态会由该方法清除。换句话说,如果连续两次调用该方法,则第二次调用将返回 false(在第一次调用已清除了其中断状态之后,且第二次调用检验完中断状态前,当前线程再次中断的情况除外)。

JAVA多线程之中断机制(如何处理中断?)

Sleep(线程睡眠):

Wait:

Join(线程合并):

线程合并是优先执行调用该方法的线程,再执行当前线程
所谓合并,就是等待其它线程执行完,再执行当前线程,执行起来的效果就好像把其它线程合并到当前线程执行一样。

Yield(线程让步):

线程让步用于正在执行的线程,在某些情况下让出CPU资源,让给其它线程执行,来看一个小例子:


主题三:多线程:

Executor:

Future、Callable、FutureTask:

  • Callable和Future,它俩很有意思的,一个产生结果,一个拿到结果。
  • Callable接口类似于Runnable,从名字就可以看出来了,但是Runnable不会返回结果,并且无法抛出返回结果的异常,
  • 而Callable功能更强大一些,被线程执行后,可以返回值,这个返回值可以被Future拿到,也就是说,Future可以拿到异步执行任务的返回值
  • FutureTask实现了两个接口,Runnable和Future,所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值,那么这个组合的使用有什么好处呢?假设有一个很耗时的返回值需要计算,并且这个返回值不是立刻需要的话,那么就可以使用这个组合,用另一个线程去计算返回值,而当前线程在使用这个返回值之前可以做其它的操作,等到需要这个返回值时,再通过Future得到。

Java进阶之FutureTask的用法及解析

Synchronized:

既保证了多线程的并发有序性,又保证了多线程的内存可见性

synchronized 关键字原理
Java并发编程:synchronized

atomic(原子类包):

java.util.concurrent.atomic,该包是对Java部分数据类型的原子封装,在原有> 数据类型的基础上,提供了原子性的操作方法,保证了线程安全
虽然使用CAS可以实现非阻塞式的原子性操作,但是会产生ABA问题

  • Compare and Swap, 翻译成比较并交换。
  • CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
  • java应用:
    java.util.concurrent包中借助CAS实现了区别于synchronouse同步锁的一种乐观锁。
    java.util.concurrent包完全建立在CAS之上的,没有CAS就不会有此包。可见CAS的重要性。

CAS原理分析
java的atomic包使用

Volatile:

volatile可以保证内存可见性,不能保证并发有序性,既保证不了执行的原子性

你应该知道的 volatile 关键字
Java并发编程:volatile关键字解析

ReentrantLock、Condition:

  • 线程互斥: 实现提供了比使用synchronized 方法和语句可获得的更广泛的锁定操作,它能以更优雅的方式处理线程同步问题。
  • 线程通信:
  • Condition将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set (wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用
  • Condition是被绑定到Lock上的,要创建一个Lock的Condition必须用newCondition()方法。
  • Condition的强大之处在于它可以为多个线程间建立不同的Condition

ReentrantLock 实现原理
Java并发编程:Lock
Java线程(篇外篇):线程和锁
Java并发编程:CountDownLatch、CyclicBarrier和Semaphore ---棒棒的

ThreadLocal:

Java并发编程:深入剖析ThreadLocal
ThreadLocal就是这么简单

ThreadLocal内存泄漏的根源是:

  • 由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key就会导致内存泄漏,而不是因为弱引用。

  • 想要避免内存泄露就要手动remove()掉!

线程池:

  • FixedThreadPool:
    创建一个可重用固定线程集合的线程池,以共享的无界队列方式来运行这些线程。
    在FixedThreadPool中,有一个固定大小的池,如果当前需要执行的任务超过了池大小,那么多于的任务等待状态,直到有空闲下来的线程执行任务,而当执行的任务小于池大小,空闲的线程也不会去销毁。
    ExecutorService threadPool = Executors.newFixedThreadPool(3);// 创建可以容纳3个线程的线程池
  • CachedThreadPool:
    创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。
    CachedThreadPool会创建一个缓存区,将初始化的线程缓存起来,如果线程有可用的,就使用之前创建好的线程,如果没有可用的,就新创建线程,终止并且从缓存中移除已有60秒未被使用的线程。
    ExecutorService threadPool = Executors.newCachedThreadPool();// 线程池的大小会根据执行的任务数动态分配
  • SingleThreadExecutor:
    创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。
    SingleThreadExecutor得到的是一个单个的线程,这个线程会保证你的任务执行完成,如果当前线程意外终止,会创建一个新线程继续执行任务,这和我们直接创建线程不同,也和newFixedThreadPool(1)不同。
    ExecutorService threadPool = Executors.newSingleThreadExecutor();// 创建单个线程的线程池,如果当前线程在执行任务时突然中断,则会创建一个新的线程替代它继续执行任务
  • ScheduledThreadPool:
    创建一个可安排在给定延迟后运行命令或者定期地执行的线程池。
    ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(3);// 效果类似于Timer定时器

你可能感兴趣的:(Java 多线程、Queue学习,CAS学习)