精华内容
下载资源
问答
  • JAVA并发专题讲解

    2019-02-01 04:50:24
    从事java开发的朋友们,一定对并发不陌生,可是很多人一接触到并发就头大甚至对并发有些畏惧之心,不用害怕只要我们抱着一颗对知识好奇的心去迎接未知的技术,相信你一定可以,接下来我给大家讲述一下并发那些事。...

         从事java开发的朋友们,一定对并发不陌生,可是很多人一接触到并发就头大甚至对并发有些畏惧之心,不用害怕只要我们抱着一颗对知识好奇的心去迎接未知的技术,相信你一定可以,接下来我给大家讲述一下并发那些事。

         其实每一门技术的诞生都是为了解决我们生活中的某些应用场景,我们日常生活中随处可见并发的例子,例如12306购票系统,多个人同时购票会不会座位号相同呢?我们带着这样的问题开始学习并发编程。
    一、并发概念

    二、线程机制

    三、共享受限资源
     

    展开全文
  • Java大牛Doug Lea对 synchronized 在并发编程条件下的性能表现不满意就自己写了个JUC,以此来提升并发性能,本文要讲的就是JUC并发包下的AbstractQueuedSynchronizer。在JUC中 CountDownLatch、ReentrantLock、Thre....

    1、JUC的由来

    synchronized 关键字是JDK官方人员用C++代码写的,在JDK6以前是重量级锁。Java大牛Doug Lea对 synchronized 在并发编程条件下的性能表现不满意就自己写了个JUC,以此来提升并发性能,本文要讲的就是JUC并发包下的AbstractQueuedSynchronizer

    在JUC中 CountDownLatch、ReentrantLock、ThreadPoolExecutor、ReentrantReadWriteLock 等底层用的都是AQS,AQS几乎占据了JUC并发包里的半壁江山,如果想要获取锁可以被中断、超时获取锁、尝试获取锁那就用AQS吧

    DougLea杰作: HashMap、JUC、ConcurrentHashMap等。

    温馨提醒

    涉及到AQS重要方法、lock、unlock、CountDownLatch、await、signal几个重要组件的底层讲解所以内容有点长,嫌啰嗦的可直接看几个流程图即可(原图公众号回复lock即可)。

    2、AQS前置知识点

    2.1、模板方法

    AbstractQueuedSynchronizer是个抽象类,所有用到方法的类都要继承此类的若干方法,对应的设计模式就是模版模式

    模版模式定义:一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。

    抽象类:

    public abstract class SendCustom { public abstract void to(); public abstract void from(); public void date() {  System.out.println(new Date()); } public abstract void send(); // 注意此处 框架方法-模板方法 public void sendMessage() {  to();  from();  date();  send(); }}

    模板方法派生类:

    public class SendSms extends SendCustom { @Override public void to() {  System.out.println("sowhat"); } @Override public void from() {  System.out.println("xiaomai"); } @Override public void send() {  System.out.println("Send message"); }  public static void main(String[] args) {  SendCustom sendC = new SendSms();  sendC.sendMessage(); }}

    2.2、LookSupport

    LockSupport 是一个线程阻塞工具类,所有的方法都是静态方法,可以让线程在任意位置阻塞,当然阻塞之后肯定得有唤醒的方法。常用方法如下:

    public static void park(Object blocker); // 暂停当前线程public static void parkNanos(Object blocker, long nanos); // 暂停当前线程,不过有超时时间的限制public static void parkUntil(Object blocker, long deadline); // 暂停当前线程,直到某个时间public static void park(); // 无期限暂停当前线程public static void parkNanos(long nanos); // 暂停当前线程,不过有超时时间的限制public static void parkUntil(long deadline); // 暂停当前线程,直到某个时间public static void unpark(Thread thread); // 恢复当前线程public static Object getBlocker(Thread t);

    park是因为park英文意思为停车。我们如果把Thread看成一辆车的话,park就是让车停下,unpark就是让车启动然后跑起来。

    与Object类的wait/notify机制相比,park/unpark有两个优点:

    thread为操作对象更符合阻塞线程的直观定义

    操作更精准,可以准确地唤醒某一个线程(notify随机唤醒一个线程,notifyAll 唤醒所有等待的线程),增加了灵活性。

    park/unpark调用的是 Unsafe(提供CAS操作) 中的 native代码。

    park/unpark 功能在Linux系统下是用的Posix线程库pthread中的mutex(互斥量),condition(条件变量)来实现的。mutexcondition保护了一个 _counter 的变量,当 park 时,这个变量被设置为0。当unpark时,这个变量被设置为1。

    2.3、CAS

    CAS 是 CPU指令级别实现了原子性的比较和交换(Conmpare And Swap)操作,注意CAS不是锁只是CPU提供的一个原子性操作指令。

    8665ee3f65d072d47e9dd751fad772a6.png

    CAS在语言层面不进行任何处理,直接将原则操作实现在硬件级别实现,之所以可以实现硬件级别的操作核心是因为CAS操作类中有个核心类UnSafe类。

    关于CAS引发的ABA问题、性能开销问题、只能保证一个共享变量之间的原则性操作问题,以前CAS中写过,在此不再重复讲解。

    注意:并不是说 CAS 一定比SYN好,如果高并发执行时间久 ,用SYN好, 因为SYN底层用了wait() 阻塞后是不消耗CPU资源的。如果锁竞争不激烈说明自旋不严重,此时用CAS。

    3、AQS重要方法

    模版方法分为独占式共享式,子类根据需要不同调用不同的模版方法(讲解有点多,想看底层可直接下滑到第四章节)。

    3.1 模板方法

    3.1.1 独占式获取

    3.1.1.1 accquire

    不可中断获取锁accquire是获取独占锁方法,acquire尝试获取资源,成功则直接返回,不成功则进入等待队列,这个过程不会被线程中断,被外部中断也不响应,获取资源后才再进行自我中断selfInterrupt()

    public final void acquire(int arg) {        if (!tryAcquire(arg) &&            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))            selfInterrupt();}
    1. acquire(arg) tryAcquire(arg) 顾名思义,它就是尝试获取锁,需要我们自己实现具体细节,一般要求是:

    如果该锁没有被另一个线程保持,则获取该锁并立即返回,将锁的保持计数设置为 1。

    如果当前线程已经保持该锁,则将保持计数加 1,并且该方法立即返回。

    如果该锁被另一个线程保持,则出于线程调度的目的,禁用当前线程,并且在获得锁之前,该线程将一直处于休眠状态,此时锁保持计数被设置为 1。

    1. addWaiter(Node.EXCLUSIVE)

    主要功能是 一旦尝试获取锁未成功,就要使用该方法将其加入同步队列尾部,由于可能有多个线程并发加入队尾产生竞争,因此采用compareAndSetTail锁方法来保证同步

    1. acquireQueued(addWaiter(Node.EXCLUSIVE), arg)

    一旦加入同步队列,就需要使用该方法,自旋阻塞 唤醒来不断的尝试获取锁,直到被中断或获取到锁。

    3.1.1.2 acquireInterruptibly

    可中断获取锁acquireInterruptibly相比于acquire支持响应中断。

    1、如果当前线程未被中断,则尝试获取锁。

    2、如果锁空闲则获锁并立即返回,state = 1。

    3、如果当前线程已持此锁,state + 1,并且该方法立即返回。

    4、如果锁被另一个线程保持,出于线程调度目的,禁用当前线程,线程休眠ing,除非锁由当前线程获得或者当前线程被中断了,中断后会抛出InterruptedException,并且清除当前线程的已中断状态。

    5、此方法是一个显式中断点,所以要优先考虑响应中断。

     if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())     throw new InterruptedException(); // acquireInterruptibly 选择      interrupted = true; // acquire 的选择

    3.1.1.3 tryAcquireNanos

    该方法可以被中断,增加了超时则失败的功能。可以说该方法的实现与上述两方法没有任何区别。时间功能上就是用的标准超时功能,如果剩余时间小于0那么acquire失败,如果该时间大于一次自旋锁时间(spinForTimeoutThreshold = 1000),并且可以被阻塞,那么调用LockSupport.parkNanos方法阻塞线程。

    doAcquireNanos内部:

      nanosTimeout = deadline - System.nanoTime();  if (nanosTimeout <= 0L)      return false;  if (shouldParkAfterFailedAcquire(p, node) && nanosTimeout > spinForTimeoutThreshold)      LockSupport.parkNanos(this, nanosTimeout);  if (Thread.interrupted())      throw new InterruptedException();

    该方法一般会有以下几种情况产生:

    在指定时间内,线程获取到锁,返回true。

    当前线程在超时时间内被中断,抛中断异常后,线程退出。

    到截止时间后线程仍未获取到锁,此时线程获得锁失败,不再等待直接返回false。

    3.1.2 共享式获取

    3.1.2.1 acquireShared

        public final void acquireShared(int arg) {        if (tryAcquireShared(arg) 

    该模版方法的工作:

    1. 调用tryAcquireShared(arg) 尝试获得资源,返回值代表如下含义:

    负数表示失败。

    0 表示成功,但没有剩余可用资源。

    正数表示成功,且有剩余资源。

    doAcquireShared作用:

    创建节点然后加入到队列中去,这一块和独占模式下的 addWaiter 代码差不多,不同的是结点的模式是SHARED,在独占模式 EXCLUSIVE。

    3.1.2.2 acquireSharedInterruptibly

    无非就是可中断性的共享方法

    public final void acquireSharedInterruptibly(long arg)  throws InterruptedException {    if (Thread.interrupted()) // 如果线程被中断,则抛出异常        throw new InterruptedException();    if (tryAcquireShared(arg) 

    3.1.2.3. tryAcquireSharedNanos

    尝试以共享模式获取,如果被中断则中止,如果超过给定超时期则失败。实现此方法首先要检查中断状态,然后至少调用一次 tryacquireshared(long),并在成功时返回。否则,在成功、线程中断或超过超时期之前,线程将加入队列,可能反复处于阻塞或未阻塞状态,并一直调用 tryacquireshared(long)

        public final boolean tryAcquireSharedNanos(long arg, long nanosTimeout)            throws InterruptedException {        if (Thread.interrupted())            throw new InterruptedException();        return tryAcquireShared(arg) >= 0 ||            doAcquireSharedNanos(arg, nanosTimeout);    }

    3.1.3 独占式释放

    独占锁的释放调用unlock方法,而该方法实际调用了AQS的release方法,这段代码逻辑比较简单,如果同步状态释放成功(tryRelease返回true)则会执行if块中的代码,当head指向的头结点不为null,并且该节点的状态值不为0的话才会执行unparkSuccessor()方法。

        public final boolean release(long arg) {        if (tryRelease(arg)) {            Node h = head;            if (h != null && h.waitStatus != 0)                unparkSuccessor(h);            return true;        }        return false;    }

    3.1.4 共享式释放

    releaseShared首先去尝试释放资源tryReleaseShared(arg),如果释放成功了,就代表有资源空闲出来,那么就用doReleaseShared()去唤醒后续结点。

    public final boolean releaseShared(int arg) {    if (tryReleaseShared(arg)) {        doReleaseShared();        return true;    }    return false;}

    比如CountDownLatch的countDown()具体实现:

        public void countDown() {        sync.releaseShared(1);    }

    3.2 子类需实现方法

    子类要实现父类方法也分为独占式共享式

    3.2.1 独占式获取

    tryAcquire 顾名思义,就是尝试获取锁,AQS在这里没有对其进行功能的实现,只有一个抛出异常的语句,我们需要自己对其进行实现,可以对其重写实现公平锁、不公平锁、可重入锁、不可重入锁

    protected boolean tryAcquire(int arg) {        throw new UnsupportedOperationException();}

    3.2.2 独占式释放

    tryRelease 尝试释放 独占锁,需要子类实现。

       protected boolean tryRelease(long arg) {        throw new UnsupportedOperationException();    }

    3.2.3 共享式获取

    tryAcquireShared 尝试进行共享锁的获得,需要子类实现。

    protected long tryAcquireShared(long arg) {        throw new UnsupportedOperationException();    }

    3.2.4 共享式释放

    tryReleaseShared尝试进行共享锁的释放,需要子类实现。

        protected boolean tryReleaseShared(long arg) {        throw new UnsupportedOperationException();    }

    3.3 状态标志位

    state因为用 volatile修饰 保证了我们操作的可见性,所以任何线程通过getState()获得状态都是可以得到最新值,但是setState()无法保证原子性,因此AQS给我们提供了compareAndSetState方法利用底层UnSafe的CAS功能来实现原子性。

        private volatile long state;    protected final long getState() {        return state;    }    protected final void setState(long newState) {        state = newState;    }   protected final boolean compareAndSetState(long expect, long update) {        return unsafe.compareAndSwapLong(this, stateOffset, expect, update);    }

    3.4 查询是否独占模式

    isHeldExclusively 该函数的功能是查询当前的工作模式是否是独占模式。需要子类实现。

        protected boolean isHeldExclusively() {        throw new UnsupportedOperationException();    }

    3.5 自定义实现锁

    这里需要重点说明一点,JUC中一般是用一个子类继承自Lock,然后在子类中定义一个内部类来实现AQS的继承跟使用

    public class SowhatLock implements Lock{ private Sync sync = new Sync(); @Override public void lock() {  sync.acquire(1); } @Override public boolean tryLock() {  return false; } @Override public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {  return sync.tryAcquireNanos(1,unit.toNanos(time)); } @Override public void unlock() {  sync.release(1); } @Override public Condition newCondition() {  return sync.newCondition(); } @Override public void lockInterruptibly() throws InterruptedException { } private class Sync extends AbstractQueuedSynchronizer {  @Override  protected boolean tryAcquire(int arg)  {   assert arg == 1;   if (compareAndSetState(0, 1))   {    setExclusiveOwnerThread(Thread.currentThread());    return true;   }   return false;  }  @Override  protected boolean tryRelease(int arg)  {   assert arg == 1;   if (!isHeldExclusively())   {    throw new IllegalMonitorStateException();   }   setExclusiveOwnerThread(null);   setState(0);   return true;  }  @Override  protected boolean isHeldExclusively()  {   return getExclusiveOwnerThread() == Thread.currentThread();  }  Condition newCondition() {   return new ConditionObject();  } }}

    自定义实现类:

    public class SoWhatTest{ public static int m = 0; public  static CountDownLatch latch  = new CountDownLatch(50); public static Lock lock = new SowhatLock(); public static void main(String[] args) throws  Exception {  Thread[] threads = new Thread[50];  for (int i = 0; i {    try{     lock.lock();     for (int j = 0; j <100 ; j++)     {      m++;     }    }finally    {     lock.unlock();    }    latch.countDown();  });  }  for(Thread t : threads) t.start();  latch.await();  System.out.println(m); }}

    篇幅限制,下文请见:由浅入深逐步讲解Java并发的半壁江山—AQS(下文)

    展开全文
  • 而对于Java并发来说,与超时相关的内容主要是线程等待超时和获取锁超时,比如调用Object.wait(long)就会使线程进入等待状并在指定时间后等待超时。  此篇主要讲解Java内置锁的获取操作的超时机制。当大量线程...

    当我们在使用Java进行网络编程时经常会遇到很多超时的概念,比如一个浏览器请求过程就可能会产生很多超时的地方,当我们在浏览器发起一个请求后,网络socket读写可能会超时,web服务器响应可能会超时,数据库查询可能会超时。而对于Java并发来说,与超时相关的内容主要是线程等待超时和获取锁超时,比如调用Object.wait(long)就会使线程进入等待状并在指定时间后等待超时。

    

    此篇主要讲解Java内置锁的获取操作的超时机制。当大量线程对某一锁竞争时可能导致某些线程在很长一段时间都获取不了锁,在某些场景下可能希望如果线程在一段时间内不能成功获取锁就取消对该锁的等待以提高性能,这时就需要用到超时机制。

    

     Synchronized 不支持超时

    我们先看Java从语法层提供的并发锁——synchronized关键词,synchronized对我们来说是相当熟悉的了,它是Java内置的锁方案。在Java的世界,每个对象都关联着一个内置锁,当线程要访问被synchronized修饰的对象时都必须先获得其对应的锁才能继续访问,否则将一直等待直到该锁被其它线程所释放。普通对象和对象的方法都关联有对应的内置锁,所以它们都可以被synchronized修饰。

    虽然synchronized使用很方便,但其存在一个缺点,那就是锁获取操作不支持超时机制。在并发的情况下,多个线程会去竞争被synchronized所修饰对应的锁对象,可能存在某个线程一直获取不到锁而一直处于阻塞等待状态。而这个处于阻塞状态的线程唯一能做的就是一直等待,我们没有办法设置一个等待超时时间。以下面的代码为例,线程一会先成功获取锁,在输出“Thread1 gets the lock”后进入睡眠,睡眠的时间很长。线程二较晚启动,它尝试获取锁,但该锁已被线程一所持有,所以线程一将永远获取不到锁而一直等待。

     

     AQS 同步器超时机制

    在JDK1.5之前还没有JUC工具,当时的并发控制只能通过上述的synchronized关键词实现锁,但它对超时取消的控制力不从心。JDK1.5开始引入的JUC工具则完美地解决了此问题,主要是因为AQS同步器提供了锁获取超时的支持。我们知道AQS同步器使用了队列的结构来处理等待的线程,AQS获取锁的超时机制大致如下图所示。首先多个线程竞争锁,因为锁已被其它线程持有,所以通过自旋的CAS操作将各自线程添加到队尾。其次是在线程添加到队列后,每个线程节点都各自轮询前一节点看是否轮到自己获取锁。假如这里线程2设置了超时机制,且线程2在超时时间内都获取不到锁,则该线程对应的节点将被取消。最终线程2因为获取锁超时而被取消。

    超时实现逻辑

    为了更精确地保证时间间隔的准确性,实现时使用了更为精确的System.nanoTime()方法,它能精确到纳秒级别。总体而言,超时机制的思想就是先计算deadline时间,然后在不断进行锁检查操作中计算是否已经到deadline时间,如果已到deadline时间则取消队列中的该节点并跳出循环。

    AQS的超时控制有两点必须要注意:

    • 一是超时时间包括了竞争入队的时间,如果竞  争入队就把超时时间消耗完的话则直接当作超时处理;

    • 另一个是关于spinForTimeoutThreshold变量阀值,它是决定使用自旋方式消耗时间还是使用系统阻塞方式消耗时间的分割线。

    JUC工具包作者通过测试将默认值设置为1000ns,即如果在成功插入等待队列后剩余时间大于1000ns则调用系统底层阻塞。否则不调用系统底层阻塞,取而代之的是仅仅让其在Java层不断循环消耗时间,这属于性能优化的措施。

    

    总结

    Java内置的synchronized关键词虽然提供了并发锁功能,但它却存在不支持超时的缺点。而AQS同步器则在获取锁的过程中提供了超时机制,同时我们深入分析了AQS获取锁超时的具体实现原理。获取锁超时的支持让Java在并发方面提供了更完善的机制,能满足开发者更多的并发策略需求。

    展开全文
  • 主要介绍了java并发编程线程的基础知识,文中讲解非常详细,帮助大家更好的学习JAVA并发编程,感兴趣想学习JAVA的可以了解下
  • Java并发编程(一) - 基础知识 之前看《Thinking In Java》时,并发讲解的挺多的,自己算是初步了解了并发。但是其讲解的不深入,自己感觉其讲解的不够好。后来自己想再学一学并发,买了《Java并发编程实战》,看了...

    Java并发编程(一) - 基础知识

    之前看《Thinking In Java》时,并发讲解的挺多的,自己算是初步了解了并发。但是其讲解的不深入,自己感觉其讲解的不够好。后来自己想再学一学并发,买了《Java并发编程实战》,看了一下讲的好基础、好多的理论,而且自我感觉讲的逻辑性不强。最后,买了本《Java并发编程的艺术》看,这本书挺好的,逻辑性非常强。

    1. 概述


    本篇文章主要内容来自《Java并发编程的艺术》,其讲解的比较深入,自己也有许多不懂的地方,然后自己主要把它讲解的提炼出来。本章是Java并发编程的基础知识,了解后能够对Java并发有一些基本的了解,其中许多深入计算机系统层面的知识我都滤过。想要深入而具体的了解,请见《Java并发编程的艺术》。

    2. 基本知识点


    内存可见性:当一个线程修改一个共享变量(存放在堆中:如实例域、静态域、数组等)时,另一个线程总能读到修改后的值。

    原子性(原子操作):不可以被中断的一个或一系列操作。

    重排序:在执行程序时,为了提高性能,编译器和处理器常常对指令做重排序。

    3. Java内存模型(JMM)


    3.1 Java的并发编程采用共享内存模型
    通信(指线程之间以何种机制交换信息):线程之间共享程序的公共状态,通过写-读内存中的公共状态进行隐式通信。

    同步(指程序中用于控制不同线程间操作发生相对顺序的机制):程序员必须显示的指定某个方法或某段代码需要在线程之间互斥执行。

    3.2 JMM定义了线程与主内存之间的抽象关系
    线程之间的共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory),本地内存存储了该线程以读/写共享变量的副本。所以,JMM通过控制主内存与每个线程的本地内容之间的交互,来为Java程序提供内存可见性。
    (注:本地内存是一个抽象概念,并不真实存在,它包含缓存、写缓冲区、寄存器以及其他的硬件和编译器优化。)

    3.3 JMM不保证对64位的long类型与double类型的写操作具有原子性,但读操作具有原子性
    原因:JMM对64位的写操作会拆分为两个32位的写操作来执行。

    4. happens-before


    4.1 概念:Java中阐述操作之间的内存可见性
    在JMM中,如果一个操作的结果需要对另一个操作可见,那么这两个操作之间存在happens-before关系。注:happens-before仅仅要对前一个操作(执行的结果)对后一操作可见,并不意味着操作顺序。

    4.2 happens-before的规则:

    • 程序顺序规则:一个线程中的每个操作,happens-before于该线程中的任意后续操作。注:JMM中只要求对该线程的结果不会变化,所以只要最后结果没有变化,JMM容许操作系统的重排序。
    • 监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁。
    • volatile变量规则:对一个volatile域的写入,happens-before于任意后续对这个volatile域的读。
    • happens-before具有传递性。
    • 线程启动规则:Thread对象的start()方法happen—before此线程的每一个动作。
    • 线程终止规则:线程的所有操作都happen—before对此线程的终止检测,可以通过Thread.join()方法结束、Thread.isAlive()的返回值等手段检测到线程已经终止执行。
    • 线程中断规则:对线程interrupt()方法的调用happen—before发生于被中断线程的代码检测到中断时事件的发生。
    • 对象终结规则:一个对象的初始化完成(构造函数执行结束)happen—before它的finalize()方法的开始。

    5. volatile


    5.1 volatile特性

    • 内存可见性:对于一个volatile变量的读,总能看到(任何线程)对这个volatile变量最后的写入。
    • 原子性:对于任意单个volatile变量的读/写具有原子性(例如volatile=*),但类似于volatile++这种复合操作不具有原子性。
    • volatile变量可以多线程的读,但只能单线程写。
    • volatile变量的写操作优先于读操作。

    5.2 volatile写-读建立的happens-before关系
    volatile变量的写-读可以实现线程之间的通信(隐式)。
    如下代码:

    class VolatileExample {
        private int a = 0;
        private volatile boolean flag = false;
    
        void write() {
            this.a = 1;		// 1
            this.flag = true;	// 2
        }
    
        void read() {
            while (flag) {		// 3
                int i = a;		// 4
                ...
            }
        }
    }
    

    如果线程A执行write()方法后,线程B执行read()方法。根据happens-before规则:B线程总能够进入循环,并且获得的a的值为1。
    程序顺序规则:1 happens-before 2;3 happens-before 4
    volatile变量规则:2 happens-before 3
    传递规则:1 happens-before 4

    5.2 volatile写-读的内存意义

    • 写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中,并通知接下来要读取这个volatile变量的某个线程发出了(其对共享变量所做修改的)消息。
    • 读一个volatile变量,实质上该线程接受到之前某个线程发出的修改消息,JMM会把该线程对应的本地内存置为无效,线程接下来会从主内存中读取共享变量。

    6. CAS


    6.1 CAS定义
    CAS:Compare And Swap(比较与交换),它会比较预期值与当前内存值是否相等,如果相等则把内存值变成期望值,否则不变。

    6.2 CAS原理
    Java中的CAS是通过JNI(Java Native Interface)调用本地方法实现的。本地调用在sun.misc.Unsafe类中的compareAndSwap的一系列方法,该方法通过使用本地方法来实现CAS的原子性。所以Java中CAS具有volatile读写的内存意义。

    6.3 CAS应用
    Java中有许多并发都是通过CAS操作来实现的,如Aomic类。Atomic类通过CAS来实现原子性,volatile来实现内存可见性。具体如下:

    public class AtomicInteger extends Number implements java.io.Serializable {
        private volatile int value;
        
        public final int get() {
            return value;
        }
        
        public final void set(int newValue) {
            value = newValue;
        }
    
        public final int getAndIncrement() {
            return unsafe.getAndAddInt(this, valueOffset, 1);
        }
    }
    

    从上面截图的一部分AtomicInteger的实现,我们可以看出由于volatile对于volatile++这种复合操作没有原子性,所以其使用了CAS来实现原子性。

    7. 锁


    7.1 锁与volatile的内存意义基本相似(隐式通信)

    • 锁的释放与volatile变量的写有相同的内存意义。
    • 锁的获取与volatile变量的读有相同的内存意义。

    7.2 锁能够让临界区互斥执行(显式同步)

    7.3 锁与volatile变量的比较

    • volatile仅仅能够保证单个volatile变量的读/写具有原子性,而锁能够保证整个临界区具有原子性。
    • volatile性能比锁好,而功能不如锁。

    注:Lock锁的实现使用到了volatile与CAS来实现的,具体后面会讲。

    8. final域


    7.1 内存意义:
    只要final对象时正确构造的,那么不需要使用同步(锁与volatile)就可以保证任何线程都能够看都该对象的final域在构造函数中被初始化的值。

    9. Concurrent包的实现


    9.1 实现原理
    由于java的CAS同时具有 volatile 读和volatile写的内存语义,因此Java线程之间的通信现在有了下面四种方式:
    A线程写volatile变量,随后B线程读这个volatile变量。
    A线程写volatile变量,随后B线程用CAS更新这个volatile变量。
    A线程用CAS更新一个volatile变量,随后B线程用CAS更新这个volatile变量。
    A线程用CAS更新一个volatile变量,随后B线程读这个volatile变量。

    9.2 实现机制
    如果我们仔细分析concurrent包的源代码实现,会发现一个通用化的实现模式:

    1. 首先,声明共享变量为volatile
    2. 然后,使用CAS的原子条件更新来实现线程之间的同步
    3. 同时,配合以volatile的读/写和CAS所具有的volatile读和写的内存语义来实现线程之间的通信

    整体实现如下图:
    enter description here

    注:AQS,非阻塞数据结构和原子变量类(java.util.concurrent.atomic包中的类),这些concurrent包中的基础类都是使用这种模式来实现的,而concurrent包中的高层类又是依赖于这些基础类来实现的

    10.应用


    8.1 单例模式中可以使用双重检查锁机制来创建单例
    代码如下:

    class Singleton {
        private volatile static Singleton instance;	// 1
        
        private Singleton() {}
        
        public static Singleton getInstance() {
            if (instance == null) {				// 2
                synchronized (Singleton.class) {
                    if (instance == null) {
                        instance = new Singleton();		// 3
                    }
                }
            }
            return instance;
        }
    }
    

    注意:实例引用必须是volatile变量。如果不是,在运行到2时读取到instance不为null,但是可能该instance所指向的对象还没有完成初始化。原因:创建对象时的重排序,自己不太清楚,具体可以参见《Java并发编程的艺术》。

    8.2 单例模式中基于类初始化的获取单例(内部类)
    代码如下:

    class Singleton {
        private Singleton() {}
        
        public static Singleton getInstance() {
            return SingletonHolder.instance;  // 这里才会导致SingletonHolder类的初始化
        }
        
        private static class SingletonHolder{
            static Singleton instance = new Singleton();
        }
    }
    

    优点:只有在第一次调用getInstance()方法时才会正式加载SingletonHolder类,也就是才会创建对象。

    线程安全的原因:在类的初始化时期,JVM会获取一个锁,该锁可以同步多个线程对同一个类的初始化。

    11.References


    《Java并发编程的艺术》

    转载于:https://www.cnblogs.com/maying3010/p/6924710.html

    展开全文
  • 当我们在使用Java进行网络编程时经常会遇到很多...而对于Java并发来说,与超时相关的内容主要是线程等待超时和获取锁超时,比如调用Object.wait(long)就会使线程进入等待状并在指定时间后等待超时。此篇主要讲解Java...
  • 本书以由浅入深的方式来对Java并发编程的相关知识进行讲解,首先是对高并发的理解、多线程基础、Java并发包相关类的使用与设计原理进行介绍;然后再拓展讲解并发系统设计的相关原理和涉及的相关框架、中间件等;...
  • Java并发Timer源码分析

    2020-08-27 05:14:00
    讲述了java并发编程的相关知识点,并通过Timer源码分析更深入的讲解java并发编程。
  • 上文讲解Java线程的创建、启动以及停止,在讲到停止线程时说到了Java中断,Java中断是停止线程的一种协作机制,本文打算对Java中断机制进行详细讲解。中断是一种协作机制必须记住,中断是一种协作机制。当一个线程...
  • 当我们在使用Java进行网络编程时经常会遇到很多...而对于Java并发来说,与超时相关的内容主要是线程等待超时和获取锁超时,比如调用Object.wait(long)就会使线程进入等待状并在指定时间后等待超时。此篇主要讲解Java...
  • Java并发概述

    2018-03-08 07:42:12
    此专栏主要讲解java并发相关的知识,包含如何创建线程,使用Executor,锁,并发集合以及java并发库的源码分析。 线程创建 [java多线程]-Executor框架简述 [java线程]-常见的锁 [java多线程]-状态依赖 [java多...
  • Java并发编程之美

    千次阅读 2018-09-01 15:21:57
    一、前言 并发编程相比 Java 中其他知识点学习门槛较高,从而导致很多人望而却步。但无论是职场面试,还是高并发/高流量的系统的...- 第一部分Java并发编程基础篇主要讲解Java并发编程的基础知识,主要讲解线程有关...
  • 上文讲解Java线程的创建、启动以及停止,在讲到停止线程时说到了Java中断,Java中断是停止线程的一种协作机制,本文打算对Java中断机制进行详细讲解。 在网上搜索Java中断机制,发现两篇好文章,分别如下:Java ...
  • 本篇主要是建立一个对java并发基础知识的整个脉络结构,用来更好得理清并发的关系,不涉及各种工具类的使用和原理讲解等。
  • java并发编程书籍推荐并发编程是我们程序员进阶必须掌握的技能,今天推荐两本书,非常适合学习并发编程(都是我自己看过的),都是阿里大佬写的,这两本书上的知识掌握了,可以说应对大厂的面试没有任何问题。...
  • Java并发(基础知识)—— 创建、运行以及停止一个线程中讲解了两种创建线程的方式:直接继承Thread类以及实现Runnable接口并赋给Thread,这两种创建线程的方式在线程比较少的时候是没有问题的,但是当需要创建...
  • Java并发编程之美》分为三部分,第一部分为Java 并发编程基础篇,主要讲解Java 并发编程的基础知识、线程有关的知识和并发编程中的其他相关概念,这些知识在高级篇都会有所使用,掌握了本篇的内容,就为学习高级篇...
  • 当我们在使用Java进行网络编程时经常会遇到很多...而对于Java并发来说,与超时相关的内容主要是线程等待超时和获取锁超时,比如调用Object.wait(long)就会使线程进入等待状并在指定时间后等待超时。此篇主要讲解Java...
  • 本文将会从一个开在学校里的水果超市作为故事背景,循序渐进地展开各种故事情节,串起大部分的 Java 并发编程的知识,其知识点包括但不限于: 线程的生命周期 锁对象 临界区和受保护资源 wait 与 no...
  • Java并发编程实践中的话:编写正确的程序并不容易,而编写...本场Chat作为Java并发编程之美系列的高级篇之二,主要讲解内容如下:(建议先阅读:Java编程之美:并发编程高级篇之一)rt.jar中Unsafe类主要函数讲解,Un
  • Java并发编程第一篇博客,主要讲解线程的安全性,有无状态类是什么,以及原子性,原子操作,竞争操作,复合操作机制,最后讲解锁机制,使用内部锁以及内部锁的解读。
  • 1. 进程与线程认知强化 1.1如何理解进程与线程? 首先我们先来了解一下关于线程的一些基本的定义: 进程: 进程:操作系统进行资源...2.并发就是在单核处理器中同时处理多个任务 3.并行就是在多核处理器中同时处理
  • JAVA并发编程核心技术精讲

    千人学习 2018-04-28 06:09:39
    [JAVA工程师必会知识点之并发编程]       1、现在几乎100%的公司面试都必须面试并发编程,尤其是互联网公司,对于并发编程的要求更高,并发编程能力已经成为职场敲门砖。 2、现在已经是移动互联和...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 666
精华内容 266
关键字:

java并发知识讲解

java 订阅