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

2018-09-17  本文已影响0人  John13

主题一: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: -- 线程安全

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


主题二:线程:

实现Runnable接口比继承Thread类所具有的优势:
线程里存在两个队列:
线程的不安全体现:

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和Lock比较:

synchronized四种锁状态的升级

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问题。如果当前引用 == 预期引用,并且当前标志等于预期标志,则以原子方式将该引用和该标志的值设置为给定的更新值。

自旋锁存在的问题:

自旋锁的优点:

自旋锁与互斥锁:

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

ThreadLocal:

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

ThreadLocal内存泄漏的根源是:

线程池:

  • 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其实和锁有点类似,它一般用于控制对某组资源的访问权限。

上一篇 下一篇

猜你喜欢

热点阅读