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操作,来保证元素的一致性。

主题二:线程:

实现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资源,让给其它线程执行,来看一个小例子:


主题三:多线程:

Future、Callable、FutureTask:

Java并发编程:Callable、Future和FutureTask

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

Java进阶之FutureTask的用法及解析

Synchronized:

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

  • synchronized底层的原理是跟jvm指令和monitor有关系的。

  • synchronized一般是对对象加锁,对类加锁也就是对类对象加锁。如果使用了synchronized关键字,在底层编译后的jvm指令中,会有monitorenter和monitorexit两个指令。线程进入synchronized代码片段,执行monitorenter指令对monitor计数器加1,这样其他线程发现monitor的计数器不为0,就阻塞等待:


  • 线程出synchronized代码片段,执行monitorexit指令就是对monitor计数器减1,这样其他线程发现monitor的计数器为0,就可以拿到锁,给monitor的计数器加1,然后执行业务逻辑了:


  • 上面的是针对synchronized对对象、类加锁的底层原理。方法加锁不是通过monitor指令,而是通过ACC_SYNCHORNIZED关键字,判断方法是否同步。

Synchronized和Lock比较:

synchronized四种锁状态的升级

  • 锁可以升级但不能降级:无锁状态->偏向锁状态->轻量级锁状态->重量级锁状态
  • 偏向锁通过对比 Mark Word 解决加锁问题,避免执行CAS操作。
  • 轻量级锁是通过用 CAS 操作和自旋来解决加锁问题,避免线程阻塞和唤醒而影响性能。
  • 重量级锁是将除了拥有锁的线程以外的线程都阻塞。

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的重要性。
  • ABA问题解决:
    JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。如果当前引用 == 预期引用,并且当前标志等于预期标志,则以原子方式将该引用和该标志的值设置为给定的更新值。

自旋锁存在的问题:

  • 如果某个线程持有锁的时间过长,就会导致其它等待获取锁的线程进入循环等待,消耗CPU。使用不当会造成CPU使用率极高。
  • 上面Java实现的自旋锁不是公平的,即无法满足等待时间最长的线程优先获取锁。不公平的锁就会存在“线程饥饿”问题。

自旋锁的优点:

  • 自旋锁不会使线程状态发生切换,一直处于用户态,即线程一直都是active的;不会使线程进入阻塞状态,减少了不必要的上下文切换,执行速度快
  • 非自旋锁在获取不到锁的时候会进入阻塞状态,从而进入内核态,当获取到锁的时候需要从内核态恢复,需要线程上下文切换。 (线程被阻塞后便进入内核(Linux)调度状态,这个会导致系统在用户态与内核态之间来回切换,严重影响锁的性能)

自旋锁与互斥锁:

  • 自旋锁与互斥锁都是为了实现保护资源共享的机制。
  • 无论是自旋锁还是互斥锁,在任意时刻,都最多只能有一个保持者。
  • 获取互斥锁的线程,如果锁已经被占用,则该线程将进入睡眠状态;获取自旋锁的线程则不会睡眠,而是一直循环等待锁释放。

CAS原理分析
java的atomic包使用

Volatile:

  • volatile可以保证内存可见性,不能保证并发有序性,既保证不了执行的原子性
  • 防止指令重排

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

相比于synchroinized来说,volatile要轻量很多,执行的成本会更低。原因是volatile不会引起线程上下文的切换和调度,但是它与synchronized的意义其实是有区别的。synchronized关键字主要体现的是互斥性。

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线程(篇外篇):线程和锁

  • AbstractQueuedSynchronized(AQS):抽象的队列式的同步器

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定时器

CountDownLatch、CyclicBarrier和Semaphore

Java并发编程:CountDownLatch、CyclicBarrier和Semaphore

1、CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同:
  1)、CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;
  2)、而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时往下执行;
  另外,CountDownLatch是不能够重用的,而CyclicBarrier是可以重用的。
2、Semaphore其实和锁有点类似,它一般用于控制对某组资源的访问权限。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,752评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,100评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,244评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,099评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,210评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,307评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,346评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,133评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,546评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,849评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,019评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,702评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,331评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,030评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,260评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,871评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,898评论 2 351

推荐阅读更多精彩内容

  • 本文是我自己在秋招复习时的读书笔记,整理的知识点,也是为了防止忘记,尊重劳动成果,转载注明出处哦!如果你也喜欢,那...
    波波波先森阅读 11,247评论 4 56
  • 本文首发于我的个人博客:尾尾部落 本文是我刷了几十篇一线互联网校招java后端开发岗位的面经后总结的多线程相关题目...
    繁著阅读 2,001评论 0 7
  • ​01 当某一种声音一旦被传播流行开来之后,容易走极端,“做自己”就是其中之一。 我耳边经常听到有些人说做自己,或...
    进击的历史君阅读 353评论 0 1
  • 一 念青难过的时候,会一个人爬到屋顶上,看着天上的星星,眼泪也就不会流下来了。那年她十岁,妈妈在病床上对她说,青青...
    柳白歌阅读 430评论 0 2
  • 文 / 栩栩三千 1. 冬日。 午后。 黄浦江畔。 虽说是冬天,可温度却一下子上升了五度,仿佛回到了秋天。 沈珂是...
    栩栩三千阅读 396评论 2 1