首页 文章 精选 留言 我的

精选列表

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

【读书笔记《Android游戏编程之从零开始》】5.Android 游戏开发常用的系统控件(ProgressBar、Seekbar)

3.7 ProgressBar ProgressBar类官方文档地址:http://developer.android.com/reference/android/widget/ProgressBar.html 在Android应用开发中,ProgressBar(运行进度条)是比较常用到的组件,例如下载进度、安装程序进度、加载资源进度显示等。在Android中提供了两种样式来分别表示在不同状态下显示的进度条,下面来实现这两种样式。 默认进度条是圆形,通过style属性来指定系统进度条的大小: style="?android:attr/progressBarStyleSmall",小圆形进度条 style="?android:attr/progressBarStyleLarge",大圆形进度条 如果需要将进度条显示为长条形,那么style必须设定为这种类型: style="?android:attr/progressBarStyleHorizontal",长条形进度条 针对长条形进度条,还有几个常用属性: android:max,设置进度条最大进度值 android:progress,设置进度条出事进度值。 android:secondaryProgress,设置底层(浅色)进度值 圆形显示进度条默认是动态,但是长条进度条却是静态的,那么修改源代码MainActivity实现长条进度条为动态显示: ProgressBar类中常用函数如下所示: getProgress();获取当前进度值; setProgress();设置进度值; getSecondaryProgress();获取底层进度值 setSecondaryProgress();设置底层进度值 getMax();获取当前最大进度值 在线程循环中对进度条的最大进度值与当前进度值进行判断处理,然后不断设置进度值进而达到动态进度值越来越大,或越来越小的动态效果。 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/txt_ProgressBar" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="默认进度条:" /> <ProgressBar android:id="@+id/pb1" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="小圆形进度条:" /> <ProgressBar android:id="@+id/pb2" style="?android:attr/progressBarStyleSmall" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="大圆形进度条:" /> <ProgressBar android:id="@+id/pb3" style="?android:attr/progressBarStyleLarge" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="条形进度条:" /> <ProgressBar android:id="@+id/pb4" style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:max="100" android:progress="50" android:secondaryProgress="70" /> </LinearLayout> import android.app.Activity; import android.os.Bundle; import android.widget.ProgressBar; public class MainActivity extends Activity implements Runnable { private Thread th; // 声明一个线程 private ProgressBar pb;// 声明一个进度条对象 private Boolean stateChage = false;// 标识进度值的最大最小的状态 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 实例化进度条对象 pb = (ProgressBar) findViewById(R.id.pb4); th = new Thread(this);// 实例化线程对象 th.start();// 启动线程 } @Override public void run() {// 实现Runnable接口抽象函数 while (true) { int current = pb.getProgress();// 得到当前的进度值 int currentMax = pb.getMax();// 得到进度条的最大进度值 // int secCurrent = pb.getSecondaryProgress();// 得到底层当前进度值 // 一下代码实现进度值越大,越来越小的一个动态效果 if (stateChage == false) { if (current >= currentMax) { stateChage = true; } else { // 设置进度值 pb.setProgress(current + 1); // 设置底层进度值 pb.setSecondaryProgress(current + 1); } } else { if (current <= 0) { stateChage = false; } else { pb.setProgress(current - 1); } } try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } } } 自定义圆形进度条样式的方法:方法1:通过一张图片填充android:indeterminateDrawable 准备图片: progress_load.png 在drawable下新建image_progress_01.xml文件 <?xml version="1.0" encoding="utf-8"?> <rotate xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/progress_load" android:fromDegrees="0" android:pivotX="50%" android:pivotY="50%" android:toDegrees="360" /> 在 \value\style.xml中定义myProgressBarStyle <style name="myProgressBarStyle" > <item name="android:indeterminateDrawable">@drawable/image_progress_01</item> <item name="android:minWidth">100dip</item> <item name="android:maxWidth">100dip</item> <item name="android:minHeight">100dip</item> <item name="android:maxHeight">100dip</item> </style> 最后在ProgressBar中使用我们自己定义的style,android:indeterminateDuration="700"指定图片旋转的速度,这样我们就可以根据自己的需要来定义ProgressBar的样式。 <ProgressBar android:id="@+id/pb1" style="@style/myProgressBarStyle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:indeterminateDuration="700" /> 方法2:定义一个动画来实现 准备图片:photo1.jpg、photo2.jpg、photo3.jpg、photo4.jpg、photo5.jpg 定义res/anim/image_progress.xml如下: <?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false"> <item android:duration="1000" android:drawable="@drawable/photo1" /> <item android:duration="1000" android:drawable="@drawable/photo2" /> <item android:duration="1000" android:drawable="@drawable/photo3" /> <item android:duration="1000" android:drawable="@drawable/photo4" /> <item android:duration="1000" android:drawable="@drawable/photo5" /> </animation-list> 在我们定义的style中引入<item name="android:indeterminateDrawable">@anim/image_progress.xml</item> <style name="myProgressBarStyle2" > <item name="android:indeterminateDrawable">@anim/image_progress</item> <item name="android:minWidth">200dip</item> <item name="android:maxWidth">200dip</item> <item name="android:minHeight">200dip</item> <item name="android:maxHeight">200dip</item> </style> 最后在ProgressBar中使用我们自己定义的style style="@style/myProgressBarStyle2" 方法3:自定义颜色来实现 定义res/drawable/image_progress_02.xml如下: <?xml version="1.0" encoding="utf-8"?> <rotate xmlns:android="http://schemas.android.com/apk/res/android" android:fromDegrees="0" android:pivotX="50%" android:pivotY="50%" android:toDegrees="360" > <shape android:innerRadiusRatio="3" android:shape="ring" android:thicknessRatio="8" android:useLevel="false" > <gradient android:centerColor="#FFFFFF" android:centerY="0.50" android:endColor="#1E90FF" android:startColor="#000000" android:type="sweep" android:useLevel="false" /> </shape> </rotate> 在我们定义的style中引入<item name="android:indeterminateDrawable">@drawable/image_progress_02</item> <style name="myProgressBarStyle3" > <item name="android:indeterminateDrawable">@drawable/image_progress_02</item> <item name="android:minWidth">100dip</item> <item name="android:maxWidth">100dip</item> <item name="android:minHeight">100dip</item> <item name="android:maxHeight">100dip</item> </style> 最后在ProgressBar中使用我们自己定义的style style="@style/myProgressBarStyle3" 自定义长条形进度条样式: 原理:在XML文件中分别定义进度条背景、第一进度颜色、第二进度颜色,然后在ProgressBar的android:progressDrawable属性应用即可。 先在drawable下建立progressbar_style.xml文件,内容如下: <?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@android:id/background"> <shape> <corners android:radius="5.0dip" /> <gradient android:startColor="#656666" android:endColor="#dbdedf" android:angle="270.0" android:centerY="0.75" android:centerColor="#bbbbbc" /> </shape> </item> <item android:id="@android:id/secondaryProgress"> <clip> <shape> <corners android:radius="8.0dip" /> <gradient android:startColor="#e71a5e" android:endColor="#6c213a" android:angle="90.0" android:centerY="0.75" android:centerColor="#ac6079" /> </shape> </clip> </item> <item android:id="@android:id/progress"> <clip> <shape> <corners android:radius="8.0dip" /> <gradient android:startColor="#464647" android:endColor="#2d9ae7" android:angle="270.0" /> </shape> </clip> </item> </layer-list> 分别定义背景,第一进度颜色,第二进度颜色gradient定义的是渐变,corners定义的是圆角布局中: <ProgressBar android:id="@+id/pb1" style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:max="100" android:progress="50" android:progressDrawable="@drawable/image_progress_03" android:secondaryProgress="70" /> MainActivity.java文件: import android.app.Activity; import android.os.Bundle; import android.widget.ProgressBar; public class MainActivity extends Activity implements Runnable { private Thread th; // 声明一个线程 private ProgressBar pb;// 声明一个进度条对象 private Boolean stateChage = false;// 标识进度值的最大最小的状态 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 实例化进度条对象 pb = (ProgressBar) findViewById(R.id.pb1); th = new Thread(this);// 实例化线程对象 th.start();// 启动线程 } @Override public void run() {// 实现Runnable接口抽象函数 while (true) { int current = pb.getProgress();// 得到当前的进度值 int currentMax = pb.getMax();// 得到进度条的最大进度值 // int secCurrent = pb.getSecondaryProgress();// 得到底层当前进度值 // 一下代码实现进度值越大,越来越小的一个动态效果 if (stateChage == false) { if (current >= currentMax) { stateChage = true; } else { // 设置进度值 pb.setProgress(current + 1); // 设置底层进度值 pb.setSecondaryProgress(current + 1); } } else { current =0; pb.setProgress(current); stateChage = false; } try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } } } 3.8 Seekbar SeekBar类官方文档地址:http://developer.android.com/reference/android/widget/SeekBar.html SeekBar(拖动条)的外观类似长条进度条。Android手机上最常见到拖动条的地方就是在播放音乐的时候,当用户在拖动条上任意拖动可以调整音乐播放的时间段;调整铃声音量大小界面也是利用拖动条与用户进行交互。下面来学习如何定义和监听拖动条事件。 对拖动条进行监听的是setOnSeekBarChangeListener这个接口,这里使用的内部类实现绑定监听,然后重写接口的三个函数: onStopTrackingTouch:当用户对拖动条的拖动动作完成时触发; onStartTrackingTouch:当用户对拖动条进行拖动时触发; onProgressChanged:当拖动条的值发生改变时触发。 其实拖动条类似长条提示进度条,也拥有setMax()、setProgress()、setSecondaryProgress()这些函数。 简单示例如下: . <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/tv" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/txt_SeekBar" /> <SeekBar android:id="@+id/skb" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout> import android.app.Activity; import android.os.Bundle; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; public class MainActivity extends Activity { private SeekBar skb; private TextView tv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); skb = (SeekBar) findViewById(R.id.skb); tv = (TextView) findViewById(R.id.tv); skb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { @Override public void onStopTrackingTouch(SeekBar seekBar) { tv.setText("“拖动条”完成拖动。"); } @Override public void onStartTrackingTouch(SeekBar seekBar) { tv.setText("“拖动条”拖动中。。。"); } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { tv.setText("当前“拖动条”的值为" + progress); } }); } } 自定义拖动条样式: SeekBar的系统有提供一种样式如下: style="@android:style/Widget.SeekBar" 在android的源码目录\platforms\android-18\data\res\values\styles.xml下,我找到了Widget.SeekBar的代码 <style name="Widget.SeekBar"> <item name="android:indeterminateOnly">false</item> <item name="android:progressDrawable">@android:drawable/progress_horizontal</item> <item name="android:indeterminateDrawable">@android:drawable/progress_horizontal</item> <item name="android:minHeight">20dip</item> <item name="android:maxHeight">20dip</item> <item name="android:thumb">@android:drawable/seek_thumb</item> <item name="android:thumbOffset">8dip</item> <item name="android:focusable">true</item> <item name="android:mirrorForRtl">true</item> </style> 这里最重要的几个属性: <!-- 背景,进度条,次进度条的设置。 --> <item name="android:progressDrawable">@android:drawable/progress_horizontal</item> <!-- 背景,进度条,次进度条的设置【进度不确定】。 --> <item name="android:indeterminateDrawable">@android:drawable/progress_horizontal</item> <!-- 拖拽的图标。 --> <item name="android:thumb">@android:drawable/seek_thumb</item> 上面有indeterminate这个概念,是指进度不确定。这种情形下,进度会来回快闪。Seekbar默认样式是进度确定的,拖拽到具体位置就停止。图片资源progress_horizontal相当关键,它不是单一的图片。在\platforms\android-18\data\res\drawable下,有progress_horizontal.xml: <?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@android:id/background"> <shape> <corners android:radius="5dip" /> <gradient android:angle="270" android:centerColor="#ff5a5d5a" android:centerY="0.75" android:endColor="#ff747674" android:startColor="#ff9d9e9d" /> </shape> </item> <item android:id="@android:id/secondaryProgress"> <clip> <shape> <corners android:radius="5dip" /> <gradient android:angle="270" android:centerColor="#80ffb600" android:centerY="0.75" android:endColor="#a0ffcb00" android:startColor="#80ffd300" /> </shape> </clip> </item> <item android:id="@android:id/progress"> <clip> <shape> <corners android:radius="5dip" /> <gradient android:angle="270" android:centerColor="#ffffb600" android:centerY="0.75" android:endColor="#ffffcb00" android:startColor="#ffffd300" /> </shape> </clip> </item> </layer-list> 上面其实就是三张自定义色彩渐变的图片。我们可以依葫芦画瓢定义自己的三张图片(背景图有点淡,gif截图看不清楚,不过大家知道是那样就行了): my_seekbar.xml <?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android" > <!-- 背景样式 --> <item android:id="@android:id/background" android:drawable="@drawable/my_seekbar_no"> </item> <!-- 次拖动条样式 --> <item android:id="@android:id/secondaryProgress"> <scale android:drawable="@drawable/my_seekbar_back" android:scaleWidth="100%" /> </item> <!-- 拖动后的样式 --> <item android:id="@android:id/progress"> <scale android:drawable="@drawable/my_seekbar_sh" android:scaleWidth="100%" /> </item> </layer-list> <style name="mySeekBar"> <item name="android:minHeight">20dip</item> <item name="android:maxHeight">20dip</item> <item name="android:progressDrawable">@drawable/my_seekbar</item> <item name="android:indeterminateDrawable">@drawable/my_seekbar</item> <item name="android:thumb">@drawable/ic_launcher</item> <item name="android:thumbOffset">8dip</item> </style> <SeekBar android:id="@+id/skb" style="@style/mySeekBar" android:layout_width="match_parent" android:layout_height="wrap_content" android:secondaryProgress="50" android:max="100" android:progress="10" /> 拖拽的小图标是由thumb属性决定的。只要搞清楚以上的东东,SeekBar做成什么样就看自己了。 本文转自叶超Luka博客园博客,原文链接:http://www.cnblogs.com/yc-755909659/p/3737975.html,如需转载请自行联系原作者

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

巡检分析智能体解决方案

--基于L4级全自治运维体系深度重构 本方案以企业核心大模型为底座,融合OpenClaw多智能体自治框架与Hermes深度推理引擎,面向云原生、分布式、微服务架构下的巡检痛点,构建具备全域感知、深度推理、自主决策、闭环自愈、持续进化的L4级全自治巡检分析智能体体系,实现运维巡检从“人工抽样、被动响应”向“全域覆盖、主动预防、无人自治”的代际升级。 一、现状与痛点:传统巡检已成为稳定性瓶颈 在云原生与微服务规模化落地背景下,传统人工/半自动化巡检模式面临效率、覆盖、深度、闭环四大不可逾越瓶颈: 1. 人力成本高、覆盖不全、隐患漏检 海量异构资源(IaaS/PaaS/SaaS)依赖人工巡检,只能抽样、低频、浅层核查,隐性隐患(内存泄漏、配置漂移、连接缓慢增长、指标渐变劣化) 无法被发现,最终演变为线上故障。 2. 数据孤岛严重,无法全局诊断 监控、日志、CMDB、链路追踪、业务指标分散割裂,无统一语义与关联分析,人工跨平台核对效率低、易误判,无法形成系统健康度全局画像。 3. 分析深度不足,只“发现”不“诊断” 传统巡检停留在阈值越界判断,缺乏根因推理、关联传导、趋势推演能力,异常定位依赖专家经验,难以应对复杂链路与渐进式风险。 4. 无闭环机制,巡检与处置脱节 巡检报告人工流转、人工处置、人工验证,发现—分析—派单—执行—复盘链条断裂,隐患整改滞后、重复问题频发。 5. 知识无法沉淀,依赖个人经验 专家经验无法产品化、规则化、模型化,人员变动导致能力断层,巡检标准不统一、质量不可控。 二、总体设计理念与架构定位 核心定位 打造企业级自治巡检中枢,以“标准统一、全域感知、深度分析、预测预警、自动闭环、持续进化”为目标,构建L4级全自治能力: L1:辅助提示 L2:半自动执行 L3:可信自治(人在回路) L4:全流程无人闭环、自主进化 设计理念 模型驱动:以大模型+推理引擎实现深度诊断与决策 智能体协同:OpenClaw实现多智能体分工、调度、联动、自愈 数据统一:MCP协议打通全域数据,构建统一运维数据视图 闭环自治:巡检→分析→预警→派单→处置→验证→复盘全自动化 持续进化:知识沉淀→规则迭代→模型优化→能力自生长 总体技术架构 1. 数据接入层:MCP协议对接监控、日志、CMDB、APM、配置平台等 2. 智能体层:巡检执行智能体、指标解析智能体、关联分析智能体、预测推理智能体、处置调度智能体、复盘进化智能体 3. 引擎层:Hermes深度推理引擎、时序预测引擎、反事实推理引擎、知识图谱引擎 4. 协同框架:OpenClaw多智能体调度、任务编排、权限控制、操作审计 5. 应用层:标准化巡检、健康度评估、隐患预警、自动处置、报告输出、知识管理 三、核心能力体系(六大核心能力) 1. 全域标准化巡检能力:统一指标、统一口径、统一模板 构建三层全栈指标模板:

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

拥抱Spring全新OAuth解决方案

以下全文 Spring Authorization Server 简称为: SAS 背景 Spring 团队正式宣布 Spring Security OAuth 停止维护,该项目将不会再进行任何的迭代 目前 Spring 生态中的 OAuth2 授权服务器是 Spring Authorization Server 已经可以正式生产使用 作为 SpringBoot 3.0 的过渡版本 SpringBoot 2.7.0 过期了大量关于 SpringSecurity 的配置类,如沿用旧版本过期配置无法向上升级。 迁移过程 本文以PIG 微服务开发平台为演示,适用于 Spring Security OAuth 2.3 <-> 2.5 的认证中心迁移 ① Java 1.8 支持 目前最新的 SAS 0.3 基于 Java 11 构建,低版本 Java 无法使用 经过和 Spring Security 官方团队的沟通 0.3.1 将继续兼容 Java 1.8 我们联合 springboot 中文社区编译了适配 java 1.8 的版本坐标如下 <dependency> <groupId>io.springboot.security</groupId> <artifactId>spring-security-oauth2-authorization-server</artifactId> <version>0.3.0</version> </dependency> ② 授权模式扩展 扩展支持密码模式,SAS 基于 oauth 2.1 协议不支持密码模式 扩展支持短信登录 ③ Redis 令牌存储 官方目前没有提供基于 Redis 令牌持久化方案 PIG 扩展 PigRedisOAuth2AuthorizationService 支持 ④ Token 输出格式化 使用自省令牌的情况下 默认实现为 ku4R4n7YD1f584KXj4k_3GP9o-HbdY-PDIIh-twPVJTmvHa5mLIoifaNhbBvFNBbse6_wAMcRoOWuVs9qeBWpxQ5zIFrF1A4g1Q7LhVAfH1vo9Uc7WL3SP3u82j0XU5x 为方便结合 redis 高效检索 token ,结合 RDM 分组也可以更方便的图形化观察 统一前缀::令牌类型::客户端ID::用户名::uuid @Bean public OAuth2TokenGenerator oAuth2TokenGenerator() { CustomeOAuth2AccessTokenGenerator accessTokenGenerator = new CustomeOAuth2AccessTokenGenerator(); // 注入Token 增加关联用户信息 accessTokenGenerator.setAccessTokenCustomizer(new CustomeOAuth2TokenCustomizer()); return new DelegatingOAuth2TokenGenerator(accessTokenGenerator, new OAuth2RefreshTokenGenerator()); } ⑤ Token 输出增强 使用自省令牌,默认情况下输出的 Token 格式 { "access_token": "xx", "refresh_token": "xx", "scope": "server", "token_type": "Bearer", "expires_in": 43199 } Token 增强输出关联用户信息 { "sub": "admin", "clientId": "test", "access_token": "xx", "refresh_token": "xx", "license": "https://pig4cloud.com", "user_info": { "username": "admin", "accountNonExpired": true, "accountNonLocked": true, "credentialsNonExpired": true, "enabled": true, "id": 1, "deptId": 1, "phone": "17034642999", "name": "admin", "attributes": {} } } ⑥ 授权码模式个性化 ⑦ 资源服务器 自省方案扩展支持资源资源服务器本地查询 扩展资源服务器本地自省 - 优势: 1. 用户状态实时更新 2. 减少网络调用提升性能 源码: https://github.com/pig-mesh/pig

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

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>

资源下载

更多资源
Mario

Mario

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

腾讯云软件源

腾讯云软件源

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

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册