Java并发

1.基本并发方式

1.1 Callable

这个方式基本不用,会影响线程的思想。

2.线程池 Executor

2.1 ThreadExecutor

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }
  • corePoolSize 线程池Idle状态的线程。
  • maximumPoolSize 线程池支持的最大线程数,这个数达到某个值以后,就会导致OOM,可以理解如果有100多个线程,会不会导致内存不足以分配这么多线程
  • defaultHandler 这个就是丢弃策略。这个不是线程等待的时候丢弃,是线程池里面有超过maximumPoolSize的时候丢弃。

    2.2 CachedThreadPool

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

    这个作用是什么,就是没有核心线程,有新的runnable进来,就new 一个线程执行。如果空闲线程在60s内,还没有被回收,就达到复用的目的。所以线程是存在复用的可能,但是不会等待。

    2.3 FixedThreadPool

    ExecutorService executorService = Executors.newFixedThreadPool(2);

    这个才是最常用的线程池方式。

    线程调度,在池满以后等待。

2.4 SingleFixedThreadPool

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

有一个核心线程常驻。可以保证执行顺序。FIFO

sleep,interrupt,yield,join,notify,wait,await

3. 同步

3.1synchronized

  • 作用在局部区域上
    • this
    • class.name
  • 作用的实例方法上
  • 作用在静态方法
    这4种使用方式作用的区域各不相同。

    3.1.1 synchronized this

    这个住的是this,也就是当前实例。

    比如有2个方法,一个read,一个write,这个就是要加锁来解决的。

    如果是2个实例,他们相互直接的synchronized是不相干的。

    3.1.2 synchronized static or class.name Java并发

    static synchronized 的作用域是所有的静态方法。

    这个3.1.1 和3.1.2的synchronized 的作用范围不同,所以没有相关性。

    static方法的使用,本质上跟class类是没有关系的,只有一个索引用以找到这个方法。所以它sync的不是所有实例对象,sync的就是这个class.name。

3.2 Synchronizd 的本质

  • synchronized this 本质是在执行的过程中,加上了锁。

    锁是由JVM自动取解析的。

    $ javap -c SynchronizedDemo
    警告: 二进制文件SynchronizedDemo包含com.demanmath.androidms.javabase.concurrent.SynchronizedDemo
    Compiled from "SynchronizedDemo.java"
    public class com.demanmath.androidms.javabase.concurrent.SynchronizedDemo {
    public com.demanmath.androidms.javabase.concurrent.SynchronizedDemo();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
    
    public void testA();
    Code:
       0: aload_0
       1: dup
       2: astore_1
       3: monitorenter
       4: iconst_0
       5: istore_2
       6: aload_1
       7: monitorexit
       8: goto          16
      11: astore_3
      12: aload_1
      13: monitorexit
      14: aload_3
      15: athrow
      16: return
    Exception table:
       from    to  target type
           4     8    11   any
          11    14    11   any
    
    public static synchronized void testB();
    Code:
       0: iconst_0
       1: istore_0
       2: return
    }

    monitorenter,monitorexit 这2条指令,就是JVM控制同步的方式。

    关于这两条指令的作用,我们直接参考 JVM 规范中描述:

monitorenter :Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows:

• If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor.

• If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count.

• If another thread already owns the monitor associated with objectref, the thread blocks until the monitor’s entry count is zero, then tries again to gain ownership.

这段话的大概意思为:每个对象有一个监视器锁(Monitor),当 Monitor 被占用时就会处于锁定状态。

线程执行 Monitorenter 指令时尝试获取 Monitor 的所有权,过程如下:

如果 Monitor 的进入数为 0,则该线程进入 Monitor,然后将进入数设置为 1,该线程即为 Monitor 的所有者。

如果线程已经占有该 Monitor,只是重新进入,则进入 Monitor 的进入数加 1。

如果其他线程已经占用了 Monitor,则该线程进入阻塞状态,直到 Monitor 的进入数为 0,再重新尝试获取 Monitor 的所有权。

monitorexit:The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref.

The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner.

Other threads that are blocking to enter the monitor are allowed to attempt to do so.

这段话的大概意思为:执行 Monitorexit 的线程必须是 Objectref 所对应的 Monitor 的所有者。

指令执行时,Monitor 的进入数减 1,如果减 1 后进入数为 0,那线程退出 Monitor,不再是这个 Monitor 的所有者。

其他被这个 Monitor 阻塞的线程可以尝试去获取这个 Monitor 的所有权。

通过这两段描述,我们应该能很清楚的看出 Synchronized 的实现原理。

  • 在反编译方法以后,会看到方法带有ACC_SYNCHRONIZED 标志位。

    所以方法在运行的时候,获取当前方法的实例对象,进行monitor,锁住。

    因此整个synchronized 作用方式就2种,对象和方法。当然静态时候 是另外2个case,所以一共4个case。

原文 

http://www.demanmath.com/index.php/2020/07/24/javabingfa/

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » Java并发

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址