首页 > 编程语言 >Java多线程(7):JUC(下)

Java多线程(7):JUC(下)

时间:2022-11-02 06:55:05浏览次数:52  
标签:count JUC Java map int list new 多线程 public

您好,我是湘王,这是我的博客园,欢迎您来,欢迎您再来~

 

除了四种常见的同步器(发令枪、摇号器、栅栏和交换机),JUC还有所谓线程安全的容器、阻塞队列和一些特殊的类。其中常出现的就是线程安全的容器和阻塞队列。与其说这是两个大的分类,还不如说它就是两个用得最多的类:ConcurrentHashMap和ArrayBlockingQueue。

我的风格是尽量少讲原理,多讲实际生活中的案例,除非它非常重要,就像AQS,这玩意绝对是个重量级的神器,差不多整个JUC都是建立在它之上的。如果说学习多线程只有一次集中全部精力的机会的话,那我绝对建议把这份宝贵的精力花在AQS上,物超所值。至于搞明白ThreadPool底层原理、synchronized关键字原理,个人认为纯属浪费时间。因为即使弄明白了,那个机制你是改不了的,而AQS原理弄明白了,可以直接拿来用,自己玩出花来。

从之前的集合类继承结构图可以知道,ConcurrentHashMap继承自Map,实现了ConcurrentMap接口,它是线程安全的Map,侧重于放入或者获取的速度,而不在乎顺序。

因为现在的Java基本上都升级到了JDK1.8以上,有的甚至和官方保持同步,所以基本上很多JDK1.7及以下会出现的问题,在新的版本中都修复了,而且性能越来越好。所以只需要知道一点:在并发量比较高的环境中尽量使用ConcurrentHashMap就好了。不用弄明白为什么,因为不值得(JDK1.8的ConcurrentHashMap底层是用红黑树实现的,至于什么是红黑树,又得回头补一些基础,而且记住了也没啥用)。当然如果是为了应付面试,稍稍了解一下就好了。

为了给大家直观的印象ConcurrentHashMap怎么个线程安全法,这里那我之前的项目代码做个演示(当然是删除了很多实际需求代码,仅演示ConcurrentHashMap)。

电商里为了造一些刷单数据,需要模拟真实用户,那么就由代码产生好了:

/**
 * 随机姓名产生器
 *
 * @author 湘王
 */
public class RandomInfo2 {
    private static final String firstname = "赵钱孙李周吴郑王冯陈褚卫蒋沈韩杨朱秦尤许何吕施张孔曹严华金魏陶姜戚谢邹喻水云苏潘" +
            "葛奚范彭郎鲁韦昌马苗凤花方俞任袁柳鲍史唐费岑薛雷贺倪汤滕殷罗毕郝邬安常乐于时傅卞齐康伍余元卜顾孟平黄和穆萧尹姚邵湛汪祁毛" +
            "禹狄米贝明臧计成戴宋茅庞熊纪舒屈项祝董粱杜阮席季麻强贾路娄危江童颜郭梅盛林刁钟徐邱骆高夏蔡田胡凌霍万柯卢莫房缪干解应宗丁" +
            "宣邓郁单杭洪包诸左石崔吉龚程邢滑裴陆荣翁荀羊甄家封芮储靳邴松井富乌焦巴弓牧隗山谷车侯伊宁仇祖武符刘景詹束龙叶幸司韶黎乔苍" +
            "双闻莘劳逄姬冉宰桂牛寿通边燕冀尚农温庄晏瞿茹习鱼容向古戈终居衡步都耿满弘国文东殴沃曾关红游盖益桓公晋楚闫";
    private static final String lastname = "伟刚勇毅俊秀娟英华慧巧美娜静淑惠珠翠雅芝玉萍红娥玲芬芳燕彩春菊兰凤洁梅琳素云莲真环" +
            "雪荣爱妹霞香月莺媛艳瑞凡佳嘉琼勤珍贞莉桂娣叶璧璐娅琦晶妍茜秋珊莎锦黛青倩婷姣婉娴瑾颖露瑶怡婵雁蓓纨仪荷丹蓉眉君琴蕊薇菁梦" +
            "岚苑婕馨瑗琰韵融园艺咏卿聪澜纯毓悦昭冰爽琬茗羽希宁欣飘育滢馥筠柔竹霭凝晓欢霄枫芸菲寒伊亚宜可姬舒影荔枝思丽峰强军平保东文" +
            "辉力明永健世广志义兴良海山仁波宁贵福生龙元全国胜学祥才发武新利清飞彬富顺信子杰涛昌成康星光天达安岩中茂进林有坚和彪博诚先" +
            "敬震振壮会思群豪心邦承乐绍功松善厚庆磊民友裕河哲江超浩亮政谦亨奇固之轮翰朗伯宏言若鸣朋斌梁栋维启克伦翔旭鹏泽晨辰士以建家" +
            "致树炎德行时泰盛雄琛钧冠策腾楠榕风航弘";

    /**
     * 随机产生姓氏
     *
     */
    public static String getFirstName() {
        int strLen = firstname.length();
        int index = new Random().nextInt(strLen - 1);
        return firstname.substring(index, index + 1);
    }

    /**
     * 随机产生名字
     *
     */
    public static String getLastName() {
        int strLen = lastname.length();
        int index = new Random().nextInt(strLen - 1);
        return lastname.substring(index, index + 2);
    }

    public static void count(final List<String> list, final Map<String, Integer> map) {
        long start = System.currentTimeMillis();
        // 这里为了比较效率,特意调大了人数
        for (int i = 0; i < 10_000_000; i++) {
            String name = getFirstName() + getLastName();
            list.add(name);
        }
        System.out.println("产生了 " + list.size() + " 个名字");
        for (int i = 0; i < 5; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    list.stream().map(str -> str.substring(0, 1)).forEach(str -> {
                        Integer count = 1;
                        if (map.containsKey(str)) {
                            // 多线程情况下,普通的HashMap的get()方法得到的结果可能为null
                            // 但ConcurrentHashMap一定不会是null
                            count = map.get(str);
                            if (null == count) {
                                System.out.println("count == null");
                            }
                            ++count;
                        }
                        map.put(str, count);
                    });
                    System.out.println("总共 " + map.size() + " 个姓氏");
                }
            }).start();
        }
        int count = 0;
        for(Map.Entry<String, Integer> entry : map.entrySet()) {
            count = count + entry.getValue();
        }
        long end = System.currentTimeMillis();
        // 最后统计名字总数
        System.out.println("执行时间:" + (end - start) + " 毫秒");
    }

    public static void main(String[] args) throws IOException {
        List<String> list = new ArrayList<>();
        Map<String, Integer> map = new HashMap<>();
        count(list, map);

        list = new ArrayList<>();
        map = new ConcurrentHashMap<>();
        count(list, map);
    }
}

 

 

通过这个程序,可以非常直观地看到这两者的区别。至于原理,就不必强记了,因为你不可能记住,记住了也没鸟用。

 

 

 

 

 

ArrayBlockingQueue和LinkedBlockingQueue在之前的线程池ThreadPool中也提过,它们的共同点都是一边放一边拿,唯一的不同是一个是数量有限的,一个是数量无限的。

 

 

 

之前拿上菜的案例演示了ArrayBlockingQueue,现在同样的案例再用LinkedBlockingQueue来实现:

/**
 * LinkedBlockingQueue测试
 * 
 * @author 湘王
 */
public class LinkedBlockingQueueTester {
    public static BlockingQueue<String> queue = new LinkedBlockingQueue<String>(5);
    // 一个往里放
    class Producer implements Runnable {
        @Override
        public void run() {
            try {
                queue.offer("川菜");
                System.out.println(Thread.currentThread().getName() + " 厨师做好 川菜");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    // 一个往外拿
    class Consumer implements Runnable {
        @Override
        public void run() {
            try {
                String food = queue.poll();
                System.out.println(Thread.currentThread().getName() + " 客人消费 " + food);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        // 和ArrayBlockingQueue不讲究顺序不同,LinkedBlockingQueue需要将生产的一方放在前面
        // 厨师做好菜
        for (int i = 0; i < 5; i++) {
            new Thread(new LinkedBlockingQueueTester().new Producer()).start();
        }
        // 客人等着菜
        for (int i = 0; i < 5; i++) {
            new Thread(new LinkedBlockingQueueTester().new Consumer()).start();
        }
    }
}

 

 

JUC中一些其他的类,例如ForkJoinPool,可以把任务分解成更小的任务,被分解出来任务会被继续分解成更小的任务,更形象地说法是“分形器”。而RecursiveTask是一种会返回结果的任务,它可以将自己分解成若干更小的任务,并将这些任务的执行结果合并到一个结果里。这两种用的不多。

整个JUC里面最重要的还是前面3种共6个Java类,掌握好AQS和这6个常见类,JUC基本就没啥问题了。

 

 


 

 

感谢您的大驾光临!咨询技术、产品、运营和管理相关问题,请关注后留言。欢迎骚扰,不胜荣幸~

 

标签:count,JUC,Java,map,int,list,new,多线程,public
From: https://www.cnblogs.com/xiangwang1111/p/16849307.html

相关文章

  • Javascript进阶笔记 - BOM
    6.BOM目录6.BOM1.BOM简介2.History3.Location4.定时器1.BOM简介BOM是指游览器对象模型,BOM提供了一组对象,方便用户通过JS操作游览器BOM对象Window代表......
  • Javascript进阶笔记 - 事件
    事件目录事件1.事件相关概念2.文档的加载3.事件的冒泡4.事件的委派5.事件监听绑定1.事件相关概念事件是电脑输入设备与页面进行交互的响应。注册就是告诉游......
  • Javascript进阶笔记 - 常用事件
    常用事件目录常用事件1.onload事件2.onclick事件3.onblur事件4.onchange事件5.onsubmit事件6.onscroll事件7.onmousemove事件8.onmousedown&onmouseup事件9.on......
  • Javascript进阶笔记 - DOM模型与节点
    DOM模型与节点目录DOM模型与节点1.DOM模型2.节点2.1节点的常用方法1.DOM模型DOM(文档对象模型)就是把文档中的标签,属性,文本转换成对象来管理(类似于Java中的对象)do......
  • Javascript进阶笔记 - DOM的增删改查
    DOM的增删改查目录DOM的增删改查1.DOM查询1.1常规DOM查询方法1.2通过CSS选择器查找HTML元素1.3通过HTML对象选择器查找HTML对象2.DOM元素的增添3.DOM元素的修改......
  • Javascript进阶笔记 - DOM操作CSS样式
    DOM操作CSS样式目录DOM操作CSS样式1.操作样式2.获取当前样式3.其它样式相关属性1.操作样式可以通过JS修改元素的内联样式语法:元素.style.样式名=样式值注意:......
  • JavaScript笔记 - 字符串常用方法
    字符串目录字符串1.字符串搜索indexOf()search()match()includes()startsWith()2.字符串提取slice()substring()substr()charAt()charCodeAt()split()3.字符串替换repl......
  • Javascript笔记 - JS中的数字
    数字目录数字1.精度2.数字方法3.把变量转换为数值1.精度整数在不使用指数或科学计数法的情况下,会被精确到15位varx=999999999999999;//x将是9999999999......
  • JavaScript笔记 - 正则表达式
    正则表达式目录正则表达式1.正则表达式语法1.1方括号1.2量词1.3元字符2.RegExp对象属性3.RegExp对象方法4.支持正则表达式的String对象的方法1.正则表达式语法......
  • java多态性(转载)
    原文链接https://www.cnblogs.com/jack204/archive/2012/10/29/2745150.htmlJava中多态性的实现什么是多态面向对象的三大特性:封装、继承、多态。从一定角度来看,封装和......