首页 文章 精选 留言 我的

精选列表

搜索[解决方案],共10002篇文章
优秀的个人博客,低调大师

AliFlutter图片解决方案与优化

Flutter与Native混合开发将是接下来很长时间的主流开发方式。一套稳定、高效、与官方体系无缝融合的外接图片缓存方案是必不可少的。在AliFlutter系列第三场直播中,由阿里巴巴新零售淘系技术部无线开发专家王乾元为大家介绍AliFlutter提供的适合混合应用的外接图片库方案。首先对Flutter官方原生方案进行了分析,并提出了AliFlutter方案的切入点以及具体优化手段。 演讲嘉宾简介:王乾元,花名神漠,13年加入阿里,先后负责过天猫、支付宝、手机淘宝App的iOS架构工作。目前在AliFlutter团队负责基础组件、iOS架构,以及引擎、工具链等方面的研究。以下内容根据演讲视频以及PPT整理而成。观看回放http://mudu.tv/watch/5624777 本次分享主要围绕以下三个方面: 一、

优秀的个人博客,低调大师

JAVA中锁的解决方案

前言 在上一节中,我们给大家介绍了什么是锁,以及锁的使用场景,我相信大家对锁的定义,以及锁的重要性都有了比较清晰的认识。在这一节中,我们会给大家继续做深入的介绍,介绍JAVA为我们提供的不同种类的锁。 JAVA为我们提供了种类丰富的锁,每种锁都有不同的特性,锁的使用场景也各不相同。由于篇幅有限,在这里只给大家介绍比较常用的几种锁。我们会通过锁的定义,核心代码剖析,以及使用场景来给大家介绍JAVA中主流的几种锁。 乐观锁 与 悲观锁 乐观锁与悲观锁应该是每个开发人员最先接触的两种锁。小编最早接触的就是这两种锁,但是不是在JAVA中接触的,而是在数据库当中。当时的应用场景主要是在更新数据的时候,更新数据这个场景也是使用锁的非常主要的场景之一。更新数据的主要流程如下: 检索出要更新的数据,供操作人员查看; 操作人员更改需要修改的数值; 点击保存,更新数据; 这个流程看似简单,但是我们用多线程的思维去考虑,这也应该算是一种互联网思维吧,就会发现其中隐藏着问题。我们具体看一下, A检索出数据; B检索出数据; B修改了数据; A修改数据,系统会修改成功吗? 当然啦,A修改成功与否,要看程序怎么写。咱们抛开程序,从常理考虑,A保存数据的时候,系统要给提示,说“您修改的数据已被其他人修改过,请重新查询确认”。那么我们程序中怎么实现呢? 在检索数据,将数据的版本号(version)或者最后更新时间一并检索出来; 操作员更改数据以后,点击保存,在数据库执行update操作; 执行update操作时,用步骤1检索出的版本号或者最后更新时间与数据库中的记录作比较; 如果版本号或最后更新时间一致,则可以更新; 如果不一致,就要给出上面的提示; 上述的流程就是乐观锁的实现方式。在JAVA中乐观锁并没有确定的方法,或者关键字,它只是一个处理的流程、策略。咱们看懂上面的例子之后,再来看看JAVA中乐观锁。 乐观锁呢,它是假设一个线程在取数据的时候不会被其他线程更改数据,就像上面的例子那样,但是在更新数据的时候会校验数据有没有被修改过。它是一种比较交换的机制,简称CAS(Compare And Swap)机制。一旦检测到有冲突产生,也就是上面说到的版本号或者最后更新时间不一致,它就会进行重试,直到没有冲突为止。 乐观锁的机制如图所示: 咱们看一下JAVA中最常用的i++,咱们思考一个问题,i++它的执行顺序是什么样子的?它是线程安全的吗?当多个线程并发执行i++的时候,会不会有问题?接下来咱们通过程序看一下: public class Test { private int i=0; public static void main(String[] args) { Test test = new Test(); //线程池:50个线程 ExecutorService es = Executors.newFixedThreadPool(50); //闭锁 CountDownLatch cdl = new CountDownLatch(5000); for (int i = 0;i < 5000; i++){ es.execute(()->{ test.i++; cdl.countDown(); }); } es.shutdown(); try { //等待5000个任务执行完成后,打印出执行结果 cdl.await(); System.out.println("执行完成后,i="+test.i); } catch (InterruptedException e) { e.printStackTrace(); } } } 上面的程序中,我们模拟了50个线程同时执行i++,总共执行5000次,按照常规的理解,得到的结果应该是5000,我们运行一下程序,看看执行的结果如何? 执行完成后,i=4975 执行完成后,i=4986 执行完成后,i=4971 这是我们运行3次以后得到的结果,可以看到每次执行的结果都不一样,而且不是5000,这是为什么呢?这就说明i++并不是一个原子性的操作,在多线程的情况下并不安全。我们把i++的详细执行步骤拆解一下: 从内存中取出i的当前值; 将i的值加1; 将计算好的值放入到内存当中; 这个流程和我们上面讲解的数据库的操作流程是一样的。在多线程的场景下,我们可以想象一下,线程A和线程B同时从内存取出i的值,假如i的值是1000,然后线程A和线程B再同时执行+1的操作,然后把值再放入内存当中,这时,内存中的值是1001,而我们期望的是1002,正是这个原因导致了上面的错误。那么我们如何解决呢?在JAVA1.5以后,JDK官方提供了大量的原子类,这些类的内部都是基于CAS机制的,也就是使用了乐观锁。我们将上面的程序稍微改造一下,如下: public class Test { private AtomicInteger i = new AtomicInteger(0); public static void main(String[] args) { Test test = new Test(); ExecutorService es = Executors.newFixedThreadPool(50); CountDownLatch cdl = new CountDownLatch(5000); for (int i = 0;i < 5000; i++){ es.execute(()->{ test.i.incrementAndGet(); cdl.countDown(); }); } es.shutdown(); try { cdl.await(); System.out.println("执行完成后,i="+test.i); } catch (InterruptedException e) { e.printStackTrace(); } } } 我们将变量i的类型改为AtomicInteger,AtomicInteger是一个原子类。我们在之前调用i++的地方改成了i.incrementAndGet(),incrementAndGet()方法采用了CAS机制,也就是说使用了乐观锁。我们再运行一下程序,看看结果如何。 执行完成后,i=5000 执行完成后,i=5000 执行完成后,i=5000 我们同样执行了3次,3次的结果都是5000,符合了我们预期。这个就是乐观锁。我们对乐观锁稍加总结,乐观锁在读取数据的时候不做任何限制,而是在更新数据的时候,进行数据的比较,保证数据的版本一致时再更新数据。根据它的这个特点,可以看出乐观锁适用于读操作多,而写操作少的场景。 悲观锁与乐观锁恰恰相反,悲观锁从读取数据的时候就显示的加锁,直到数据更新完成,释放锁为止。在这期间只能有一个线程去操作,其他的线程只能等待。在JAVA中,悲观锁可以使用synchronized关键字或者ReentrantLock类来实现。还是上面的例子,我们分别使用这两种方式来实现一下。首先是使用synchronized关键字来实现: public class Test { private int i=0; public static void main(String[] args) { Test test = new Test(); ExecutorService es = Executors.newFixedThreadPool(50); CountDownLatch cdl = new CountDownLatch(5000); for (int i = 0;i < 5000; i++){ es.execute(()->{ //修改部分 开始 synchronized (test){ test.i++; } //修改部分 结束 cdl.countDown(); }); } es.shutdown(); try { cdl.await(); System.out.println("执行完成后,i="+test.i); } catch (InterruptedException e) { e.printStackTrace(); } } } 我们唯一的改动就是增加了synchronized块,它锁住的对象是test,在所有线程中,谁获得了test对象的锁,谁才能执行i++操作。我们使用了synchronized悲观锁的方式,使得i++线程安全。我们运行一下,看看结果如何。 执行完成后,i=5000 执行完成后,i=5000 执行完成后,i=5000 我们运行3次,结果都是5000,符合预期。接下来,我们再使用ReentrantLock类来实现悲观锁。代码如下: public class Test { //添加了ReentrantLock锁 Lock lock = new ReentrantLock(); private int i=0; public static void main(String[] args) { Test test = new Test(); ExecutorService es = Executors.newFixedThreadPool(50); CountDownLatch cdl = new CountDownLatch(5000); for (int i = 0;i < 5000; i++){ es.execute(()->{ //修改部分 开始 test.lock.lock(); test.i++; test.lock.unlock(); //修改部分 结束 cdl.countDown(); }); } es.shutdown(); try { cdl.await(); System.out.println("执行完成后,i="+test.i); } catch (InterruptedException e) { e.printStackTrace(); } } } 我们在类中显示的增加了Lock lock = new ReentrantLock();,而且在i++之前增加了lock.lock(),加锁操作,在i++之后增加了lock.unlock()释放锁的操作。我们同样运行3次,看看结果。 执行完成后,i=5000 执行完成后,i=5000 执行完成后,i=5000 3次运行结果都是5000,完全符合预期。我们再来总结一下悲观锁,悲观锁从读取数据的时候就加了锁,而且在更新数据的时候,保证只有一个线程在执行更新操作,没有像乐观锁那样进行数据版本的比较。所以乐观锁适用于读相对少,写相对多的操作。 公平锁 与 非公平锁 前面我们介绍了乐观锁与悲观锁,这一小节我们将从另外一个维度去讲解锁——公平锁与非公平锁。从名字不难看出,公平锁在多线程情况下,对待每一个线程都是公平的;而非公平锁恰好与之相反。从字面上理解还是有些晦涩难懂,我们还是举例说明,场景还是去超市买东西,在储物柜存储东西的例子。储物柜只有一个,同时来了3个人使用储物柜,这时A先抢到了柜子,A去使用,B和C自觉进行排队。A使用完以后,后面排队中的第一个人将继续使用柜子,这就是公平锁。在公平锁当中,所有的线程都自觉排队,一个线程执行完以后,排在后面的线程继续使用。 非公平锁则不然,A在使用柜子的时候,B和C并不会排队,A使用完以后,将柜子的钥匙往后一抛,B和C谁抢到了谁用,甚至可能突然跑来一个D,这个D抢到了钥匙,那么D将使用柜子,这个就是非公平锁。 公平锁如图所示: 多个线程同时执行方法,线程A抢到了锁,A可以执行方法。其他线程则在队列里进行排队,A执行完方法后,会从队列里取出下一个线程B,再去执行方法。以此类推,对于每一个线程来说都是公平的,不会存在后加入的线程先执行的情况。 非公平锁入下图所示: 多个线程同时执行方法,线程A抢到了锁,A可以执行方法。其他的线程并没有排队,A执行完方法,释放锁后,其他的线程谁抢到了锁,谁去执行方法。会存在后加入的线程,反而先抢到锁的情况。 公平锁与非公平锁都在ReentrantLock类里给出了实现,我们看一下ReentrantLock的源码。 /** * Creates an instance of {@code ReentrantLock}. * This is equivalent to using {@code ReentrantLock(false)}. */ public ReentrantLock() { sync = new NonfairSync(); } /** * Creates an instance of {@code ReentrantLock} with the * given fairness policy. * * @param fair {@code true} if this lock should use a fair ordering policy */ public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); } ReentrantLock有两个构造方法,默认的构造方法中,sync = new NonfairSync();我们可以从字面意思看出它是一个非公平锁。再看看第二个构造方法,它需要传入一个参数,参数是一个布尔型,true是公平锁,false是非公平锁。从上面的源码我们可以看出sync有两个实现类,分别是FairSync和NonfairSync,我们再看看获取锁的核心方法,首先是公平锁FairSync的, @ReservedStackAccess protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } 然后是非公平锁NonfairSync的, @ReservedStackAccess final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } 通过对比两个方法,我们可以看出唯一的不同之处在于!hasQueuedPredecessors()这个方法,很明显这个方法是一个队列,由此可以推断,公平锁是将所有的线程放在一个队列中,一个线程执行完成后,从队列中取出下一个线程,而非公平锁则没有这个队列。这些都是公平锁与非公平锁底层的实现原理,我们在使用的时候不用追到这么深层次的代码,只需要了解公平锁与非公平锁的含义,并且在调用构造方法时,传入true和false即可。 总结 JAVA中锁的种类非常多,在这一节中,我们找了非常典型的几个锁的类型给大家做了介绍。乐观锁与悲观锁是最基础的,也是大家必须掌握的。大家在工作中不可避免的都要使用到乐观锁和悲观锁。从公平锁与非公平锁这个维度上看,大家平时使用的都是非公平锁,这也是默认的锁的类型。如果要使用公平锁,大家可以在秒杀的场景下使用,在秒杀的场景下,是遵循先到先得的原则,是需要排队的,所以这种场景下是最适合使用公平锁的。 如想深入了解,请关注我们的《架构师课程》

优秀的个人博客,低调大师

iframe父子页面通讯解决方案

有两点要注意 onload加载完成再进行操作 子页面延时获取 场景是这样的,父级页面嵌套了多个iframe,report_six.html子级页面按需加载数量,且每个子页面渲染不同的数据。 父页面for循环加载子页面数量,动态生成id,根据对应的id,传值给对应的子级页面元素,一定要写在onload里面 下面是父页面 <body> <iframe src="report_one.html" frameborder="0" width="630" height="891"></iframe> <iframe src="report_two.html" frameborder="0" width="630" height="891"></iframe> <iframe src="report_three.html" frameborder="0" width="630" height="891"></iframe> <iframe src="report_four.html" frameborder="0" width="630" height="891"></iframe> <iframe src="report_five.html" frameborder="0" width="630" height="891"></iframe> <iframe id="a_0" src="report_six.html" frameborder="0" width="630" height="891"></iframe> <iframe id="a_1" src="report_six.html" frameborder="0" width="630" height="891"></iframe> <iframe id="a_2" src="report_six.html" frameborder="0" width="630" height="891"></iframe> <iframe id="a_3" src="report_six.html" frameborder="0" width="630" height="891"></iframe> <iframe id="a_4" src="report_six.html" frameborder="0" width="630" height="891"></iframe> <iframe id="report_ten" src="report_ten.html" frameborder="0" width="630" height="891"></iframe> <iframe id="report_eleven" src="report_eleven.html" frameborder="0" width="630" height="891"></iframe> <iframe id="report_nine" src="report_nine.html" frameborder="0" width="630" height="891"></iframe> </body> <script src="./js/jquery-2.1.4.js"></script> <script> if(obj.type == 3){ $('<iframe src="report_one.html" frameborder="0" width="630" height="891"></iframe>').appendTo('body'); $('<iframe src="report_two.html" frameborder="0" width="630" height="891"></iframe>').appendTo('body'); $('<iframe src="report_three.html" frameborder="0" width="630" height="891"></iframe>').appendTo('body'); $('<iframe src="report_four.html" frameborder="0" width="630" height="891"></iframe>').appendTo('body'); $('<iframe src="report_five.html" frameborder="0" width="630" height="891"></iframe>').appendTo('body'); var rank_a = obj.k.length; for(var i = 0;i < rank_a;i++){ $('<iframe id="a_' + i + '" src="report_six.html" frameborder="0" width="630" height="891"></iframe>').appendTo('body'); } $('<iframe id="report_ten" src="report_ten.html" frameborder="0" width="630" height="891"></iframe>').appendTo('body'); $('<iframe id="report_eleven" src="report_eleven.html" frameborder="0" width="630" height="891"></iframe>').appendTo('body'); var cancer = obj.c.length; if(cancer > 0){ $('<iframe id="report_nine" src="report_nine.html" frameborder="0" width="630" height="891"></iframe>').appendTo('body'); } window.onload = function(){ var rank_a = obj.m07.dis_risk.length; for(var i = 0;i < rank_a;i++){ $('#a_' + i).contents().find('#rank').val(i); } $('#report_ten').contents().find('#change').html('四'); $('#report_eleven').contents().find('#change').html('五'); $('#report_nine').contents().find('#change').html('六'); } } </script> 子级页面带有默认值的元素input#rank接收数据,为了确定接收到参数,设置一个定时器,不等于默认值的情况下就表示接收成功,接收成功之后清除定时器执行业务代码 下面是子页面 <body> <section> <div id="mainChart" class="chart"></div> </section> <input type="hidden" id="rank" value="a"/> <script src="./js/echarts-all-3.js"></script> <script> var timer = setInterval(function(){ var rank_value = document.getElementById("rank").value; if(rank_value != 'a'){ clearInterval(timer); //执行代码 } },2000); </script> </body>

优秀的个人博客,低调大师

阿里云 APM 解决方案地图

APM 概述 APM 全称是 Application Performance Management, 是指对应用程序的性能和可用性的监控管理。狭义上的APM单指应用程序的监控,如应用的各接口性能和错误监控,分布式调用链路跟踪,以及其他各类用于诊断(内存,线程等)的监控信息,等;广义上的APM, 除了应用层的监控意外,还包括手机App端监控,页面端监控,容器、服务器监控,以及其他平台组件如中间件容器,数据库等层面的监控。 APM是近5年来伴随着云技术、微服务架构发展起来的一个新兴监控领域。在国内外,无论是云厂商(如AWS, Azure,等)还是独立的公司(Dynatrace, Appdynamics,等),都有着非常优秀的APM产品。 阿里云作为国内最大,世界排名前三的云厂商,其在APM领域也有很多优秀的产品提供,整个产品家族也比较全面。

优秀的个人博客,低调大师

VR直播云服务解决方案

VR直播 通过VR(虚拟现实)技术,用户通过佩戴相关硬件设备,通过平台提供的APP进行直播观看。主播需采用360°全景的拍摄设备,捕捉多角度画面,进行多画面传输后,观看者可通过任意角度进行对直播体验的观察,使得观看者能更加身临其境。 行业背景 VR概念早在1989年被Jaron Lanier所提出,早期主要就是做头显设备,市场上很少有人关注。 2016年,VR+直播的应用场景开始流行。 1)消费级VR设备,包括全景相机、VR眼镜、头显大量涌现。 2)2016年,直播行业从爆发逐渐进入红海,各直播平台借助VR技术,开创VR直播新玩法,希望再次占领舆论风口 行业趋势 2016年VR直播开始流行时,行业普遍看好这种新玩法,认为能够通过沉浸式直播观看体验吸引用户,各直播平台争相引入VR直播。有些创业公司甚至主打VR直播,比如小花秀。 发展1年,VR直播并没

优秀的个人博客,低调大师

嵌入式流程解决方案

一.需求分析由于企业业务的独特性或者企业高层独特的管理思想,很多客户选择了自行开发业务系统的方式来实现独有的竞争力。这类信息系统通常经过了多年的开发,伴随着企业的发展一直在不断优化,与企业的业务非常匹配。然而,近几年流程管理思想和技术的不断兴起,这类系统由于规划时间早,对流程的支持非常弱,因此,很多客户期望通过集成第三方的流程管理产品,在业务系统尽量少调整的前提下,嵌入工作流,实现业务系统的工作流驱动。二.方案实现 以H3 BPM流程引擎为基础,企业自主开发的业务系统通过H3 BPM开放的Webservice接口、 API接口、功能控件等,将流程引擎集成到业务系统,实现业务系统的工作流驱动。 H3 BPM采用微软.net技术架构,如果客户业务系统同样采用.net,可在项目中直接引用H3 BPM的 程序集和控件集,把H3 BPM流 程引擎作为基础构建来使用,如下图: 这样,可以充分的运用H3 BPM进百个控件和所有的API函数,H3 BPM有近 600页的API库,可以完成几乎所有的流程操作,如下图: 旧系统如果不是.net系统,可以采用WebService的方式,H3 BPM给常用 接口封装了WebService接口,包括流程发起、任务提交、任务打回、任务转交、撤销流程等等。如下图: 三.方案价值 本文转自 lwl_BPM 51CTO博客,原文链接:http://blog.51cto.com/12438115/1913512,如需转载请自行联系原作者

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册