精华内容
下载资源
问答
  • 大家好,并发编程 今天开始进入第二篇。 今天的内容会比较基础,主要是为了让新手也能无障碍地阅读,所以还是要再巩固下基础。学完了基础,你们也就能很顺畅地跟着我的思路理解以后的文章。 本文目录 学会使用函数...
  • 在.NET的世界里面,处理高并发大致有以下几种方法: 1、异步编程 异步编程就是使用future模式(又称promise)或者回调机制来实现(Non-blocking on waiting)。如果使用回调或事件来实现(容易callback hell),不仅...
  • 假设你是架构师,请用多线程相关基础知识实现单机 环境下,在3s内实现1亿的计数,并统计亿级计数的耗时。 要求:请运用11种方法实现亿级的并发计数。
  •  高并发编程第三阶段12讲 sun.misc.Unsafe介绍以及几种Counter方案性能对比.mp4  高并发编程第三阶段13讲 一个JNI程序的编写,通过Java去调用C,C++程序.mp4  高并发编程第三阶段14讲 Unsafe中的方法使用,一半是...
  • 在《Java 并发编程实战》中,定义如下: 当多个线程访问某个类时,不管运行时环境采用何调度方式或者这些线程 将如何交替执行,并且在调用代码中不需要任何额外的同步或者协同,这个类都 能表现出正确的行为,...

    什么是线程安全性

    在《Java 并发编程实战》中,定义如下:

    当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在调用代码中不需要任何额外的同步或者协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。

    线程封闭

    实现好的并发是一件困难的事情,所以很多时候我们都想躲避并发。避免并发最简单的方法就是线程封闭。什么是线程封闭呢?
    就是把对象封装到一个线程里,只有这一个线程能看到此对象。那么这个对象就算不是线程安全的也不会出现任何安全问题。实现线程封闭有哪些方法呢?

    ad-hoc线程封闭
    这是完全靠实现者控制的线程封闭,他的线程封闭完全靠实现者实现。 Ad-hoc 线程封闭非常脆弱,应该尽量避免使用。

    栈封闭
    栈封闭是我们编程当中遇到的最多的线程封闭。
    什么是栈封闭呢?
    简单的说 就是局部变量。多个线程访问一个方法,此方法中的局部变量都会被拷贝一份到 线程栈中。所以局部变量是不被多个线程所共享的,也就不会出现并发问题。所以能用局部变量就别用全局的变量,全局变量容易引起并发问题。

    无状态的类

    没有任何成员变量的类,就叫无状态的类,这种类一定是线程安全的。
    参见代码:

    /**
     1. 无状态的类
     */
    public class StatelessClass {
    	public int service(int a,int b){
    	    return a+b;
        }
    
        public void serviceUser(UserVo user){
            //do sth user
        }
    }
    

    如果这个类的方法参数中使用了对象,也是线程安全的吗?比如:
    在这里插入图片描述
    当然也是,为何?因为多线程下的使用,固然 user 这个对象的实例会不正 常,但是对于 StatelessClass 这个类的对象实例来说,它并不持有 UserVo 的对象 实例,它自己并不会有问题,有问题的是 UserVo 这个类,而非 StatelessClass 本身。

    让类不可变

    让状态不可变,两种方式:

    1. 加 final 关键字,对于一个类,所有的成员变量应该是私有的,同样的只 要有可能,所有的成员变量应该加上 final 关键字,但是加上 final,要注意如果 成员变量又是一个对象时,这个对象所对应的类也要是不可变,才能保证整个类 是不可变的。
      参见代码:
    /**
     1. 类不可变
     */
    public class ImmutableClass {
        private final int a;
        private final UserVo user = new UserVo();//不安全
    
    	public int getA() {
    		return a;
    	}
    	public UserVo getUser() {
    		return user;
    	}
    	public ImmutableClass(int a) {
    		this.a = a;
    	}
    	public static class User{
        	private int age;
    
    		public int getAge() {
    			return age;
    		}
    		public void setAge(int age) {
    			this.age = age;
    		}
        }
    }
    
    1. 根本就不提供任何可供修改成员变量的地方,同时成员变量也不作为方 法的返回值。
      参见代码:
    /**
     * 类不可变--事实不可变
     */
    public class ImmutableClassToo {
        private final List<Integer> list = new ArrayList<>(3);
    
        public ImmutableClassToo() {
            list.add(1);
            list.add(2);
            list.add(3);
        }
    
        public boolean isContain(int i){
            return list.contains(i);
        }
    }
    

    但是要注意,一旦类的成员变量中有对象,上述的 final 关键字保证不可变并不能保证类的安全性,为何?
    因为在多线程下,虽然对象的引用不可变,但是 对象在堆上的实例是有可能被多个线程同时修改的,没有正确处理的情况下,对 象实例在堆中的数据是不可预知的。这就牵涉到了如何安全的发布对象这个问题。
    在这里插入图片描述

    volatile

    并不能保证类的线程安全性,只能保证类的可见性,最适合一个线程写,多个线程读的情景。

    加锁和CAS

    我们最常使用的保证线程安全的手段,使用 synchronized 关键字,使用显式 锁,使用各种原子变量,修改数据时使用 CAS 机制等等。

    安全的发布

    类中持有的成员变量,如果是基本类型,发布出去,并没有关系,因为发布 出去的其实是这个变量的一个副本
    参见代码:

    /**
     * 演示基本类型的发布
     */
    public class SafePublish {
    	private int i;
    
        public SafePublish() {
        	i = 2;
        }
    	public int getI() {
    		return i;
    	}
    	public static void main(String[] args) {
    		SafePublish safePublish = new SafePublish();
    		int j = safePublish.getI();
    		System.out.println("before j="+j);
    		j = 3;
    		System.out.println("after j="+j);
    		System.out.println("getI = "+safePublish.getI());
    	}
    }
    

    但是如果类中持有的成员变量是对象的引用,如果这个成员对象不是线程安 全的,通过 get 等方法发布出去,会造成这个成员对象本身持有的数据在多线程 下不正确的修改,从而造成整个类线程不安全的问题。
    参见代码

    /**
     * 不安全的发布
     */
    public class UnSafePublish {
    	private List<Integer> list = new ArrayList<>(3);
    	
        public UnSafePublish() {
        	list.add(1);
        	list.add(2);
        	list.add(3);
        }
    	
    	public List getList() {
    		return list;
    	}
    
    	public static void main(String[] args) {
    		UnSafePublish unSafePublish = new UnSafePublish();
    		List<Integer> list = unSafePublish.getList();
    		System.out.println(list);
    		list.add(4);
    		System.out.println(list);
    		System.out.println(unSafePublish.getList());
    	}
    }
    

    在这里插入图片描述
    这个 list 发布出去后,是可以被外部线程之间修改,那么在多个线程同时修 改的情况下不安全问题是肯定存在的,怎么修正这个问题呢?我们在发布这对象 出去的时候,就应该用线程安全的方式包装这个对象。
    参见代码:

    /**
     1. 安全的发布
     */
    public class SafePublishToo {
        private List<Integer> list
                = Collections.synchronizedList(new ArrayList<>(3));
    
        public SafePublishToo() {
            list.add(1);
            list.add(2);
            list.add(3);
        }
    
        public List getList() {
            return list;
        }
    
        public static void main(String[] args) {
            SafePublishToo safePublishToo = new SafePublishToo();
            List<Integer> list = safePublishToo.getList();
            System.out.println(list);
            list.add(4);
            System.out.println(list);
            System.out.println(safePublishToo.getList());
        }
    }
    

    我们将 list 用 Collections.synchronizedList 进行包装以后,无论多少线程使用这个 list,就都是线 程安全的了。
    在这里插入图片描述
    对于我们自己使用或者声明的类,JDK 自然没有提供这种包装类的办法,但 是我们可以仿造这种模式或者委托给线程安全的类,当然,对这种通过 get 等方 法发布出去的对象,最根本的解决办法还是应该在实现上就考虑到线程安全问题

    另外对容器的包装

    /**
     * 仿Collections对容器的包装,将内部成员对象进行线程安全包装
     */
    public class SoftPublicUser {
        private final UserVo user;
    
        public UserVo getUser() {
            return user;
        }
    
        public SoftPublicUser(UserVo user) {
            this.user = new SynUser(user);
        }
    
        private static class SynUser extends UserVo{
            private final UserVo userVo;
            private final Object lock = new Object();
    
            public SynUser(UserVo userVo) {
                this.userVo = userVo;
            }
    
            @Override
            public int getAge() {
                synchronized (lock){
                    return userVo.getAge();
                }
            }
    
            @Override
            public void setAge(int age) {
                synchronized (lock){
                    userVo.setAge(age);
                }
            }
        }
    
    }
    
    /**
     * 类说明:委托给线程安全的类来做
     */
    public class SafePublicFinalUser {
        private final SynFinalUser user;
    
        public SynFinalUser getUser() {
            return user;
        }
    
        public SafePublicFinalUser(FinalUserVo user) {
            this.user = new SynFinalUser(user);
        }
    
        public static class SynFinalUser{
            private final FinalUserVo userVo;
            private final Object lock = new Object();
    
            public SynFinalUser(FinalUserVo userVo) {
                this.userVo = userVo;
            }
    
            public int getAge() {
                synchronized (lock){
                    return userVo.getAge();
                }
            }
    
            public void setAge(int age) {
                synchronized (lock){
                    userVo.setAge(age);
                }
            }
        }
    
    }
    

    TheadLocal

    ThreadLocal 是实现线程封闭的最好方法。 ThreadLocal 内部维护了一个 Map, Map 的 key 是每个线程的名称,而 Map 的值就是我们要封闭的对象。每个线程 中的对象都对应着 Map 中一个值,也就是 ThreadLocal 利用 Map 实现了对象的线程封闭。

    Servlet辨析

    不是线程安全的类,为什么我们平时没感觉到:

    1. 在需求上,很少有共享的需求
    2. 接收到了请求,返回应答的时候,一 般都是由一个线程来负责的。 但是只要 Servlet 中有成员变量,一旦有多线程下的写,就很容易产生线程安全问题。
    展开全文
  • 主要介绍了PHP编程中尝试程序并发几种方式总结,这里举了借助yield的异步以及swoole_process的进程创建等例子,PHP本身并不支持多线程并发,需要的朋友可以参考下
  • C#编程并发几种处理方法

    千次阅读 2017-12-31 12:53:00
    在.NET的世界里面,处理高并发大致有以下几种方法: 1,异步编程 异步编程就是使用future模式(又称promise)或者回调机制来实现(Non-blocking on waiting)。如果使用回调或事件来实现(容易callback hell),...

    并发(英文Concurrency),其实是一个很泛的概念,字面意思就是“同时做多件事”,不过方式有所不同。在.NET的世界里面,处理高并发大致有以下几种方法:

    1,异步编程

    异步编程就是使用future模式(又称promise)或者回调机制来实现(Non-blocking on waiting)。如果使用回调或事件来实现(容易callback hell),不仅编写这样的代码不直观,很快就容易把代码搞得一团糟。

    不过在.NET 4.5 及以上框架中引入的async/await关键字(在.NET 4.0中通过添加Microsoft.Bcl.Async包也可以使用),让编写异步代码变得容易和优雅。通过使用async/await关键字,可以像写同步代码那样编写异步代码,所有的回调和事件处理都交给编译器和运行时帮你处理了,简单好用。

    使用异步编程有两个好处:不阻塞主线程(比如UI线程),提高服务端应用的吞吐量。所以微软推荐ASP.NET中默认使用异步来处理请求。

     如果你看到这段文字,说明您正使用RSS阅读或转自《一棵树-博客园》,原文地址:http://www.cnblogs.com/atree/p/Concurrency_Async.html 

    例如:我用异步做微信模板消息推送。

    /// <summary>
    /// 使用异步Action测试异步模板消息接口
    /// </summary>
    /// <param name="checkcode"></param>
    /// <returns></returns>
    public async Task<string> TemplateMessageAsync(string openId, string first, string keyword1, string keyword2, string keyword3, string keyword4, string remark, string url)
    {
        if (openId == null)
        {
            return ReturnString(7771, "OPENID不能为空");
        }
        else
        {
            var testData = new //TestTemplateData()
            {
                first = new TemplateDataItem(first),
                keyword1 = new TemplateDataItem(keyword1),
                keyword2 = new TemplateDataItem(keyword2),
                keyword3 = new TemplateDataItem(keyword3),
                keyword4 = new TemplateDataItem(keyword4),
                remark = new TemplateDataItem(remark)
            };
    
            var result = await TemplateApi.SendTemplateMessageAsync(_wechat.APPID, openId, "m6td4jp_heMA5rhopbUaHApOlp2DD5x18BMXWKj3M5U", url, testData);
            return ReturnString(0, "成功");
        }
    }

     2,并行编程

    并行编程的出现实际上是随着CPU有多核而兴起的,目的是充分利用多核CPU的计算能力。并行编程由于会提高CPU的利用率,更适合客户端的一些应用,对于服务端的应用可能会造成负面影响(因为服务器本身就具有并行处理的特点,比如IIS会并行的处理多个请求)。我自己使用并行编程最多的场景是之前分析环境数据不确定度的时候,使用并行的方式计算蒙特卡洛模拟(计算上千次之后拟合),当然后来我使用泰勒级数展开来计算不确定度,没有这么多的计算量就无需并行了。当然在计算多方案结果比较的情况下,还是继续使用了并发计算。

    在.NET中,并行的支持主要靠.NET 4.0引入的任务并行库和并行LINQ。通过这些库可以实现数据并行处理(处理方式相同,输入数据不同,比如我上面提到的应用场景)或者任务并行处理(处理方式不同,且数据隔离)。通过使用并行处理库,你不用关心Task的创建和管理(当然更不用说底层的线程了),只需要关注处理任务本身就行了。

    具体的用法还是参考官方文档:https://msdn.microsoft.com/en-us/library/dd460693(v=vs.110).aspx

    3,响应式编程

    响应式编程最近成为了一个Buzzword,其实微软6年前就开始给.NET提供一个Reactive Extensions了。一开始要理解响应式编程有点困难,但是一旦理解了,你就会对它的强大功能爱不释手。简单来说,响应式编程把事件流看作数据流,不过数据流是从IEnumable中拉取的,而事件流是从IObservable推送给你的。为什么响应式编程可以实现并发呢?这是因为Rx做到线程不可知,每次事件触发,后续的处理会从线程池中任意取出一个线程来处理。且可以对事件设置窗口期和限流。举个例子,你可以用Rx来让搜索文本框进行延迟处理(而不用类似我很早的时候用个定时器来延迟了)。

    要详细了解Rx最好的方式就是浏览 IntroToRx.com 这个网站,当然还有官方文档:https://msdn.microsoft.com/en-us/data/gg577609。

    4,数据流编程

    数据流(DataFlow)编程可能大家就更陌生了,不过还是有些常用场景可以使用数据流来解决。数据流其实是在任务并行库(TPL)上衍生出来的一套处理数据的扩展(也结合了异步的特性),TPL也是处理并行编程中任务并行和数据并行的基础库。

    望文生义,TPL DataFlow就是对数据进行一连串处理,首先为这样的处理定义一套网格(mesh),网格中可以定义分叉(fork)、连接(join)、循环(loop)。数据流入这样的处理网格就能够并行的被处理。你可以认为网格是一种升级版的管道,实际上很多时候就是被当作管道来使用。使用场景可以是“分析文本文件中词频”,也可以是“处理生产者/消费者问题”。

    参考资料当然也是官方文档:https://msdn.microsoft.com/en-us/library/hh228603(v=vs.110).aspx。

    5,Actor模型

    Scala有Akka,其实微软研究院也推出了Orleans来支持了Actor模型的实现,当然也有Akka.NET可用。Orleans设计的目标是为了方便程序员开发需要大规模扩展的云服务, 可用于实现DDD+EventSourcing/CQRS系统。

    官方网站是:http://dotnet.github.io/orleans/

    展开全文
  • java高并发处理方法

    2018-09-07 17:17:35
    总共有6小节介绍解决方案,个人觉得是挺有用的
  • java并发编程艺术

    2018-07-23 17:23:41
    在进行并发编程时,如果希望通过多线程执行任务让程序运行的更快,会面临非常多的挑战,比如上下文切换的问题,死锁的问题,以及受限于硬件和软件的资源限制问题,本章会介绍几种并发编程的挑战,以及解决方案。
  • 对于并发编程,Python的实现,总结了一下,大致有如下三种方法: 多线程 多进程 协程(生成器) 在之后的章节里,将陆陆续续地给大家介绍到这三个知识点。 . 并发编程的基本概念 在开始讲解理论知识之前,先过...
  • 【多线程高并发编程】二 实现多线程的几种方式

    万次阅读 多人点赞 2020-02-17 23:32:54
    本文我们来看看多线程的应用场景,为什么要用多线程,以及实现一个多线程有几种方式。

    程序猿学社的GitHub,欢迎Star
    https://github.com/ITfqyd/cxyxs
    本文已记录到github,形成对应专题

    前言:

    上章,我们已经了解线程的一些基本概念。本文我们来看看多线程的应用场景,为什么要用多线程,以及实现一个多线程有几种方式。

    1.什么是多线程?

    多线程是指通过软件优化和硬件(CPU)的方式,同时并发运行多个线程(任务)。更好的运行系统的资源。

    例如,社长,很久以前,接到boss的提的一个业务,需要开发一个充电桩管理物联网管理平台,实现通过网站,查看各个充电桩的情况。如果就社长一个人开发,感觉1年搞定都有点难,毕竟社长专注于后端开发,这时社长就跟boss提出,需要增加人马,招一个前端,一个后端。社长就负责跟硬件对接,每个人负责一块,各种同步开发。最后,通过社长三人的努力,半年就交差了。这就是多线程的好处。多个人,信息也是共享(一个进程内的多个线程,资源是共享在同一个内存中)
    在这里插入图片描述

    1.1应用场景

    • 网站发送多个请求,会一一返回结果,也就是很高的使用了多线程。如果没有多线程,我们抢票,就得发一个请求后,需要等请求处理完后,才能运行。
    • 扣扣聊天界面,如果没有多线程,发一个消息,需要上一个消息处理完后,才能处理下一个需求。
    • 通过netty解析数据报文,如果没有多线程,1w个线程,直接怼进来,我们只能一个个处理,肯定处理不过来,如果没有多线程,解析逻辑也无法和业务逻辑分离开,实现程序的解耦。

    2.实现一个多线程的常见几种方式

    为了模拟真实的场景,每个线程中,都增加了延迟运行的代码。

    Thread.sleep(1000);
    

    这句代码表示休眠1秒钟,以毫秒为单位。

    通过继承的方式,实现多线程(第一种)

    package com.cxyxs.two;
    
    import java.util.Date;
    
    /**
     * Description:第一种:通过继承的方式,实现多线程
     *  转发请注明来源  程序猿学社 - https://ithub.blog.csdn.net/
     * Author: 程序猿学社
     * Date:  2020/2/17 21:37
     * Modified By:
     */
    public class MyThreadExtend extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("程序猿学社:社长在开发中,通过继承的方式实现:" + new Date());
            }
        }
    }
    

    调用代码

      //第一种方式
      MyThreadExtend threadExtend = new MyThreadExtend();
      threadExtend.start();
    
    • 因为java中是单继承,所以不推荐通过这种方式实现多线程。如果该类已经被继承,是无法继承Thread类的。

    通过实现runnable接口,实现多线程(第二种)

    package com.cxyxs.two;
    
    import java.util.Date;
    
    /**
     * Description:转发请注明来源  程序猿学社 - https://ithub.blog.csdn.net/
     * Author: 程序猿学社
     * Date:  2020/2/17 21:43
     * Modified By:
     */
    public class MyThreadRunnable implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("小二在开发中,通过实现Runnable接口方式实现:" + new Date());
            }
        }
    }
    

    调用代码

    //第二种方式
    MyThreadRunnable runnable = new MyThreadRunnable();
    Thread thread = new Thread(runnable);
    thread.start();
    

    匿名内部类实现(第三种)

    Runnable方式

    //匿名内部类-第一种
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("通过匿名内部类的方式第一种实现!");
                }
            }).start();
    

    继承类方式

    //匿名内部类-第二种
            new Thread(){
                @Override
                public void run() {
                    System.out.println("通过匿名内部类的方式第二种实现!");
                }
            }.start();
    

    通过Callable实现多线程(第四种)

    public class MyThreadCallable implements Callable<Integer> {
    
        @Override
        public Integer call() throws Exception {
            int sum=0;
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                sum+=i;
                System.out.println("小王通过实现Callable接口的方式实现:" + new Date());
            }
            return sum;
    
        }
    }
    

    调用代码

    //第四种 通过Callable实现多线程
            MyThreadCallable callable = new MyThreadCallable();
            FutureTask<Integer> result = new FutureTask<Integer>(callable);
            new Thread(result).start();
            try {
                Integer sum =  result.get();
                System.out.println("计算结果:"+sum);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
    
    • 之前几种实现多线程的方法,需要重写run方法,启动多线程。而Callable方式实现多线程,需要重写call方法。通过FutureTask包装器来创建Thread线程
    • 通过调用get方法获取多线程的运行结果。注意,get方法会一直堵塞,没有返回值,主线程会一直等待。
    • 应用场景,例如批量大数据的导出,假设我们要导出100w数据,需要30s,我们就可以通过分页,每个线程查10w的数据,启动10个线程,来获取处理结果。这样就可以通过多线程提供查询的效率。

    启动一个main线程,启动了几个线程

     public class ThreadCount {
        public static void main(String[] args) {
            ThreadGroup group =
                    Thread.currentThread().getThreadGroup();
            int count = group.activeCount();
            group.list();
        }
    }
    

    idea打印
    在这里插入图片描述
    eclipse打印
    在这里插入图片描述
    启动了2个线程,一个主线程main,是程序的入口。
    还有一个[Monitor Ctrl-Break,这是IDEA特有的监控线程。正确的打印应该是gc线程。通过上面两张图,就可以得出这个结论。

    多个线程进行测试

    public class Test {
        public static void main(String[] args) {
            //第一种方式
            MyThreadExtend threadExtend = new MyThreadExtend();
            threadExtend.start();
    
            //第二种方式
            MyThreadRunnable runnable = new MyThreadRunnable();
            Thread thread = new Thread(runnable);
            thread.start();
    
            //第三种方式
            //匿名内部类-第一种
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("通过匿名内部类的方式第一种实现!");
                }
            }).start();
    
            //匿名内部类-第二种
            new Thread() {
                @Override
                public void run() {
                    System.out.println("通过匿名内部类的方式第二种实现!");
                }
            }.start();
    
            //第四种 通过Callable实现多线程
            MyThreadCallable callable = new MyThreadCallable();
            FutureTask<Integer> result = new FutureTask<Integer>(callable);
            new Thread(result).start();
            try {
                Integer sum =  result.get();
                System.out.println("计算结果:"+sum);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
    

    在这里插入图片描述

    • 通过测试的结果,我们发现,数据是交换运行的,如果是单cpu,每次只能运行一个通道,就算用多线程的方式实现,每次也是交替运行。只是计算机运行速度很快,我们看不出什么区别。说到这里,就有社友提出疑问,单cpu,还有必要用多线程吗
      就算是单cpu,利用多线程也有很多好处。让我们可以减少没必要的等待,更好的利用资源。
    • 如果是多cpu,就可以同时并发运行多个任务,大大的提高运行效率。
    展开全文
  •  高并发编程第三阶段12讲 sun.misc.Unsafe介绍以及几种Counter方案性能对比.mp4  高并发编程第三阶段13讲 一个JNI程序的编写,通过Java去调用C,C++程序.mp4  高并发编程第三阶段14讲 Unsafe中的方法使用,一半是...
  • 《死磕 Java 并发编程》系列连载中,大家可以关注一波: ????????『死磕Java并发编程系列』 01 十张图告诉你多线程那些破事 『死磕Java并发编程系列』 02 面试官:说说什么是Java内存模型? 『死磕Java并发编程...

    《死磕 Java 并发编程》系列连载中,大家可以关注一波:

    👍🏻『死磕Java并发编程系列』 01 十张图告诉你多线程那些破事

    『死磕Java并发编程系列』 02 面试官:说说什么是Java内存模型?

    『死磕Java并发编程系列』 03 面试必问的CAS原理你会了吗?

    『死磕Java并发编程系列』 04 面试官:说说Atomic原子类的实现原理?

    👍🏻『死磕Java并发编程系列』 05 图解Java中那18 把锁


    在日常编码中,Java 并发编程可是少不了,试试下面这些并发编程工具类。

    今天先带领大家一起重温学习 CountDownLatch 这个牛叉的工具类。

    认识 CountDownLatch

    CountDownLatch是一个同步工具类,用来协调多个线程之间的同步,或者说起到线程之间通信的作用(非互斥)。

    CountDownLatch 能够使一个线程在等待另外一些线程完成各自工作之后,再继续执行。使用一个计数器进行实现。计数器初始值为线程的数量。当每一个线程完成自己任务后,计数器的值就会减一。当计数器的值为0时,表示所有的线程都已经完成一些任务,然后在CountDownLatch上等待的线程就可以恢复执行接下来的任务。

    CountDownLatch 的使用

    CountDownLatch类使用起来非常简单。

    Class 位于:java.util.concurrent.CountDownLatch

    下面简单介绍它的构造方法和常用方法。

    构造方法

    CountDownLatch只提供了一个构造方法:

    // count 为初始计数值
    public CountDownLatch(int count) {
      // ……
    }
    

    常用方法

    //常用方法1:调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
    public void await() throws InterruptedException {
      // ……
    }   
    
    // 常用方法2:和await()类似,只不过等待超时后count值还没变为0的话就会继续执行
    public boolean await(long timeout, TimeUnit unit) throws InterruptedException { 
      // ……
    }
    
    // 常用方法3:将count值减1
    public void countDown() {
      // ……
    }  
    

    CountDownLatch 的应用场景

    我们考虑一个场景:用户购买一个商品下单成功后,我们会给用户发送各种消息提示用户『购买成功』,比如发送邮件、微信消息、短信等。所有的消息都发送成功后,我们在后台记录一条消息表示成功。

    当然我们可以使用单线程去完成,逐个完成每个操作,如下图所示:

    但是这样效率就会非常低。如何解决单线程效率低的问题?当然是通过多线程啦。

    使用多线程也会遇到一个问题,子线程消息还没发送完,主线程可能就已经打出『所有的消息都已经发送完毕啦』,这在逻辑上肯定是不对的。我们期望所有子线程发完消息主线程才会打印消息,怎么实现呢?CountDownLatch就可以解决这一类问题。

    我们使用代码实现上面的需求。

    import java.util.concurrent.*;
    
    public class OrderServiceDemo {
    
        public static void main(String[] args) throws InterruptedException {
            System.out.println("main thread: Success to place an order");
    
            int count = 3;
            CountDownLatch countDownLatch = new CountDownLatch(count);
    
            Executor executor = Executors.newFixedThreadPool(count);
            executor.execute(new MessageTask("email", countDownLatch));
            executor.execute(new MessageTask("wechat", countDownLatch));
            executor.execute(new MessageTask("sms", countDownLatch));
    
            // 主线程阻塞,等待所有子线程发完消息
            countDownLatch.await();
            // 所有子线程已经发完消息,计数器为0,主线程恢复
            System.out.println("main thread: all message has been sent");
        }
    
        static class MessageTask implements Runnable {
            private String messageName;
            private CountDownLatch countDownLatch;
    
            public MessageTask(String messageName, CountDownLatch countDownLatch) {
                this.messageName = messageName;
                this.countDownLatch = countDownLatch;
            }
    
            @Override
            public void run() {
                try {
                    // 线程发送消息
                    System.out.println("Send " + messageName);
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } finally {
                    // 发完消息计数器减 1
                    countDownLatch.countDown();
                }
            }
        }
    }
    

    程序运行结果:

    main thread: Success to place an order
    Send email
    Send wechat
    Send sms
    main thread: all message has been sent
    

    从运行结果可以看到主线程是在所有的子线程发送完消息后才打印,这符合我们的预期。

    CountDownLatch 的限制

    CountDownLatch是一次性的,计算器的值只能在构造方法中初始化一次,之后没有任何机制再次对其设置值,当CountDownLatch使用完毕后,它不能再次被使用。

    大家学会了么?后面会接着讲剩余的几种并发工具类,拭目以待吧~

    我是雷小帅,爱了~

    展开全文
  • 种并发编程模型简介

    千次阅读 2019-08-29 18:25:11
    本文来自网易云社区 概述 并发往往和并行一起被提及,但是我们应该...并发可以构造出一问题解决方法,该方法能够被用于并行化,从而让原本只能串行处理的事务并行化,更好地发挥出当前多核CPU,分布式集群的能...
  • Java实现并发几种方法

    千次阅读 2019-06-08 20:37:51
    Java实现并发几种方法 Java实现并发几种方法synchronizedVolatileThreads 和 RunnableThread poolsCompletableFuture ) Java实现并发几种方法 Java程序默认以单线程方式运行。 synchronized Java 用过...
  • c#并发编程经典实例

    2018-01-17 14:22:18
    全书分为几大部分:首先介绍几种并发编程技术,包括异步编程、并行编程、TPL数据流、响应式编程;然后阐述一些重要的知识点,包括测试技巧、互操作、取消并发、函数式编程与OOP、同步、调度;最后介绍了几个实用技巧...
  • 并发编程面试题(2020最新版)

    万次阅读 多人点赞 2020-03-14 17:28:01
    文章目录基础知识并发编程的优缺点为什么要使用并发编程并发编程的优点)并发编程有什么缺点并发编程三要素是什么?在 Java 程序中怎么保证多线程的运行安全?并行和并发有什么区别?什么是多线程,多线程的优劣?...
  • 常见有几种场景: 高优先级的线程一直在运行消耗CPU,所有的低优先级线程一直处于等待; 一些线程被永久堵塞在一个等待进入同步块的状态,而其他线程总是能在它之前持续地对该同步块进行访问; 有一个非常经典的饥饿...
  • java并发编程三剑客

    2020-12-21 10:52:43
    目录java并发编程三剑客思维导图CountDownLatch用法构造器以及方法构造器主要方法使用方法CyclicBarrier用法构造器以及主要方法构造器主要方法使用方法Semaphore用法构造器和主要方法构造器主要方法使用方法辅助...
  • 解决高并发几种方法

    万次阅读 2017-08-07 11:00:26
    二、使用高性能的服务器、高性能的数据库、高效率的编程语言、还有高性能的Web容器. 三、使用Ngnix负载均衡 在电商项目中,会有某一件商品许多用户去访问,这个时候就会产生高并发,我解决的方式就是使用redis缓存去...
  • C# 5.0并发编程

    2016-01-15 18:03:05
    首先介绍几种并发编程技术,包括异步编程、并行编程、 TPL 数据流、响应式编程 ;然后阐述一些重要的知识点,包括测试技巧、互操作、取消并发、函数式编程与 OOP、同步、调度;最后介绍了几个实用技巧。全书共包含 ...
  • 这里对python支持的几种并发方式进行简单的总结。 Python支持的并发分为多线程并发与多进程并发(异步IO本文不涉及)。概念上来说,多进程并发即运行多个独立的程序,优势在于并发处理的任务都由操作系统管理,不足...
  • NET并发编程经典实例

    2018-02-08 16:49:52
    全书分为几大部分:首先介绍几种并发编程技术,包括异步编程、并行编程、TPL数据流、响应式编程;然后阐述一些重要的知识点,包括测试技巧、互操作、取消并发、函数式编程与OOP、同步、调度;最后介绍了几个实用技巧...
  • C# 并发编程经典

    2018-08-23 15:33:08
    首先介绍几种并发编程技术,包括异步编程、并行编程、TPL数据流、响应式编程 ;然后阐述一 些重要的知识点,包括测试技巧、互操作、取消并发、函数式编程与OOP、同步、调度;最后介 绍了几个实用技巧。全书共包含70...
  • 并发编程与高并发解决方案(一):并发编程相关基础知识 【原文链接】www.ronglexie.top 目录 基本概念 CPU多级缓存 CPU多级缓存-缓存一致性协议(MESI) MESI协议中的状态 MESI状态转换图 CPU多级缓存-乱序...
  • Java并发编程的艺术-并发编程基础

    千次阅读 2019-06-22 00:15:17
    Java从诞生开始就明智地选择了内置对多线程的支持,这使得Java语言相比同一时期的其他...本章将着重介绍Java并发编程的基础知识,从启动一个线程到线程间不同的通信方式,最后通过简单的线程池示例以及应用(简单的...
  • 并发编程的优缺点

    万次阅读 多人点赞 2019-10-03 21:50:50
    Java并发编程是整个Java开发体系中最难以理解,但也是最重要的知识点之一,因此学习起来比较费劲,从而导致很多人望而却步,但是无论是职场面试还是高并发高流量的系统的实现都离不开并发编程,能够真正掌握并发编程...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 228,126
精华内容 91,250
关键字:

并发编程的几种方法