热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

深入解析JDK1.8中的并发新功能与优化

JDK1.8引入了多项并发新特性,显著提升了编程效率。本文重点探讨了LongAdder和StampedLock的特性和应用场景。此外,还介绍了在多线程环境中发生死锁时,如何通过jps命令进行诊断和排查,提供了详细的步骤和示例。这些改进不仅增强了系统的性能,还简化了开发者的调试工作。

JDK1.8中有一些并发的新特性,可以提高变成的效率。本文写的主要是LongAdder和stampedlock的特性。
多线程发生死锁时dump查看方式:
使用命令jps:如下所示
这里写图片描述

通过这个命令我们可以得到死锁号,然后再通过命令jstack查看

如下所示:
这里写图片描述

LongAdder

LongAdder是什么?
在大数据处理过程,为了方便监控,需要统计数据,少不了原子计数器。为了尽量优化性能,需要采用高效的原子计数器。在jdk8中,引入了LongAdder,非常适合多线程原子计数器。
我们知道,AtomicLong已经是非常高效的了,涉及并发的地方都是使用CAS(无锁)操作,在硬件层次上去做 compare and set操作。效率非常高。
LongAdder比AtomicLong更加高效。

实现原理:
LongAdder 沿用了concurrentMap原理,他是将1个整数拆分成一个数组cells,数组中有若干个cell。若有多个线层,每个线程通过CAS更新其中的一个小cell。然后内部将数组做sum求和操作得到整数的value;
这样就使得AtomicLong的单一线程做CAS操作演变成多个线程同时做CAS操作,期间互不影响。从而提高效率;
LongAdder开始并没有做拆分,当多线程间执行遇到冲突时才会拆分cell,若是多线程执行始终没有冲突,则它相当于AtomicLong;

如何分配cell的???
拿到线程相关的HashCode对象后,获取它的code变量,计算出一个在Cells 数组中当前线程的HashCode对应的索引位置,并将该位置的Cell 对象拿出来用CAS更新它的value值。

LongAdder的继承树
这里写图片描述

LongAdder的方法
这里写图片描述

使用案例

import java.util.concurrent.atomic.LongAdder;

public class LongAdderTest {

    private static LongAdder la =new LongAdder();

    public static int a =0;
    public static void add(){
        la.increment();
        a++;
    }
    /** * @param args * @throws InterruptedException */
    public static void main(String[] args) throws InterruptedException {
        // TODO Auto-generated method stub
        Thread t1 = new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                for(int i=0;i<10000;i++){
                    add();
                }
            }
        });
        t1.start();

        Thread t2 = new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                for(int i=0;i<10000;i++){
                    add();
                }
            }
        });
        t2.start();
        t1.join();t2.join();
        System.out.println("---la-----"+la);
        System.out.println("---a-----"+a);
    }

}

执行结果:
这里写图片描述

StampedLock

stampedLock推出了乐观读锁,在使用乐观读锁时,不会阻塞写锁,这使得我们在写数据时,不会因为使用读锁而长时间的阻塞写,从而提高效率;
ReentrantReadWriteLock 在沒有任何读写锁时,才可以取得写入锁,这可用于实现了悲观读取(Pessimistic Reading),即如果执行中进行读取时,经常可能有另一执行要写入的需求,为了保持同步,ReentrantReadWriteLock 的读取锁定就可派上用场。
然而,如果读取执行情况很多,写入很少的情况下,使用 ReentrantReadWriteLock 可能会使写入线程遭遇饥饿(Starvation)问题,也就是写入线程迟迟无法竞争到锁定而一直处于等待状态。
StampedLock控制锁有三种模式(写,读,乐观读),一个StampedLock状态是由版本和模式两个部分组成,锁获取方法返回一个数字作为票据stamp,它用相应的锁状态表示并控制访问,数字0表示没有写锁被授权访问。在读锁上分为悲观锁和乐观锁。
所谓的乐观读模式,也就是若读的操作很多,写的操作很少的情况下,你可以乐观地认为,写入与读取同时发生几率很少,因此不悲观地使用完全的读取锁定,程序可以查看读取资料之后,是否遭到写入执行的变更,再采取后续的措施(重新读取变更信息,或者抛出异常) ,这一个小小改进,可大幅度提高程序的吞吐量!!
示例代码:

import java.util.concurrent.locks.StampedLock;

public class StampedLockTest {

    private static final StampedLock sl = new StampedLock();
    private static int x;
    private static int y;

    public static void move(int deltax,int deltay) throws InterruptedException{
        System.out.println("写线程----"+Thread.currentThread().getName());
        long sw = sl.writeLock();//获取写锁
        try{
            x = x+deltax;
            y = y+deltay;
        }finally{
            sl.unlockWrite(sw);//释放写锁
        }
    }

    public static int distanceFromOrigin(){
        long sr = sl.tryOptimisticRead(); //获取乐观读锁,不会阻塞写锁
        int currentx = x;
        int currenty = y;
        //读完成后验证期间是否有写操作改变了数据sl.validate(sr)为true则表示期间无写操作,否则表示数据可能已经被改变
        System.out.println(currentx+"---第一次读取数据-----"+currenty);
        if(!sl.validate(sr)){
            sr = sl.readLock(); //使用了悲观锁,会阻塞写锁
            try{
                System.out.println("悲观锁读线程----------"+Thread.currentThread().getName());
                currentx = x;
                currenty = y;
            }finally{
                sl.unlockRead(sr);//释放悲观读锁
            }
        }else{
            System.out.println("乐观锁读线程----------"+Thread.currentThread().getName());
        }
        System.out.println(currentx+"----第二次读取数据----"+currenty);
        return currentx*currenty;
    }
    /** * @param args */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        for(int i=0;i<10;i++){
            final int q =i;
            new Thread(new Runnable() {

                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    try {
                        move(q,q+8);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }).start();
        }
        for(int i=0;i<50;i++){
            new Thread(new Runnable() {

                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    try {
                        distanceFromOrigin();
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }

}

这里写图片描述


推荐阅读
  • 2023年京东Android面试真题解析与经验分享
    本文由一位拥有6年Android开发经验的工程师撰写,详细解析了京东面试中常见的技术问题。涵盖引用传递、Handler机制、ListView优化、多线程控制及ANR处理等核心知识点。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 并发编程:深入理解设计原理与优化
    本文探讨了并发编程中的关键设计原则,特别是Java内存模型(JMM)的happens-before规则及其对多线程编程的影响。文章详细介绍了DCL双重检查锁定模式的问题及解决方案,并总结了不同处理器和内存模型之间的关系,旨在为程序员提供更深入的理解和最佳实践。 ... [详细]
  • 本文探讨了在Java多线程环境下,如何确保具有相同key值的线程能够互斥执行并按顺序输出结果。通过优化代码结构和使用线程安全的数据结构,我们解决了线程同步问题,并实现了预期的并发行为。 ... [详细]
  • 本文介绍了Java并发库中的阻塞队列(BlockingQueue)及其典型应用场景。通过具体实例,展示了如何利用LinkedBlockingQueue实现线程间高效、安全的数据传递,并结合线程池和原子类优化性能。 ... [详细]
  • 本文详细介绍了 Java 中 org.apache.xmlbeans.SchemaType 类的 getBaseEnumType() 方法,提供了多个代码示例,并解释了其在不同场景下的使用方法。 ... [详细]
  • MySQL索引详解与优化
    本文深入探讨了MySQL中的索引机制,包括索引的基本概念、优势与劣势、分类及其实现原理,并详细介绍了索引的使用场景和优化技巧。通过具体示例,帮助读者更好地理解和应用索引以提升数据库性能。 ... [详细]
  • 在多线程编程环境中,线程之间共享全局变量可能导致数据竞争和不一致性。为了解决这一问题,Linux提供了线程局部存储(TLS),使每个线程可以拥有独立的变量副本,确保线程间的数据隔离与安全。 ... [详细]
  • 本文总结了Java程序设计第一周的学习内容,涵盖语言基础、编译解释过程及基本数据类型等核心知识点。 ... [详细]
  • 作者:守望者1028链接:https:www.nowcoder.comdiscuss55353来源:牛客网面试高频题:校招过程中参考过牛客诸位大佬的面经,但是具体哪一块是参考谁的我 ... [详细]
  • 不确定性|放入_华为机试题 HJ9提取不重复的整数
    不确定性|放入_华为机试题 HJ9提取不重复的整数 ... [详细]
  • 对象自省自省在计算机编程领域里,是指在运行时判断一个对象的类型和能力。dir能够返回一个列表,列举了一个对象所拥有的属性和方法。my_list[ ... [详细]
  • 深入解析Java枚举及其高级特性
    本文详细介绍了Java枚举的概念、语法、使用规则和应用场景,并探讨了其在实际编程中的高级应用。所有相关内容已收录于GitHub仓库[JavaLearningmanual](https://github.com/Ziphtracks/JavaLearningmanual),欢迎Star并持续关注。 ... [详细]
  • 深入解析Java虚拟机(JVM)架构与原理
    本文旨在为读者提供对Java虚拟机(JVM)的全面理解,涵盖其主要组成部分、工作原理及其在不同平台上的实现。通过详细探讨JVM的结构和内部机制,帮助开发者更好地掌握Java编程的核心技术。 ... [详细]
author-avatar
张雍昊
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有