2021年1月31日星期日

高性能数据导入方案&表过滤器&一对多支持筛选

一、数据导入有哪些难题

该功能可以说100%的开发人员都会遇到,并且非常的常见,比如批量操作,你知道哪条数据错了吗?

你都不知道客户更不知道了

 

1、数据分类

   你需要将 插入、更新、忽略不计、错误数据 等进么分类汇总,最后返回给客户,如果没有很好的设计想把这些操作一步到位非常的难

2、高性能

   对于插入或者更新 肯定不能单纯的插入或者更新,一定要批量操作,或者用到blukcopy操作

3、数据验证

   对于错误数据要进行组装 ,并返回客户,让客户知道哪些字段 哪个数据出现错误

 

二、使用 Storageable 解决难题

我们可以使用SqlSugar ORM中的 Storageable功能来解决上面的几大难题 ,SqlSugar 作为老牌ORM框架一直在创新和更新

 

1、入门示例

如何用Storageable实现简单的插入或者更新

例题1:导入一个List ,当id等0插入, id>0执行更新 (id是主键 )

List<UinitBlukTable> list2 = new List<UinitBlukTable>();list2.Add(new UinitBlukTable() { Id = 1, Name = "a", Create = DateTime.Now });list2.Add(new UinitBlukTable() { Id = 2, Name = "a", Create = DateTime.Now });list2.Add(new UinitBlukTable() { Id = 0, Name = "a", Create = DateTime.Now });var x = Db.Storageable(list2).SplitUpdate(it => it.Item.Id > 0).SplitInsert(it => it.Item.Id == 0).ToStorage();x.AsInsertable.ExecuteCommand();x.AsUpdateable.ExecuteCommand();

  

例题2:导入一个LIST,当Id存在数据库执行更新,否则执行插入

var x = Db.Storageable(list2)       .SplitUpdate(it => it.Any(y=>y.Id==it.Item.Id))//数据库存在更新       .SplitInsert(it => true ).ToStorage();//其余插入       x.AsInsertable.ExecuteCommand(); //也可以使用blukcopy 参考SqlSugar blukcopy用法       x.AsUpdateable.ExecuteCommand();  

如果实体没有主键我们可以用

var x = Db.Storageable(list2)       .SplitUpdate(it => it.Any(y=>y.Id==it.Item.Id))       .SplitInsert(it => it.NotAny(y => y.Id == it.Item.Id))       .WhereColumns(it=>it.Id).ToStorage(); //以id作为数据库唯一列,当然支持多个 new {it.id,it.name}

  

2.数据验证和统计

准备4条测试数据

List<UinitBlukTable> list2 = new List<UinitBlukTable>();list2.Add(new UinitBlukTable() { Id = 1, Name = "a", Create = DateTime.Now });list2.Add(new UinitBlukTable() { Id = 2, Name = "a", Create = DateTime.Now });list2.Add(new UinitBlukTable() { Id = 3, Name = "a", Create = DateTime.Now.AddYears(-2) });list2.Add(new UinitBlukTable() { Id = 4, Name ="", Create = DateTime.Now.AddYears(-2) });

编写代码将错误数据、可插入数据、可更新数据等进行分类  

var x = Db.Storageable(list2)          .SplitError(it => string.IsNullOrEmpty(it.Item.Name), "名称不能为空")          .SplitError(it => it.Item.Create<DateTime.Now.AddYears(-1),"不是今年的数据")          .SplitUpdate(it => it.Any(y=>y.Id==it.Item.Id))//存在更新          .SplitInsert(it => true)//剩余的插入          .ToStorage();Console.WriteLine(" 插入 {0} 更新{1} 错误数据{2} 不计算数据{3} 删除数据{4},总共{5}" ,     x.InsertList.Count,     x.UpdateList.Count,     x.ErrorList.Count,     x.IgnoreList.Count,     x.DeleteList.Count,     x.TotalList.Count    ); 

执行代码输出结果:

 

我们可以看到其中有1条可以插入的,1条可以更新的,并且2条错误数据

输出错误明细:

foreach (var item in x.ErrorList){  Console.WriteLine("id等于"+item.Item.Id+" : "+item.StorageMessage);}

  

我们可以看到输出id3和id4是错误的,并且可以输出具体的错误明细

执行更新和插入

x.AsInsertable.ExecuteCommand();x.AsUpdateable.ExecuteCommand();

 

三、使用表过滤器

SqlSugar以前也支持了全局过滤器,不过不好用,大部分用户习惯了以表的方式实现过滤器,用法如下

1、创建表过滤器

 SqlSugarClient db = new SqlSugarClient(new ConnectionConfig() { DbType = DbType.SqlServer, ConnectionString = Config.ConnectionString, IsAutoCloseConnection = true }); db.QueryFilter.Add(new TableFilterItem<Order>(it => it.Name.Contains("a"))); //只有表Order才会生效

2、生效的查询语句

使用了表过滤器后只要带有order表的查询语句,都会自动添加一个条件

 db.Queryable<Order>().ToList();   //SELECT [Id],[Name],[Price],[CreateTime],[CustomId] FROM [Order] WHERE ([Name] like '%'+@MethodConst0+'%') db.Queryable<OrderItem, Order>((i, o) => i.OrderId == o.Id)    .Where(i => i.OrderId != 0)    .Select("i.*").ToList();   //SELECT i.* FROM [OrderDetail] i ,[Order] o WHERE ( [i].[OrderId] = [o].[Id] ) AND ( [i].[OrderId] <> @OrderId0 ) AND ([o].[Name] like '%'+@MethodConst1+'%')

3、禁止全局过滤器

db.Queryable<Order>().Filter(null, false).ToList(); //SELECT [Id],[Name],[Price],[CreateTime],[CustomId] FROM [Order]

  

四、一对多查询支持条件过滤 

 sqlsugar对于导航查询也是支持的越来越好,下面是一对多查询后在过滤的例子

var list7= Db.Queryable<Order>().Mapper(it => it.Items, it => it.Items.First().OrderId).Where(it => it.Items.Any(y => y.ItemId == 1)) //以前只支持.any().ToList();  

 

五、总结

SqlSugar所有功能都真实来自于客户,并且是多个客户共同的需求,其实我并没有做到什么创新,只是在客户的基础上把他们想要的功能进行了一些设计,如果他们用了不满意,我在这个基础上在

慢慢的修改

 

源码下载:

https://github.com/donet5/SqlSugar   sqlsugar已经持续更新6年之久,也越来越完善 ,如果说EF或者完它ORM不更新了,那么多一个开源就是多一个选择









原文转载:http://www.shaoqun.com/a/521237.html

跨境电商:https://www.ikjzd.com/

amazon go:https://www.ikjzd.com/w/67

环球b2b:https://www.ikjzd.com/w/1762


一、数据导入有哪些难题该功能可以说100%的开发人员都会遇到,并且非常的常见,比如批量操作,你知道哪条数据错了吗?你都不知道客户更不知道了1、数据分类你需要将插入、更新、忽略不计、错误数据等进么分类汇总,最后返回给客户,如果没有很好的设计想把这些操作一步到位非常的难2、高性能对于插入或者更新肯定不能单纯的插入或者更新,一定要批量操作,或者用到blukcopy操作3、数据验证对于错误数据要进行组装,
acedota:acedota
farfetch:farfetch
口述:老婆趁逛街偷会情人被公公撞破逛街老婆情人:口述:老婆趁逛街偷会情人被公公撞破逛街老婆情人
口述:崩溃 约会时相亲女突然吟诗:口述:崩溃 约会时相亲女突然吟诗
战火连天的伊拉克,跨境市场也许超乎你想象:战火连天的伊拉克,跨境市场也许超乎你想象

1分险胜绿军! 湖人两大功臣救了浓眉:一个玩命回追 一个飞身扑防_哈雷尔

原标题:1分险胜绿军! 湖人两大功臣救了浓眉:一个玩命回追 一个飞身扑防

北京时间1月30日,洛杉矶湖人继续着他们的七连客之旅,客场挑战劲敌波士顿凯尔特人。

开场之后,安东尼·戴维斯就疯狂攻击篮下,并且拼抢篮板球也相当积极。首节过后,浓眉已经出手12次,拿下12分8篮板,其中还有4个前场板。而凯尔特人方面,泰斯也是连续命中三分球还以颜色。首节结束,湖人28-24领先四分。

第二节比赛,两队打得相当胶着,哈雷尔和库兹马的连续得分保证了湖人的微弱优势。不过,第三节开始,杰伦·布朗展现出了花式的进攻技巧,无论是波普还是塔克,完全抵挡不住,绿军单节赢下10分,将比分反超。

最后一节比赛,出现了意外的情况。当时,哈雷尔在篮下进攻,斯玛特上前防守。对抗过后,斯玛特痛苦倒地,惨叫声响彻球馆。斯玛特被搀扶离场,无法回归。

这一意外过后,场上的形势也发生了逆转,詹姆斯开始发力,沃格尔没有换下卡鲁索和哈雷尔,决胜阶段让波普和小加索尔枯坐板凳。

这一决定被证明是相当正确的。比赛的最后时刻,湖人握有球权,还领先1分。但是,浓眉哥持球被协防的肯巴·沃克抢断,距离第四节结束还有11.7秒,留给绿军的时间太过充足!

沃克将球传给了杰伦·布朗,此时,原本还在布朗身后的卡鲁索一路狂奔,不断给杰伦·布朗施压,布朗踉踉跄跄,很难控制住皮球,赶紧将球传出。

卡鲁索的拼抢造成布朗无法完成投篮,也消耗了大把的时间。接下来,皮球转移到了沃克的手上,沃克出手的瞬间,施罗德用尽全力,将自己的身体完全伸展。

面对施罗德的防守,沃克投篮不中,泰斯补篮时间已经不够,湖人96-95惊险取胜。浓眉拽着篮网,似乎仍然惊魂未定。

数据方面,詹姆斯21分7篮板7助攻,浓眉27分14篮板,哈雷尔10中8得到16分5篮板。

施罗德8中3,得到12分3篮板7助攻,卡鲁索出场19分钟,仅仅得到2分3篮板1助攻,但是,湖人的小卡的贡献,是数据无法体现的!返回搜狐,查看更多

责任编辑:

原文转载:http://sport.shaoqun.com/a/389650.html

跨境电商:https://www.ikjzd.com/

丰趣海淘:https://www.ikjzd.com/w/1716

手机trademanager:https://www.ikjzd.com/w/730


原标题:1分险胜绿军!湖人两大功臣救了浓眉:一个玩命回追一个飞身扑防北京时间1月30日,洛杉矶湖人继续着他们的七连客之旅,客场挑战劲敌波士顿凯尔特人。开场之后,安东尼·戴维斯就疯狂攻击篮下,并且拼抢篮板球也相当积极。首节过后,浓眉已经出手12次,拿下12分8篮板,其中还有4个前场板。而凯尔特人方面,泰斯也是连续命中三分球还以颜色。首节结束,湖人28-24领先四分。第二节比赛,两队打得相当
tm商标:tm商标
兰亭集势:兰亭集势
亚马逊黑五别人爆单,你还是那个熟悉的"林丹"吗?:亚马逊黑五别人爆单,你还是那个熟悉的"林丹"吗?
2018年12月亚马逊美国站车窗防光罩品类(Sun Protection)数据报告:2018年12月亚马逊美国站车窗防光罩品类(Sun Protection)数据报告
最全表格填写方法:最全表格填写方法

勇士大胜活塞!库里三节28+5+7,乌布雷18+6,克莱客串解说_维金斯

原标题:勇士大胜活塞!库里三节28+5+7,乌布雷18+6,克莱客串解说

1月31日,NBA常规赛结束了一场焦点战,勇士118-91大胜活塞。这场比赛勇士从未落后过哪怕一秒钟,他们13-3开局,首节领先12分,上半场领先19分。下半场活塞没有反扑,末节沦为垃圾时间,勇士扩大优势成功收获胜利。以下是详细战报:

数据统计:

活塞:格兰特18分,格里芬5分3板2助,普拉姆拉6分,赖特7分6板4助,杰克逊17分6板,斯图尔特8分,罗斯8分;

勇士:维金斯20分4板3助,格林4分5板6助,乌布雷18分6板,库里28分5板7助2断,怀斯曼11分,李6分;

特殊人物!本场比赛,克莱在第二节担任现场解说员;

本场之星:库里;

今天库里的三分球手感火热,经历上一场独木难支输球之后,这一场的库里没有丝毫的大意,他的突破、上篮和三分球很快就帮助勇士打开局面,再加上乌布雷和维金斯的爆发,勇士主将们3节打卡,迎来胜利。

比赛回顾:

第一节,乌布雷命中三分球,格里芬还以颜色,库里外线出手也中,维金斯抛投打进,格林反击和维金斯的三分球命中,勇士13-3开局。普拉姆利上篮止血,维金斯断球反击命中,普拉姆利两罚全中,库里打板得分,格兰特命中三分球。怀斯曼勾手得分,格林一条龙上进,约什-杰克逊反击得分,维金斯上篮打进,斯图尔特中距离投进。维金斯大帽敦布亚,乌布雷抛投打进,斯图尔特两罚中一,乌布雷突破暴扣,罗斯上篮打进,库里打板得分,首节结束,勇士29-17领先活塞。

第二节,乌布雷和罗斯抛投打进,达米恩-李命中三分球,斯图尔特补篮得分,乌布雷跳投打进,约什-杰克逊两罚全中。罗斯低手上篮打停勇士,普尔三罚全中,约什-杰克逊命中三分球,普尔上篮打进,罗斯两罚全中,沃纳梅克空切上进。维金斯上篮扩大优势,格兰特打成3+1,普尔两罚全中,格兰特跳投再中,帕斯卡尔和维金斯连得5分。怀斯曼勾手打进,库里三分球,勇士领先20分。格兰特上篮打进,怀斯曼勾手再中,库里轰进三分球,格兰特三罚中二,随后他助攻赖特暴扣,乌布雷暴扣,上半场战罢,活塞45-64勇士。

第三节,维金斯上篮打进,格里芬助飞普拉姆拉暴扣,库里抛投打进,乌布雷和库里命中三分球,打停活塞。格兰特两罚全中,库里再中三分球,约什-杰克逊两罚全中,乌布雷跳投得手,怀斯曼打成2+1,勇士的优势拉大到30分以上。约什-杰克逊两罚中一,格里芬两罚全中,库里抛投打进,格兰特和约什-杰克逊两罚全中,库里打板得分。达米恩-李命中三分球,约什-杰克逊上篮打进,斯图尔特反击暴扣。库里命中三分球,怀斯曼篮下吃饼,赖特投进三分球,米凯柳克外线出手也中,三节结束,勇士94-68领先活塞。

第四节,双方都派上了替补球员,普尔命中三分球,赖特两罚中一,沃纳梅克外线出手也中。奥卡福跳投得分,维金斯命中三分球,约什-杰克逊外线出手投进,维金斯两罚中一,敦布亚低手上篮得分,卢尼勾手打进,普尔命中三分球。之后勇士一直保持领先,他们轻松拿下胜利。

双方首发阵容:

活塞:格兰特、格里芬、普拉姆利、艾灵顿、赖特;

勇士:库里、乌布雷、维金斯、格林、卢尼;返回搜狐,查看更多

责任编辑:

原文转载:http://sport.shaoqun.com/a/389649.html

跨境电商:https://www.ikjzd.com/

邮政电话:https://www.ikjzd.com/w/202

crowd:https://www.ikjzd.com/w/880


原标题:勇士大胜活塞!库里三节28+5+7,乌布雷18+6,克莱客串解说1月31日,NBA常规赛结束了一场焦点战,勇士118-91大胜活塞。这场比赛勇士从未落后过哪怕一秒钟,他们13-3开局,首节领先12分,上半场领先19分。下半场活塞没有反扑,末节沦为垃圾时间,勇士扩大优势成功收获胜利。以下是详细战报:数据统计:活塞:格兰特18分,格里芬5分3板2助,普拉姆拉6分,赖特7分6板4助,杰克逊17分
picitup:picitup
ideal:ideal
一包"天价辣条"引发的故事,亚马逊无货源模式真的可行吗? :一包"天价辣条"引发的故事,亚马逊无货源模式真的可行吗?
口述:男友送我二手订婚戒指 还保证说是名牌:口述:男友送我二手订婚戒指 还保证说是名牌
海外仓的模式有几种,跨境电商物流选择海外仓面临着哪些挑战?:海外仓的模式有几种,跨境电商物流选择海外仓面临着哪些挑战?

Shiro中Subject对象的创建与绑定流程分析

我们在平常使用Shrio进行身份认证时,经常通过获取Subject 对象中保存的Session、Principal等信息,来获取认证用户的信息,也就是说Shiro会把认证后的用户信息保存在Subject 中供程序使用

 public static Subject getSubject() {  return SecurityUtils.getSubject(); }

 Subject 是Shiro中核心的也是我们经常用到的一个对象,那么Subject 对象是怎么构造创建,并如何存储绑定供程序调用的,下面我们就对其流程进行一下探究,首先是Subject 接口本身的继承与实现,这里我们需要特别关注下WebDelegatingSubject这个实现类,这个就是最终返回的具体实现类

 一、Subject的创建

 在Shiro中每个http请求都会经过SpringShiroFilter的父类AbstractShiroFilte中的doFilterInternal方法,我们看下具体代码

 protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain)   throws ServletException, IOException {  Throwable t = null;  try {   final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);   final ServletResponse response = prepareServletResponse(request, servletResponse, chain);   //创建Subject   final Subject subject = createSubject(request, response);   //执行Subject绑定   //noinspection unchecked   subject.execute(new Callable() {    public Object call() throws Exception {     updateSessionLastAccessTime(request, response);     executeChain(request, response, chain);     return null;    }   });  } catch (ExecutionException ex) {   t = ex.getCause();  } catch (Throwable throwable) {   t = throwable;  }  if (t != null) {   if (t instanceof ServletException) {    throw (ServletException) t;   }   if (t instanceof IOException) {    throw (IOException) t;   }   //otherwise it's not one of the two exceptions expected by the filter method signature - wrap it in one:   String msg = "Filtered request failed.";   throw new ServletException(msg, t);  } }

继续进入createSubject方法,也就是创建Subject对象的入口

 protected WebSubject createSubject(ServletRequest request, ServletResponse response) {  return new WebSubject.Builder(getSecurityManager(), request, response).buildWebSubject(); }

这里使用了build的对象构建模式,进入WebSubject接口中查看Builder与buildWebSubject()的具体实现

Builder()中主要用于初始化SecurityManager 、ServletRequest 、ServletResponse 等对象,构建SubjectContext上下文关系对象

   */  public Builder(SecurityManager securityManager, ServletRequest request, ServletResponse response) {   super(securityManager);   if (request == null) {    throw new IllegalArgumentException("ServletRequest argument cannot be null.");   }   if (response == null) {    throw new IllegalArgumentException("ServletResponse argument cannot be null.");   }   setRequest(request);   setResponse(response);  }

 buildWebSubject方法中开始构造Subject对象

  public WebSubject buildWebSubject() {   Subject subject = super.buildSubject();//父类build方法   if (!(subject instanceof WebSubject)) {    String msg = "Subject implementation returned from the SecurityManager was not a " +      WebSubject.class.getName() + " implementation. Please ensure a Web-enabled SecurityManager " +      "has been configured and made available to this builder.";    throw new IllegalStateException(msg);   }   return (WebSubject) subject;  }

进入父类的buildSubject对象我们可以看到,具体实现是由SecurityManager来完成的

  public Subject buildSubject() {   return this.securityManager.createSubject(this.subjectContext);  }

 在createSubject方法中会根据你的配置从缓存、redis、数据库中获取Session、Principals等信息,并创建Subject对象

 public Subject createSubject(SubjectContext subjectContext) {  //create a copy so we don't modify the argument's backing map:  SubjectContext context = copy(subjectContext); //复制一个SubjectContext对象  //ensure that the context has a SecurityManager instance, and if not, add one:  context = ensureSecurityManager(context); // 检查并初始化SecurityManager对象  //Resolve an associated Session (usually based on a referenced session ID), and place it in the context before  //sending to the SubjectFactory. The SubjectFactory should not need to know how to acquire sessions as the  //process is often environment specific - better to shield the SF from these details:  context = resolveSession(context);//解析获取Sesssion信息  //Similarly, the SubjectFactory should not require any concept of RememberMe - translate that here first  //if possible before handing off to the SubjectFactory:  context = resolvePrincipals(context);//解析获取resolvePrincipals信息  Subject subject = doCreateSubject(context);//创建Subject  //save this subject for future reference if necessary:  //(this is needed here in case rememberMe principals were resolved and they need to be stored in the  //session, so we don't constantly rehydrate the rememberMe PrincipalCollection on every operation).  //Added in 1.2:  save(subject);  return subject; }

在doCreateSubject中通过SubjectFactory创建合成Subject对象

 protected Subject doCreateSubject(SubjectContext context) {  return getSubjectFactory().createSubject(context); }

我们可以看到最后返回的是具体实现类WebDelegatingSubject

 public Subject createSubject(SubjectContext context) {  //SHIRO-646  //Check if the existing subject is NOT a WebSubject. If it isn't, then call super.createSubject instead.  //Creating a WebSubject from a non-web Subject will cause the ServletRequest and ServletResponse to be null, which wil fail when creating a session.  boolean isNotBasedOnWebSubject = context.getSubject() != null && !(context.getSubject() instanceof WebSubject);  if (!(context instanceof WebSubjectContext) || isNotBasedOnWebSubject) {   return super.createSubject(context);  }  //获取上下文对象中的信息  WebSubjectContext wsc = (WebSubjectContext) context;  SecurityManager securityManager = wsc.resolveSecurityManager();  Session session = wsc.resolveSession();  boolean sessionEnabled = wsc.isSessionCreationEnabled();  PrincipalCollection principals = wsc.resolvePrincipals();  boolean authenticated = wsc.resolveAuthenticated();  String host = wsc.resolveHost();  ServletRequest request = wsc.resolveServletRequest();  ServletResponse response = wsc.resolveServletResponse();  //构造返回WebDelegatingSubject对象  return new WebDelegatingSubject(principals, authenticated, host, session, sessionEnabled,    request, response, securityManager); }

以上是Subject的创建过程,创建完成后我们还需要与当前请求线程进行绑定,这样才能通过SecurityUtils.getSubject()方法获取到Subject

二、Subject的绑定

Subject对象本质上是与请求所属的线程进行绑定,Shiro底层定义了一个ThreadContext对象,一个基于ThreadLocal的上下文管理容器,里面定义了一个InheritableThreadLocalMap<Map<Object, Object>>(),Subject最后就是被放到这个map当中,我们获取时也是从这个map中获取

首先我们看下绑定操作的入口,execuse是执行绑定,后续操作采用回调机制来实现

   //执行Subject绑定   //noinspection unchecked   subject.execute(new Callable() {    public Object call() throws Exception {     updateSessionLastAccessTime(request, response);     executeChain(request, response, chain);     return null;    }   });

 初始化一个SubjectCallable对象,并把回调方法传进去

 public <V> V execute(Callable<V> callable) throws ExecutionException {  Callable<V> associated = associateWith(callable);//初始化一个SubjectCallable对象,并把回调方法传进去  try {   return associated.call();  } catch (Throwable t) {   throw new ExecutionException(t);  } } public <V> Callable<V> associateWith(Callable<V> callable) {  return new SubjectCallable<V>(this, callable); }

看下SubjectCallable类的具体实现

public class SubjectCallable<V> implements Callable<V> { protected final ThreadState threadState; private final Callable<V> callable; public SubjectCallable(Subject subject, Callable<V> delegate) {  this(new SubjectThreadState(subject), delegate);//初始化构造方法 } protected SubjectCallable(ThreadState threadState, Callable<V> delegate) {  if (threadState == null) {   throw new IllegalArgumentException("ThreadState argument cannot be null.");  }  this.threadState = threadState;//SubjectThreadState对象  if (delegate == null) {   throw new IllegalArgumentException("Callable delegate instance cannot be null.");  }  this.callable = delegate;//回调对象 } public V call() throws Exception {  try {   threadState.bind();//执行绑定操作   return doCall(this.callable);//执行回调操作  } finally {   threadState.restore();  } } protected V doCall(Callable<V> target) throws Exception {  return target.call(); }} 

具体绑定的操作是通过threadState.bind()来实现的

 public void bind() {  SecurityManager securityManager = this.securityManager;  if ( securityManager == null ) {   //try just in case the constructor didn't find one at the time:   securityManager = ThreadContext.getSecurityManager();  }  this.originalResources = ThreadContext.getResources();  ThreadContext.remove();//首先执行remove操作  ThreadContext.bind(this.subject);//执行绑定操作  if (securityManager != null) {   ThreadContext.bind(securityManager);  } }

在上面bind方法中又会执行ThreadContext的bind方法,这里就是之前说到的shiro底层维护了的一个ThreadContext对象,一个基于ThreadLocal的上下文管理容器,bind操作本质上就是把创建的Subject对象维护到resources 这个InheritableThreadLocalMap中, SecurityUtils.getSubject()方法其实就是从InheritableThreadLocalMap中获取所属线程对应的Subject

 private static final ThreadLocal<Map<Object, Object>> resources = new InheritableThreadLocalMap<Map<Object, Object>>();//定义一个InheritableThreadLocalMap public static void bind(Subject subject) {  if (subject != null) {   put(SUBJECT_KEY, subject);//向InheritableThreadLocalMap中放入Subject对象  } } public static void put(Object key, Object value) {  if (key == null) {   throw new IllegalArgumentException("key cannot be null");  }  if (value == null) {   remove(key);   return;  }  ensureResourcesInitialized();  resources.get().put(key, value);  if (log.isTraceEnabled()) {   String msg = "Bound value of type [" + value.getClass().getName() + "] for key [" +     key + "] to thread " + "[" + Thread.currentThread().getName() + "]";   log.trace(msg);  } }

 三、总结

从以上对Shiro源码的分析,我们对Subject对象的创建与绑定进行了基本的梳理,Subject对象的创建是通过不断的对context上下文对象进行赋值与完善,并最终构造返回WebDelegatingSubject对象的过程;Subject对象创建后,会通过Shiro底层维护的一个基于ThreadLocal的上下文管理容器,即ThreadContext这个类,与请求所属的线程进行绑定,供后续访问使用。对Subject对象创建与绑定流程的分析,有助于理解Shiro底层的实现机制与方法,加深对Shiro的认识,从而在项目中能够正确使用。希望本文对大家能有所帮助,其中如有不足与不正确的地方还望指出与海涵。

 









原文转载:http://www.shaoqun.com/a/521213.html

跨境电商:https://www.ikjzd.com/

飞书互动:https://www.ikjzd.com/w/1319

primc:https://www.ikjzd.com/w/129


我们在平常使用Shrio进行身份认证时,经常通过获取Subject对象中保存的Session、Principal等信息,来获取认证用户的信息,也就是说Shiro会把认证后的用户信息保存在Subject中供程序使用publicstaticSubjectgetSubject(){returnSecurityUtils.getSubject();}Subject是Shiro中核心的也是我们经常用到的一个
ensogo:ensogo
ifttt:ifttt
新政!变体权重增加,不代表可以随意搞事!:新政!变体权重增加,不代表可以随意搞事!
2019年11月份Shopee市场周报&热卖商品数据:2019年11月份Shopee市场周报&热卖商品数据
又现黑科技!一周铺货5000+,0评价产品称霸亚马逊Best seller:又现黑科技!一周铺货5000+,0评价产品称霸亚马逊Best seller

严重时要看烧伤科、冷到极限会脱衣...关于冻伤的五个“冷知识”|北京

  来源:科普中央厨房

  昨日,北京再次开启新一轮大风降温模式。受强冷空气影响,28日气温大幅回落,白天最高气温-2℃,再次降至冰点之下,夜间最低气温也下降至-9℃,风寒效应尤其显著。各大媒体发出紧急提醒:"出行务必做好防风防寒措施!"而行走在路上的人们,心里也已经在不自觉地念叨着"冷,冷,冷"。。。。。。。


  其实,早在2021年1月7日早晨6点前后,北京南郊观象台气温就显示,北京迎来了半世纪以来最冷的一个早晨,最低气温-19.6℃,打破了自1966年以来的气温纪录,创造了历史新低。

  而远在公元1476年正月十三,明成化十二年,那天的北京城天气异常寒冷,南郊参加祭祀的皇家仪仗队和乐官都被冻死了。公元1567年某天,温度骤降,更是"骤寒如穷冬,至晚大风雪。京城城内九门,凡冻死者一百七十余人"(《万历野获编补遗》)。

  虽然人类现在已经具备了应对寒冷的足够智慧,但仍要小心地面对酷寒天气带来的伤害。据媒体报道,每年俄罗斯都有4.2万人因户外醉酒被冻死。在我国黑龙江、吉林、辽宁、内蒙古等地,分分钟泼水成冰,仅仅是皮肤裸露在外,就会有冻裂、冻伤的风险。

  面对"冻"引发的伤害,身体如何抉择?五个关于冻伤的"冷知识",需要我们去了解。

  严重冻伤要转到烧伤科

  身体"选择"放弃它们,是因为要活下去。

  手指、脚趾、耳朵、鼻尖,以及男性生殖器官等肢体末端,是冻伤最常见的部位。冻伤最严重时,上述部位的机体组织已经坏死,即使不自行脱落,为保障生命,最后的治疗也只有截肢。

  简单地说,血管的"热胀冷缩"是冻伤的根本原因。低温环境下,肢体末端的血管发生了收缩,供血因此减少,继而造成了组织缺血缺氧,最终损伤了细胞。

  冻伤的程度有轻有重,轻的可自愈,重的要截肢甚至危及生命。总地来说,冻伤可以分成以下四个程度:

  皮肤表层的局部红肿,是冻伤最轻的一度,可以自愈;二度冻伤,会伤及皮肤生发层,将形成水疱,在较长的恢复时间里会形成痂皮,最终也会留下瘢痕;三度冻伤已经伤及皮下组织,水疱中有出血,皮肤呈现青紫色,坏死组织最终将结成黑色干痂,干痂脱落后露出肉芽组织,皮肤恢复后会留有疤痕。四度是最严重的冻伤,皮肤、皮下组织、肌肉甚至骨骼都会坏死,皮肤完全变黑,需要截肢治疗。


  中国医学科学院北京协和医院皮肤科主任医师李军告诉记者,因冻伤导致皮肤组织的坏死,如果不处理,会继发细菌感染等情况,危及其他器官组织甚至是生命。为阻断继发感染,必要时需要截肢。截肢后,在伤口覆盖抗生素敷料,经过一段时间,慢慢自愈,形成疤痕。

  这一过程与烧伤处理有相似之处,北京大学第三医院皮肤科副主任医师门月华表示,冻伤程度达到三度四度,尤其是需要截肢手术的情况下,患者将转诊至烧伤科进行下一步治疗。

  冻到极限会脱衣服

  "反常脱衣现象"曾出现在部分冻死案例中,表现为户外极寒天气下,人体长时间失温却发生的脱衣服行为。部分研究认为这一反常源于下丘脑发出了错误指令,让人在肢体末端冻僵时,将重新涌入血管的血液所带来的迅速升温,误认为是"热",由此导致了"反常脱衣"。


  其实,"反常脱衣现象"属于法医的专业术语,已经超出了冻伤的范畴。

  1959年俄罗斯乌拉尔山遇难事件中,9名登山者不幸遭遇暴风雪导致低温冻死。人们发现他们的尸体都穿着单薄,衣物或多或少地被脱去了。德国柏林自由大学法医学研究所1995年发表的论文,分析了1978年至1994年之间69例因致死性低温引起的死亡案例,其中有17例存在"反常脱衣"现象。

  鞋太紧会导致冻伤

  不在低温环境下待太久,就能远离冻伤吗?

  "往年一周遇到一两个冻伤患者,今年一天就接诊了两三个",门月华告诉记者,在1月初北京的大风降温后,皮肤科接诊的冻伤患者比往年都多了一些。

今年1月份,北京大风降温环境中,因为没戴帽子出门,约10分钟就二度冻伤的小朋友耳朵今年1月份,北京大风降温环境中,因为没戴帽子出门,约10分钟就二度冻伤的小朋友耳朵

  她讲述了几个印象深刻的案例。一个是大风降温当天,没有戴帽子出门,约10分钟耳朵就二度冻伤的小朋友;另一个是"鞋子不合脚"导致的单侧脚趾冻伤。

  人的左右脚大小并不完全一样,那位患者滑雪时所穿的鞋子一只合适另一只稍紧,结果鞋紧的那侧脚趾发生了冻伤。

  由此可知,引发冻伤与低温环境有关,但低温却不是唯一的因素。

  冻疮其实不是冻伤

  相比各种程度的冻伤而言,皮肤科在冬季接诊冻疮的情况会更多一些。但是,冻疮并不属于冻伤的范畴,因为二者发生的根本原因不同。

  门月华介绍说,冻伤是一个急性期的损伤,是血管收缩、血流不到肢体末端造成的局部组织损伤及坏死。冻疮则是一个慢性的疾病,因为长期的低温刺激而造成了局部的炎症,但并没有局部组织的坏死,因此与冻伤的成因是不同的。

  冻疮对机体的损伤程度一般不会太重,只是比较容易复发。即使经过处理,得过冻疮的患处,也会对冷特别敏感,因而要注意预防和保暖。

  最不易冻伤的居然是眼睛

  在低温面前,哪个人体器官最抗冻呢?"血液越丰富的部位就越安全、越抗冻"。李军告诉记者,人的眼部血管非常丰富,可以说是相对耐寒的身体器官。其实,根据冻伤造成的机体反应,也衍生出了皮肤病的一种治疗方式液氮冷冻治疗。

  液氮的温度为-196℃,当使用液氮对皮肤局部进行冷冻时,可以迅速杀死病区的细胞,使得病区细胞坏死,并在一定时间内自行脱落,最终使得原来的病区恢复正常状态。这种方式一般用来治疗扁平疣等病毒性皮肤病。

  Tips:

  如何给身体保温和复温

  冬季气温下降,人们会添衣保暖。温度平稳下降时,陆续增减衣物即可;但遭遇大风而气温骤降,体感温度就会比室外的气温更低。

  所谓"体感温度",也就是人体所感受到的冷暖程度,温度、湿度以及风速都会影响体感温度。其实,在气温低的同时,体感温度的骤降也是发生冻伤的重要因素。两位受访专家均提示,这种时刻,为防风防冻防感冒,最好将所有终极保暖装备配齐:手套、帽子、面罩、口罩和厚袜子等一个都不要少。

  同时,所穿的衣物要避免过于紧身,否则同样容易造成肢体末端因血液供应不足导致冻伤。而贴身衣物则要尽量保持干燥。一旦衣物、鞋袜沾湿,要及时更换干燥衣物,以免继续在低温环境下逗留造成冻伤。


  血管的收缩与冻伤甚至冻僵有关,因此,皮肤受损的程度如何在刚发生冻伤时是很难辨别的,一般需要在复温后再作诊断。所谓"复温",就是恢复体温,这是冻伤急救和治疗的主要方法。

  如果发现有人冻伤,首先要迅速将伤者转移到温暖的室内,小心脱掉覆盖伤处的衣物,将冻伤处患处浸入38℃-42℃左右的温水之中,直到受冻部位恢复知觉。如果无法恢复知觉,或者冻伤部位出现发紫、肿胀、疼痛等情况,则需要及时就医,以免延误治疗导致截肢。

  快速复温时,水温不能过高,否则会因血管迅速扩张,导致对皮肤组织的二次损伤。有些地方流传着"用雪搓""用手搓"的处理方法,也是不科学的,有可能造成新的皮肤损伤。

  李军指出,雪的温度很低,用雪"复温"可能对冻伤患处造成第二次低温伤害。"冻伤时不要搓、不要揉",门月华也表示,冻伤后皮肤受损,再施加物理压力的刺激,会使损伤再度加重。如果没有温水,也可以用体温进行复温,如放在腋下、腹部等体温较高的位置。

原文转载:http://tech.shaoqun.com/a/317689.html

跨境电商:https://www.ikjzd.com/

首信易支付:https://www.ikjzd.com/w/1841

巴克莱:https://www.ikjzd.com/w/2775


来源:科普中央厨房  昨日,北京再次开启新一轮大风降温模式。受强冷空气影响,28日气温大幅回落,白天最高气温-2℃,再次降至冰点之下,夜间最低气温也下降至-9℃,风寒效应尤其显著。各大媒体发出紧急提醒:"出行务必做好防风防寒措施!"而行走在路上的人们,心里也已经在不自觉地念叨着"冷,冷,冷"。。。。。。。  其实,早在2021年1月7日早晨6点前后,北京南郊观象台气温就显示,北京迎来了半世纪以来最
递四方:递四方
国际标准书号:国际标准书号
黑五电商学院:黑五电商学院
跨境电商 Shopee虾皮好做吗?Shopee虾皮该如何选品?这三点你要知道:跨境电商 Shopee虾皮好做吗?Shopee虾皮该如何选品?这三点你要知道
亚马逊拒让姐夫出庭作证,难道是想凌驾于法律之上?!:亚马逊拒让姐夫出庭作证,难道是想凌驾于法律之上?!

我国人流情况严重,人流率高达29%!别让爱变成伤害

核心提示:临床数据显示,我国的人流率高达29%,全球人流率最高的爱沙尼亚也仅为30%,其中很大一部分是未婚少女因没有做好保护措施而怀孕,继而选择的人流。国家人口计生委科学技术研究所统计,仅在2013年,我国的人流数量高达1300万人次。

小婉(化名),16岁,说到这个年纪,很多人想到的都是青春烂漫,但小婉已经是做过4次人流的女生了。

前两天,小婉由她妈妈带着,到医院做了人流,手术之后的小婉看上去很虚弱,但她脸上却写满无所谓,还训斥妈妈不要老是跟着她。小婉的妈妈心里很自责,她说道,女儿变成现在这样,自己负有一定的责任。

原来,因为婆婆一直希望自己生多一个儿子,小婉的妈妈不顾女儿的反对,在小婉10岁那年生了二胎,从此之后,小婉就变了,尤其是这两年,连学校都不愿意去,夜不归宿更是常态。因为感到自己缺乏关爱,小婉开始谈恋爱,而这4次人流正是小婉的男朋友给她带来的伤害,尽管如此,小婉还是很爱他,在她心里,男朋友是真的爱她,她也愿意为他付出。

医生告诉小婉,如果再做多一次人流,以后自己再想怀孕都很难了,她这才开始紧张起来,而小婉的妈妈更是后悔到默默流泪,心里不断责备自己没有教育好自己的女儿。


一、我国人流情况严重,高达29%!

人流,一个人们并不陌生的名词,但它会给女性身体带来的伤害,很多人们却并不了解。临床数据显示,我国的人流率高达29%,全球人流率最高的爱沙尼亚也仅为30%,其中很大一部分是未婚少女因没有做好保护措施而怀孕,继而选择的人流[1]。国家人口计生委科学技术研究所统计,仅在2013年,我国的人流数量高达1300万人次。有研究指出,在那些有婚前性行为的女性群体中,超过20%的女性有非意愿怀孕的经历,而只有少部分选择生下孩子并结婚,绝大多数为了继续学业或其他原因而选择做人流。

然而人流给女性带来的伤害也不容忽视,不仅是身体上会带来无法弥补的伤害,女性的心理也可能受到影响,毕竟相当于自己亲手结束掉一个鲜活的生命,而且有不少情侣和夫妻之间的感情也可能因一次人流而出现破裂。

为了避免人流带来的影响,在性生活中做好避孕措施,是对伴侣负责的做法,更是每一对还没做好迎接新生命的情侣和夫妻应当做好的方式。安全套不仅具有避孕的目的,更能避免女性可能受到的多种伤害。

二、不戴安全套,会给女性带来很多伤害

上文中说,在情侣爱爱的过程中不带安全套,会增加人流的风险,给女性带来巨大的伤害,但是性生活中,有些情侣情到浓时直接忽视戴安全套这一环节,然而不戴安全套,除了怀孕的风险外,这些伤害也不能忽视。

第一,可能导致女性得妇科疾病,尤其是在男性患有生殖器官感染、泌尿器官感染等疾病时,如果不戴安全套,可能能够性交将病菌传染给女性,引起宫颈炎、阴道炎等妇科疾病。实际上,如果不戴安全套,如果女性患有妇科疾病时,也很可能将病菌传染给男性。因此,为了健康着想,性生活中应全程戴好安全套。

第二,增加性病的感染风险,艾滋病、尖锐湿疣、梅毒等性传播疾病的主要传播途径正是性交,而戴安全套能够有效隔离这些病菌的传播,从而避免感染性病。

此外,如果不戴安全套,很容易导致女性怀孕,对于还没做好要孩子准备的人来说,会选择去医院做人流,而人流会给女性的身体带来很大的伤害,甚至导致不孕。

因此,为了避免这些伤害,在性生活中一定要使用安全套,而且还要正确使用!

三、怎么正确使用安全套

如果没有正确使用安全套,也可能使得安全套失去作用,那么在使用安全套的时候,有哪些需要注意的事情呢?

首先,在选择安全套的时候,应根据个人情况选择合适的型号,太大的型号容易滑落,太小的型号则容易破裂,而且还会影响性生活的感受。

在打开安全套的包装时,一定要沿着锯齿处轻轻撕开,不要用剪刀剪开包装,否则可能剪破安全套。待小弟弟勃起之后,捏住突起的小气囊,并使卷边朝外,套在小弟弟上,并慢慢往下。此外,安全套一定要在男性还没疲软的时候就取下,并且用纸巾包好之后扔弃。

对于女性而言,避免做人流是保护子宫的关键,除此之外,在生活中也应该学会保护自己的子宫。

四、女性该如何保护自己子宫?

生活中,要想保护好子宫,首先要养成规律的作息习惯和饮食习惯,不要熬夜,少吃辛辣刺激、寒凉的食物;其次,要注意个人卫生,尤其是私处的清洁,即使做不到每天洗澡,也应该每天更换内裤,清洗后的内裤最好在阳光下晒干,以更好得杀菌消毒。对于25岁之后的女性而言,每年做一次常规的妇科检查,是保障子宫健康的重要方式。

现如今,医学技术的进步使得人流已经变成一种小手术,但即便是简单的小手术,人流给女性身体带来的伤害仍然不可避免,为了避免人流带来的伤害,保护好自己的身体,是每个女性应该重视的问题。

参考资料:

[1]调查显示我国人工流产率达29‰ 专家:子宫须保护. 广州日报.2015-03-09

[2]巧用小小避孕套[J].人人健康,2017

[3]怎样正确使用避孕套[J].现代妇女,2009:51-51.



未经作者允许授权,禁止转载

39健康网()原创内容,未经授权不得转载,违者必究。内容合作请联系:020-85501999-8819或39media@mail.39.net

原文转载:http://health.shaoqun.com/a/149002.html

跨境电商:https://www.ikjzd.com/

upc:https://www.ikjzd.com/w/111

55海淘网:https://www.ikjzd.com/w/1723


核心提示:临床数据显示,我国的人流率高达29%,全球人流率最高的爱沙尼亚也仅为30%,其中很大一部分是未婚少女因没有做好保护措施而怀孕,继而选择的人流。国家人口计生委科学技术研究所统计,仅在2013年,我国的人流数量高达1300万人次。 小婉(化名),16岁,说到这个年纪,很多人想到的都是青春烂漫,但小婉已经是做过4次人流的女生了。前两天,小婉由她妈妈带着,到医院做了人流,手术之后的小婉看上去很虚
55海淘:55海淘
铭宣:铭宣
eBay三季度GMV同比增长22% 年度活跃买家数增5%:eBay三季度GMV同比增长22% 年度活跃买家数增5%
口述:一年内 老公被我捉奸在床4次老公闺蜜女人:口述:一年内 老公被我捉奸在床4次老公闺蜜女人
亚马逊Amazon开放索评功能引发的评价大战!:亚马逊Amazon开放索评功能引发的评价大战!

又是跟腱!篮网公布杜兰特伤病报告 场均37分钟 纳什真的早该醒了_赛季

原标题:又是跟腱!篮网公布杜兰特伤病报告 场均37分钟 纳什真的早该醒了

北京时间1月30日上午,布鲁克林篮网客场挑战俄克拉荷马雷霆,从纸面上来看,两支球队的实力还是存在着一定差距的。对于哈登和杜兰特来说,这场比赛别有一番味道。

这是昔日的雷霆二少在重逢之后第一次联手对阵雷霆。

但是,篮网赛前却公布了伤病报告,杜兰特因为跟腱伤势恢复,缺席比赛!看到跟腱二字,不少球迷还是心头一紧。

也是因为跟腱,杜兰特整整伤停了一个赛季,本赛季复出后,看到杜兰特摔倒在地,大家还是会下意识想到阿杜的"伤病史",球迷们希望看到杜兰特的炸裂表现,更希望看到的是一个健康的杜兰特,能够平平稳稳打完整个赛季。

但是,经过了丁威迪的重伤、哈登的大交易,篮网可用的球员已经不多,杜兰特不得不一次次"被迫加班"。

本赛季至今,杜兰特场均能够得到30.5分7.5篮板5.5助攻,场均出场36.7分钟,这是怎样的概念?效力于勇士期间,杜兰特的场均出场时间从未达到35分钟,自13-14赛季之后,杜兰特就没有这么长的场均出场时间!

在谈到杜兰特的伤病管理层时,纳什也终于意识到了这一问题,纳什说:"最近几场,凯文(杜兰特)确实打了太多的时间,球队有着长远眼光!"

其实,纳什早就该醒了,虽然赢球非常重要,但是当杜兰特在加时赛还要连续折返跑时,纳什的手里是否应该有一个闹铃,让杜兰特准时下班呢?返回搜狐,查看更多

责任编辑:

原文转载:http://sport.shaoqun.com/a/389638.html

跨境电商:https://www.ikjzd.com/

泛亚班拿:https://www.ikjzd.com/w/1262

dhl:https://www.ikjzd.com/w/516


原标题:又是跟腱!篮网公布杜兰特伤病报告场均37分钟纳什真的早该醒了北京时间1月30日上午,布鲁克林篮网客场挑战俄克拉荷马雷霆,从纸面上来看,两支球队的实力还是存在着一定差距的。对于哈登和杜兰特来说,这场比赛别有一番味道。这是昔日的雷霆二少在重逢之后第一次联手对阵雷霆。但是,篮网赛前却公布了伤病报告,杜兰特因为跟腱伤势恢复,缺席比赛!看到跟腱二字,不少球迷还是心头一紧。也是因为跟腱,杜兰特整整伤停
一淘网:一淘网
法瑞儿:法瑞儿
越来越多商家开始卖口罩,是风险还是机遇?:越来越多商家开始卖口罩,是风险还是机遇?
旺季售后高峰不用怕,退货实践指南来帮忙~:旺季售后高峰不用怕,退货实践指南来帮忙~
速来领取!深圳市2018年第二批境外商标资助拨款来了:速来领取!深圳市2018年第二批境外商标资助拨款来了

使用低碳水化合物减肥,需要注意些什么?

核心提示:对一般健康无任何过往病史的人来说,想要执行低碳水化合物减肥并不一定需要先咨询过医生。

低碳水化合物减肥就是一种只吃肉类、蔬菜而不吃碳化水化合的减肥法。


原理就在于人体消耗热量是从最容易分解的开始,顺序是从糖份开始,消耗的差不多了,然后才是脂肪(锻炼要30分钟后才开始消耗脂肪,也是这个原因)。

而身体里面多余的糖分则会转换成脂肪存储。所以当不给身体提供碳水化合物的时候,身体就转向燃烧脂肪来提供热量。

给低碳水化合物减肥者的建议:

1、调查后再决定,看低碳水化合物减肥是否适合你

减少碳水化合物的摄入并不适合每一个人。对于那些尝试过低脂、低热量饮食失败后,低碳水化合物减肥可能是一个可行的健康的方法,但记住并不是唯一的方法。

2. 拟定计划多读些有关这方面减肥的资料。

很多低碳水化合物饮食菜单可以含有很多健康的食物。肉类和脂肪也在这个健康食谱中。很多人就是因为对自己太苛刻了,只单吃某一类食物而危及了健康。

3. 吃优质蛋白质

无疑,农家饲养的牛、猪、鸡、鸭等的肉类提供的蛋白质更优质,所以市面上卖得也会比较贵。

如果不能选优质的蛋白质,至少也要控制住:不吃速食。一般来说速冻食品都没那么安全健康。

4. 不要忘了多吃蔬菜

让我们粉碎这个谣言:低碳水化物合等同高蛋白质,所以低碳水化合物减肥只能吃肉类,不吃蔬菜。这是错误的。

蔬菜同样也属于低碳水化合物,而且含有营养丰富的抗氧化剂、维生素、矿物质和纤维素,能提供人体抵抗疾病的能力。如果可以的话,买有机蔬菜更健康。

5. 多喝水。

尽量多喝水,达到极限。蛋白质分解出来的副产品应该及时排出体外,所以当然少不了水这个介质。

6. 警惕电解液腿抽筋?

如果你还处于低碳水化合物减肥初期,那么你体内很可能缺一些矿物质:钾、钙、镁等。

多注意补充钾,如果可能的话,吃些香蕉,不过香蕉是一种高热量水果,要有节制。

7. 定下你的减肥目标

一开始的几周,你可能减下好几磅。但是没有任何一种减肥方法能保证坚持下来,后面的减肥速度能这么快。所以不要期望太高,太心急了,每周一至两磅的期望值还是比较现实的。

8. 活动筋骨。

在低碳水化合物减肥法中,并不强求一定要运动,但是减肥成功后回复正常的饮食后,如果不坚持运动可能很容易反弹。

9. 实现诺言。

成功减肥的关键是要忠实于这种方法,把它作为一种生活习惯来维持。但是你是承诺不是要让自己减肥成功然后穿回好看的塑腿牛仔裤,而应该是承诺要把健康的饮食做为长久的目标来执行。


10. 需要咨询医生吗?

对一般健康无任何过往病史的人来说,想要执行低碳水化合物减肥并不一定需要先咨询过医生。


原文转载:http://health.shaoqun.com/a/148993.html

跨境电商:https://www.ikjzd.com/

启明星软件:https://www.ikjzd.com/w/1436

名人堂是什么:https://www.ikjzd.com/w/1082


核心提示:对一般健康无任何过往病史的人来说,想要执行低碳水化合物减肥并不一定需要先咨询过医生。 低碳水化合物减肥就是一种只吃肉类、蔬菜而不吃碳化水化合的减肥法。原理就在于人体消耗热量是从最容易分解的开始,顺序是从糖份开始,消耗的差不多了,然后才是脂肪(锻炼要30分钟后才开始消耗脂肪,也是这个原因)。而身体里面多余的糖分则会转换成脂肪存储。所以当不给身体提供碳水化合物的时候,身体就转向燃烧脂肪来提供
转运中国:转运中国
usps国际快递查询:usps国际快递查询
黑五爆单好时节!但你对黑五真的了解吗?:黑五爆单好时节!但你对黑五真的了解吗?
Lazada大冒险:开拓东南亚差异化市场:Lazada大冒险:开拓东南亚差异化市场
e票联:e票联

别等老了才补钙,年轻人也该早重视

核心提示:很多人觉得,补钙是老年人的事情,和年轻人没有关系。事实上,如果30岁以后再补钙,效率将大打折扣。

很多人觉得,补钙是老年人的事情,和年轻人没有关系。事实上,如果30岁以后再补钙,效率将大打折扣。

据统计,全中国有 96.6% 的人钙摄入不足,平均摄入量不足 400 毫克,低于中国营养学会推荐钙摄入量的一半。也就是说,有相当多的人需要额外补钙。若是在年轻时不注意补钙,老了更容易骨质疏松!

严重的骨质疏松会怎样?轻微碰撞、打个喷嚏都可能闪到腰!

所以,别再以为补钙是老年人的事情,年轻时就应该引起重视。那么日常生活中要如何做才能轻松补钙呢?除了使用钙剂补充以外,我们还可以试试多吃以下几种食物:

1.奶制品

奶制品可帮助快速补钙。比如牛奶,号称"天然钙片"。膳食指南推荐我们每天喝 300 克牛奶或者摄入相当量的奶制品,差不多就能满足 1/3 的钙需求。

有的人可能对乳糖不耐受,则可选择无乳糖牛奶、酸奶或者奶酪。从奶制品的钙含量来看,奶酪最高,酸奶次之,第三是牛奶。虽然奶酪钙含量最高,但不可多吃哦!因为奶酪的热量也很高。

2.深绿色蔬菜

别小瞧深绿色蔬菜的钙含量,这可是"大户人家"。而且,它们不仅钙含量高,还有丰富的维生素K,这也是骨钙素合成过程中必需的。

深绿色蔬菜中钙含量"高材生"分别有:芥菜、乌塌菜、小油菜、芥蓝

推荐每天摄入一斤左右(300 ~ 500 克)的蔬菜 ,其中深色蔬菜占一半。但是,菠菜、苋菜里含有比较多的草酸,会影响钙的吸收,记得焯水再吃。

3.豆制品


有一些豆制品不仅可以补钙,还能提供优质蛋白。比如豆腐干、千张、南豆腐、北豆腐等。

只要正常饮食并做到以上3点,每天的钙量基本就能满足了。但除了补钙,还应该考虑一些影响钙吸收的因素,比如多晒太阳、补充维生素D等。

对于孕晚期、哺乳期、青少年和老年人等,钙量的需求更大,更应该引起重视。


原文转载:http://health.shaoqun.com/a/148980.html

跨境电商:https://www.ikjzd.com/

邮政电话:https://www.ikjzd.com/w/202

e票联:https://www.ikjzd.com/w/1452


核心提示:很多人觉得,补钙是老年人的事情,和年轻人没有关系。事实上,如果30岁以后再补钙,效率将大打折扣。 很多人觉得,补钙是老年人的事情,和年轻人没有关系。事实上,如果30岁以后再补钙,效率将大打折扣。据统计,全中国有96.6%的人钙摄入不足,平均摄入量不足400毫克,低于中国营养学会推荐钙摄入量的一半。也就是说,有相当多的人需要额外补钙。若是在年轻时不注意补钙,老了更容易骨质疏松!严重的骨质疏
联动优势电子商务:联动优势电子商务
acca是什么:acca是什么
三大策略搞定亚马逊YouTube站外引流:三大策略搞定亚马逊YouTube站外引流
亚马逊CPC广告监控表:亚马逊CPC广告监控表
美将28家中国实体列入出口管制"实体清单",商务部回应!:美将28家中国实体列入出口管制"实体清单",商务部回应!

男人护好前列腺,一定要记住这5个护腺常识

核心提示:蔬菜水果对前列腺健康也起着很重要的作用,我们平时常吃的苹果就有保护前列腺的功效,瑞士研究者表示,这可能是苹果中的锌在起作用。

  前列腺作为男性特有的腺体,是男人的健康卫士,可以分泌稀薄奶样的前列腺液,构成了精液的一部分。当男人40岁以后,前列腺体积开始增长,50岁以上的男性大概有一半患良性前列腺增生症。护好前列腺,男人一定要记住这些护腺常识!

  1、前列腺也会"感冒"

  很多人不知道,前列腺也是怕冷的器官。在冬天除了心血管疾病和呼吸疾病,男人的前列腺疾病也会因气温急降而发作。因此,天气冷了,前列腺也要防寒保暖。

  冬季降温后要及时增加衣物,着装、鞋袜、坐垫要保暖;特别是身体下半身切勿受寒,外出时不要坐在石凳上。

  2、抽烟太多会"扰腺"

  黑夜里一副深邃的眼神,再加上一根没抽完的香烟,勾勒出了一副魅力熟男的画面。

  然而现实中,抽烟太多会危害前列腺。香烟中的烟碱、焦油、尼古丁、亚硝胺类等有毒物质能干扰支配血管的神经功能,使前列腺血液循环受影响,从而导致前列腺充血。香烟的有毒物质还会直接毒害前列腺组织,使功能紊乱,抵抗力下降,导致疾病发生。

  因此,男人要尽量避免抽烟,这不仅是为了你的心血管健康,还为了你的前列腺。


  3、纵欲、寡欲都伤前列腺

  虽说纵欲是前列腺的"大敌",但寡欲亦非前列腺"伙伴"。性生活适度最和谐。

  性生活太频繁会导致前列腺过度充血,诱发前列腺炎。而过度压抑性欲,则会使前列腺液大量囤积,诱发炎症。


原文转载:http://health.shaoqun.com/a/148975.html

跨境电商:https://www.ikjzd.com/

grab:https://www.ikjzd.com/w/841

代购公司:https://www.ikjzd.com/w/1982


核心提示:蔬菜水果对前列腺健康也起着很重要的作用,我们平时常吃的苹果就有保护前列腺的功效,瑞士研究者表示,这可能是苹果中的锌在起作用。   前列腺作为男性特有的腺体,是男人的健康卫士,可以分泌稀薄奶样的前列腺液,构成了精液的一部分。当男人40岁以后,前列腺体积开始增长,50岁以上的男性大概有一半患良性前列腺增生症。护好前列腺,男人一定要记住这些护腺常识!  1、前列腺也会"感冒"  很多人不知道,
递四方:递四方
海豚村:海豚村
口述:我爱爱时做面膜 害老公性冷淡爱爱爱美美容:口述:我爱爱时做面膜 害老公性冷淡爱爱爱美美容
怎么办?Facebook广告表现越来越差了!:怎么办?Facebook广告表现越来越差了!
英国最受欢迎的50个B2C网站:英国最受欢迎的50个B2C网站

2021年1月30日星期六

爱情厌倦期一般为多久 男生在厌倦期的表现

几乎所有恋情都会遇到感情的平淡期,甚至厌倦期。那么爱情厌倦期一般为多久呢?长久的厌倦必然带来感情的影响,男生在厌倦期的表现又有哪些?了解一下吧。

爱情厌倦期一般为多久 男生在厌倦期的表现

爱情厌倦期一般为多久

当一个人刚坠入情网,就像开启了全新的世界。ta讲的笑话总是那么好笑,每一顿与他共进的晚餐都是绝顶的美味,和他在一起的日子是多么的幸福。你总是魂不守舍,你愿意全身心地投入。你可能会认为这种感觉不可思议,事实上,用科学的解释就是你的荷尔蒙在起作用,让你整日都保持兴奋热情的状态。

而随着恋爱日子的越来越久,大概3~6个月的热恋期过后,体内的荷尔蒙也会逐渐下降,爱情不可避免地会趋于平淡。从心理学角度来讲,这是因为你的负担过重,由于长期兴奋过度,使大脑无法适应。这时你会想到他的种种不好,也不再用心做一些小浪漫了。你还会突然发现更愿意自己一个人睡觉。这个时期继续延续,一成不变的平淡,从而产生对感情的厌倦。

爱情厌倦期一般为多久 男生在厌倦期的表现

男生在厌倦期的表现

1、不主动联系你

我们都知道,其实在一段感情里,想要维系好两个人之间的关系的话,那么首先你就要懂得跟自己的另一半保持联系,因为只有这样子才能够很好地保持你们之间的感情,增进你们对彼此之间的了解。因此,我们不难发现,在刚开始谈恋爱的时候,很多人都会一直跟自己的另一半保持联系。但是,假如说你的男朋友在热恋期过后一直不主动跟你联系,你平时找他的话他也不会很快就回复你的消息,有的时候甚至是好几天都不搭理你的话,那么就证明你的男朋友肯定是觉得对这段感情厌倦了。

2、开始挑剔你

相信不少人都有这样子的感觉,那就是不管别人说自己的另一半有多么不好,但是在我们自己的眼里,自己的另一半就是很完美的一个人,因为爱情很多时候都会使得我们变得比较盲目,所以你喜欢的人在你的眼里都是比较完美的,没有什么缺点,就算是有你也会选择当做看不见。因此,假如说你的另一半对你特别挑剔的话,那么其实这就意味着他并不是特别喜欢你了,所以才会觉得你哪里都不是很好,才会觉得你身上都是缺点,没有一点点可取的地方。

3、经常跟你吵架

还有一点其实也是很明显的,那么就是他会经常因为一点点很小的事情就跟你吵架,不管这件事情是谁的错,他都会一直对这件事情表现得很敏感。如果说你犯了什么错的话,那么他就会一直揪着你的错误不放,不管你怎么解释都不会听。如果说想要经营好一段感情的话,那么很多时候我们都会选择避免跟自己的另一半发生冲突,因为吵架只会伤害到你们两个人之间的感情,但是一旦男生进入厌倦期,那么他就会觉得吵架什么的都无所谓了。

原文转载:http://lady.shaoqun.com/a/265119.html

跨境电商:https://www.ikjzd.com/

paipaiwang:https://www.ikjzd.com/w/2205

e票联:https://www.ikjzd.com/w/1452


几乎所有恋情都会遇到感情的平淡期,甚至厌倦期。那么爱情厌倦期一般为多久呢?长久的厌倦必然带来感情的影响,男生在厌倦期的表现又有哪些?了解一下吧。爱情厌倦期一般为多久当一个人刚坠入情网,就像开启了全新的世界。ta讲的笑话总是那么好笑,每一顿与他共进的晚餐都是绝顶的美味,和他在一起的日子是多么的幸福。你总是魂不守舍,你愿意全身心地投入。你可能会认为这种感觉不可思议,事实上,用科学的解释就是你的荷尔蒙在
f2c:f2c
bol:bol
本周,意大利交通、加拿大邮政工人确定罢工:本周,意大利交通、加拿大邮政工人确定罢工
TiKTok购物车来袭,不允许你再错过红利了!:TiKTok购物车来袭,不允许你再错过红利了!
欧洲站卖家要注意了,2020年不注意这些细节将导致产品下架... :欧洲站卖家要注意了,2020年不注意这些细节将导致产品下架...

ConcurrentHashMap详解

原文链 id="concurrenthashmap详解">ConcurrentHashMap详解

JDK7

Segment

在jdk8之前concurrentHashMap使用该对象进行分段加锁,降低了锁的粒度,使得并发效率提高,Segment本身也相当于一个HashMap,Segment包含一个HashEntry数组,数组中每个HashEntry既是一个键值对,又是一个链表的头结点

get方法

  • 根据key做hash运算,得到hash值
  • 通过hash值,定位到对应的segment对象
  • 再次通过hash值,定位到segment当中数组的具体位置

put方法

  • 根据key做hash运算,得到hash值
  • 通过hash值,定位到对应的segment对象
  • 获取可重入锁
  • 再次通过hash值,定位到segment当中数组的具体位置
  • 插入或覆盖hashEntry对象
  • 释放锁

但是使用这种方式实现需要进行两次hash操作,第一次hash操作找到对应的segment,第二次hash操作定位到元素所在链表的头部

JDK8

在jdk8的时候参考了HashMap的设计,采用了数组+链表+红黑树的方式,内部大量采用CAS操作,舍弃了分段锁的思想

CAS

CAS是compare and swap的缩写,即我们所说的比较交换,CAS属于乐观锁。

CAS包含三个操作数,---内存中的值(V),预期原值(A),新值(B) 如果内存中的值和A的值一样,就可以将内存中的值更新为B。CAS通过无限循环来获取数据,一直到V和A一致为止

乐观锁

乐观锁会很乐观的认为不会出现并发问题,所以采用无锁的机制来进行处理,比如通过给记录加version来获取数据,性能比悲观锁要高

悲观锁

悲观锁会很悲观的认为肯定会出现并发问题,所以会将资源锁住,该资源只能有一个线程进行操作,只有前一个获得锁的线程释放锁之后,下一个线程才可以访问

源码分析

重要变量
// 表示整个hash表,初始化阶段是在第一次插入的时候,容量总是2的次幂transient volatile Node<K,V>[] table;// 下一个使用的表 只有在扩容的时候非空,其他情况都是nullprivate transient volatile Node<K,V>[] nextTable;/** * Base counter value, used mainly when there is no contention, * but also as a fallback during table initialization * races. Updated via CAS. */private transient volatile long baseCount;// 用于初始化和扩容控制// 0:默认值// -1:正在初始化// 大于0:为hash表的阈值// 小于-1:有多个线程在进行扩容 该值为 -(1+正在扩容的线程数)private transient volatile int sizeCtl;/** * The next table index (plus one) to split while resizing. */private transient volatile int transferIndex;/** * Spinlock (locked via CAS) used when resizing and/or creating CounterCells. */private transient volatile int cellsBusy;/** * Table of counter cells. When non-null, size is a power of 2. */private transient volatile CounterCell[] counterCells;// viewsprivate transient KeySetView<K,V> keySet;private transient ValuesView<K,V> values;private transient EntrySetView<K,V> entrySet;
构造函数
/** * Creates a new, empty map with the default initial table size (16). */public ConcurrentHashMap() {}/** * Creates a new, empty map with an initial table size * accommodating the specified number of elements without the need * to dynamically resize. * * @param initialCapacity The implementation performs internal * sizing to accommodate this many elements. * @throws IllegalArgumentException if the initial capacity of * elements is negative */public ConcurrentHashMap(int initialCapacity) { if (initialCapacity < 0)  throw new IllegalArgumentException(); int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?    MAXIMUM_CAPACITY :    tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1)); this.sizeCtl = cap;}/** * Creates a new map with the same mappings as the given map. * * @param m the map */public ConcurrentHashMap(Map<? extends K, ? extends V> m) { this.sizeCtl = DEFAULT_CAPACITY; putAll(m);}/** * Creates a new, empty map with an initial table size based on * the given number of elements ({@code initialCapacity}) and * initial table density ({@code loadFactor}). * * @param initialCapacity the initial capacity. The implementation * performs internal sizing to accommodate this many elements, * given the specified load factor. * @param loadFactor the load factor (table density) for * establishing the initial table size * @throws IllegalArgumentException if the initial capacity of * elements is negative or the load factor is nonpositive * * @since 1.6 */public ConcurrentHashMap(int initialCapacity, float loadFactor) { this(initialCapacity, loadFactor, 1);}/** * Creates a new, empty map with an initial table size based on * the given number of elements ({@code initialCapacity}), table * density ({@code loadFactor}), and number of concurrently * updating threads ({@code concurrencyLevel}). * * @param initialCapacity the initial capacity. The implementation * performs internal sizing to accommodate this many elements, * given the specified load factor. * @param loadFactor the load factor (table density) for * establishing the initial table size * @param concurrencyLevel the estimated number of concurrently * updating threads. The implementation may use this value as * a sizing hint. * @throws IllegalArgumentException if the initial capacity is * negative or the load factor or concurrencyLevel are * nonpositive */public ConcurrentHashMap(int initialCapacity,       float loadFactor, int concurrencyLevel) { if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)  throw new IllegalArgumentException(); if (initialCapacity < concurrencyLevel) // Use at least as many bins  initialCapacity = concurrencyLevel; // as estimated threads long size = (long)(1.0 + (long)initialCapacity / loadFactor); int cap = (size >= (long)MAXIMUM_CAPACITY) ?  MAXIMUM_CAPACITY : tableSizeFor((int)size); this.sizeCtl = cap;}
重要方法
put方法

ConcurrentHashMap是如何保证在插入的时候线程安全的呢

public V put(K key, V value) { return putVal(key, value, false);}
final V putVal(K key, V value, boolean onlyIfAbsent) { 	// ConcurrentHashMap不允许key和value为null if (key == null || value == null) throw new NullPointerException(); 	// 计算hash值 int hash = spread(key.hashCode()); int binCount = 0; for (Node<K,V>[] tab = table;;) {  Node<K,V> f; int n, i, fh;  	// tab为null,哈希表还没有初始化,进行初始化哈希表  if (tab == null || (n = tab.length) == 0)   tab = initTable();  	// 该索引位置为null,表示还没有元素  else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {   	// 使用CAS的方式添加节点   if (casTabAt(tab, i, null,       new Node<K,V>(hash, key, value, null)))    break;     // no lock when adding to empty bin  }  	// 节点的hash值为-1,表示该哈希表正在扩容  else if ((fh = f.hash) == MOVED)   tab = helpTransfer(tab, f);  else {   V oldVal = null;   	// 对头节点加锁   synchronized (f) {    	// 再次判断一下该节点是否为目标索引位置的头节点,防止期间被修改    if (tabAt(tab, i) == f) {     	// 表示是普通的链表     if (fh >= 0) {      binCount = 1;      for (Node<K,V> e = f;; ++binCount) {       K ek;       if (e.hash == hash &&        ((ek = e.key) == key ||         (ek != null && key.equals(ek)))) {        oldVal = e.val;        if (!onlyIfAbsent)         e.val = value;        break;       }       Node<K,V> pred = e;       if ((e = e.next) == null) {        pred.next = new Node<K,V>(hash, key,               value, null);        break;       }      }     }     	// 红黑树 TreeBin的hash值为TREEBIN,是-2     else if (f instanceof TreeBin) {      Node<K,V> p;      binCount = 2;      if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,              value)) != null) {       oldVal = p.val;       if (!onlyIfAbsent)        p.val = value;      }     }    }   }   	// 可以看一下上述的赋值流程   	// 默认初始值是0   	// 链表时为1 在遍历时进行累加,直到找到所要添加的位置为止   	// 红黑树时为2   if (binCount != 0) {    	// 链表的长度是否达到8 达到8转为红黑树    if (binCount >= TREEIFY_THRESHOLD)     treeifyBin(tab, i);    	// oldVal不为null,表示只是对key的值进行的修改,没有添加元素,直接返回即可    if (oldVal != null)     return oldVal;    break;   }  } } 	//  addCount(1L, binCount); return null;}

哈希函数根据hashCode计算出哈希值,这里的hash值与HashMap的计算方式稍微有点不同,在低十六位异或高十六位之后还需要与HASH_BITS在进行与运算,HASH_BITS的值是0x7fffffff,转为二进制是31个1,进行与运算是为了保证得到的hash值为正数。

ConcurrentHashMap中hash值为负数包含有其他含义,-1表示为ForwardingNode节点,-2表示为TreeBin节点

static final int spread(int h) { 	// (h ^ (h >>> 16)与hashMap相同 	// HASH_BITS进行与运算 return (h ^ (h >>> 16)) & HASH_BITS;}

初始化hash表的操作

private final Node<K,V>[] initTable() { Node<K,V>[] tab; int sc; 	// hash表为null时才需要进行初始化 while ((tab = table) == null || tab.length == 0) {  	// sizeCtl小于0表示有其他线程在进行初始化操作了  if ((sc = sizeCtl) < 0)   Thread.yield(); // lost initialization race; just spin  	// 将SIZECTL设为-1,表示该线程要开始初始化表了  else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {   try {    if ((tab = table) == null || tab.length == 0) {     int n = (sc > 0) ? sc : DEFAULT_CAPACITY;     @SuppressWarnings("unchecked")     Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];     table = tab = nt;     	// n右移两位 表示1/4n n-1/4n为3/4n 即为n*0.75     sc = n - (n >>> 2);    }   } finally {    sizeCtl = sc;   }   break;  } } return tab;}
private final void addCount(long x, int check) { CounterCell[] as; long b, s; if ((as = counterCells) != null ||  !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {  CounterCell a; long v; int m;  boolean uncontended = true;  if (as == null || (m = as.length - 1) < 0 ||   (a = as[ThreadLocalRandom.getProbe() & m]) == null ||   !(uncontended =    U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {   fullAddCount(x, uncontended);   return;  }  if (check <= 1)   return;  s = sumCount(); } if (check >= 0) {  Node<K,V>[] tab, nt; int n, sc;  while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&    (n = tab.length) < MAXIMUM_CAPACITY) {   int rs = resizeStamp(n);   if (sc < 0) {    if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||     sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||     transferIndex <= 0)     break;    if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))     transfer(tab, nt);   }   else if (U.compareAndSwapInt(this, SIZECTL, sc,           (rs << RESIZE_STAMP_SHIFT) + 2))    transfer(tab, null);   s = sumCount();  } }}

由于本身的博客百度没有收录,博客地 />






原文转载:http://www.shaoqun.com/a/521203.html

跨境电商:https://www.ikjzd.com/

grab:https://www.ikjzd.com/w/841

extra:https://www.ikjzd.com/w/1736


原文链id="concurrenthashmap详解">ConcurrentHashMap详解JDK7Segment在jdk8之前concurrentHashMap使用该对象进行分段加锁,降低了锁的粒度,使得并发效率提高,Segment本身也相当于一个HashMap,Segment包含一个HashEntry数组,数组中每个HashEntry既是一个键值对,又是一个链表的头
淘粉吧怎么返利:淘粉吧怎么返利
斑马物联网:斑马物联网
腾邦:腾邦
美国最新法律:各州及地方政府可对互联网零售商强征销售税!:美国最新法律:各州及地方政府可对互联网零售商强征销售税!
又一爆款陨落!房子烧了, 亚马逊召回50万件产品, 卖家面临被起诉!:又一爆款陨落!房子烧了, 亚马逊召回50万件产品, 卖家面临被起诉!

2021最新 Java虚拟机(JVM)面试题精选(附刷题小程序)

推荐使用小程序阅读

为了能让您更加方便的阅读
本文所有的面试题目均已整理至小程序《面试手册
可以通过微信扫描(或长按)下图的二维码享受更好的阅读体验!

ah_xct


目录
  • 推荐使用小程序阅读
  • 1. JVM 基础
    • 1.1 JVM 内存分哪几个区,每个区的作用是什么?
    • 方法区
    • 虚拟机栈:
    • 本地方法栈
    • 程序计数器
    • 1.2 对象的访问定位有几种方式?
    • 句柄访问
    • 直接指针访问
    • 1.3 JVM内存模型是什么?
    • 1.4 finalize()方法什么时候被调用?析构函数(finalization)的目的是什么?
    • 1.5 什么是深拷贝和浅拷贝?什么是深复制和浅复制?
    • 1.6 说一下堆栈的区别?
    • 1.7 队列和栈是什么?有什么区别?
    • 1.8 Java会存在内存泄漏吗?请简单描述
    • 1.9 Java对象结构是什么?
    • 1.10 引用的分类有几种?
  • 2. JVM 垃圾回收
    • 2.1 如和判断一个对象是否存活?(或者 GC 对象的判定方法)
    • 引用计数法
    • 可达性算法(引用链法)
    • 2.2 简述 java 垃圾回收机制?
    • 2.3 垃圾回收有什么目的?什么时候进行垃圾回收?
    • 2.4 如果对象的引用被置为null,垃圾收集器是否会立即释放对象占用的内存?
    • 2.5 GC是什么?为什么要GC?
    • 2.6 垃圾回收的优点有那些?
    • 2.7 垃圾回收器的基本原理是什么?
    • 2.8 垃圾回收器可以马上回收内存吗?
    • 2.9 有什么办法主动通知虚拟机进行垃圾回收?
    • 2.10 Java 中都有哪些引用类型?
    • 2.11 JVM中的永久代中会发生垃圾回收吗?
    • 2.12 JVM 有哪些垃圾回收算法?
    • 2.13 简述一下标记-清除算法
    • 2.14 简述一下复制算法
    • 2.15 简述一下标记-整理算法
    • 2.16 简述一下分代收集算法
    • 2.17 JVM 有哪些垃圾回收器?
    • 2.18 详细介绍一下 CMS 垃圾回收器?
    • 2.19 新生代垃圾回收器和老年代垃圾回收器都有哪些?有什么区别?
    • 2.20 简述分代垃圾回收器是怎么工作的?
  • 3. JVM 类加载
    • 3.1 JVM类加载的时机?
    • 3.2 JVM类加载过程?
    • 加载
    • 验证
    • 准备
    • 解析
    • 初始化
    • 卸载
    • 3.3 JVM加载Class文件的原理机制是什么?
    • 3.4 什么是类加载器,类加载器有哪些?
    • 3.5 什么是双亲委派模型?
  • 4. JVM调优
    • 4.1 用过那些JVM 调优的工具?
    • 4.2 常用的 JVM 调优的参数都有哪些?
    • 4.3 如何分析GC日志?
    • 4.4 JVM调优命令有那些?
    • 4.5 你知道哪些JVM性能调优方式有那些?


1. JVM 基础

1.1 JVM 内存分哪几个区,每个区的作用是什么?

方法区

1. 有时候也成为永久代,在该区内很少发生垃圾回收,但是并不代表不发生 GC,在这里进行的 GC 主要是对方法区里的常量池和对类型的卸载2. 方法区主要用来存储已被虚拟机加载的类的信息、常量、静态变量和即时编译器编译后的代码等数据。3. 该区域是被线程共享的。4. 方法区里有一个运行时常量池,用于存放静态编译产生的字面量和符号引用。该常量池具有动态性,也就是说常量并不一定是编译时确定,运行时生成的常量也会存在这个常量池中。

虚拟机栈:

  1. 虚拟机栈也就是我们平常所称的栈内存,它为 java 方法服务,每个方法在执行的时候都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接和方法出口等信息。

  2. 虚拟机栈是线程私有的,它的生命周期与线程相同。

  3. 局部变量表里存储的是基本数据类型、returnAddress 类型(指向一条字节码指令的地址)和对象引用,这个对象引用有可能是指向对象起始地址的一个指针,也有可能是代表对象的句柄或者与对象相关联的位置。局部变量所需的内存空间在编译器间确定

  4. 操作数栈的作用主要用来存储运算结果以及运算的操作数,它不同于局部变量表通过索引来访问,而是压栈和出栈的方式

  5. 每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接.动态链接就是将常量池中的符号引用在运行期转化为直接引用。

本地方法栈

本地方法栈和虚拟机栈类似,只不过本地方法栈为 Native 方法服务。

java 堆是所有线程所共享的一块内存,在虚拟机启动时创建,几乎所有的对象实例都在这里创建,因此该区域经常发生垃圾回收操作。

程序计数器

内存空间小,字节码解释器工作时通过改变这个计数值可以选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理和线程恢复等功能都需要依赖这个计数器完成。该内存区域是唯一一个 java 虚拟机规范没有规定任何 OOM 情况的区域。

1.2 对象的访问定位有几种方式?

Java程序需要通过 JVM 栈上的引用访问堆中的具体对象。对象的访问方式取决于 JVM 虚拟机的实现。目前主流的访问方式有 句柄直接指针 两种方式。

  • 句柄:
    可以理解为指向指针的指针,维护着对象的指针。句柄不直接指向对象,而是指向对象的指针(句柄不发生变化,指向固定内存地址),再由对象的指针指向对象的真实内存地址。
  • 直接指针:
    指向对象,代表一个对象在内存中的起始地址。

句柄访问

Java堆中划分出一块内存来作为句柄池,引用中存储对象的句柄地址,而句柄中包含了对象实例数据对象类型数据各自的具体地址信息,具体构造如下图所示:
句柄访问对象.png
优势:引用中存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中实例数据指针,而引用本身不需要修改。

直接指针访问

如果使用直接指针访问,引用 中存储的直接就是对象地址,那么Java堆对象内部的布局中就必须考虑如何放置访问类型数据的相关信息。
指针访问对象.png
优势:速度更,节省了一次指针定位的时间开销。由于对象的访问在Java中非常频繁,因此这类开销积少成多后也是非常可观的执行成本。HotSpot 中采用的就是这种方式。

1.3 JVM内存模型是什么?

java 内存模型(JMM)是线程间通信的控制机制.JMM 定义了主内存和线程之间抽象关系。线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是 JMM 的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。Java 内存模型的抽象示意图如下:

线程 A 与线程 B 之间如要通信的话,必须要经历下面 2 个步骤:

  1. 首先,线程 A 把本地内存 A 中更新过的共享变量刷新到主内存中去。
  2. 然后,线程 B 到主内存中去读取线程 A 之前已更新过的共享变量。

1.4 finalize()方法什么时候被调用?析构函数(finalization)的目的是什么?

垃圾回收器(garbage colector)决定回收某对象时,就会运行该对象的finalize()方法;
finalize是Object类的一个方法,该方法在Object类中的声明protected void finalize() throws Throwable { }
在垃圾回收器执行时会调用被回收对象的finalize()方法,可以覆盖此方法来实现对其资源的回收。注意:一旦垃圾回收器准备释放对象占用的内存,将首先调用该对象的finalize()方法,并且下一次垃圾回收动作发生时,才真正回收对象占用的内存空间

GC本来就是内存回收了,应用还需要在finalization做什么呢? 答案是大部分时候,什么都不用做(也就是不需要重载)。只有在某些很特殊的情况下,比如你调用了一些native的方法(一般是C写的),可以要在finaliztion里去调用C的释放函数。

1.5 什么是深拷贝和浅拷贝?什么是深复制和浅复制?

浅拷贝(shallowCopy)只是增加了一个指针指向已存在的内存地址,

深拷贝(deepCopy)是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存,

使用深拷贝的情况下,释放内存的时候不会因为出现浅拷贝时释放同一个内存的错误。

浅复制:仅仅是指向被复制的内存地址,如果原地址发生改变,那么浅复制出来的对象也会相应的改变。

深复制:在计算机中开辟一块新的内存地址用于存放复制的对象。

1.6 说一下堆栈的区别?

  • 物理地址

    • 堆的物理地址分配对对象是不连续的。因此性能慢些。在GC的时候也要考虑到不连续的分配,所以有各种算法。比如,标记-消除复制标记-压缩分代(即新生代使用复制算法,老年代使用标记——压缩)

    • 栈使用的是数据结构中的栈,先进后出的原则,物理地址分配是连续的。所以性能快。

  • 内存分别

    • 堆因为是不连续的,所以分配的内存是在运行期确认的,因此大小不固定。一般堆大小远远大于栈。
    • 栈是连续的,所以分配的内存大小要在编译期就确认,大小是固定的。
  • 存放的内容

    • 堆存放的是对象的实例和数组。因此该区更关注的是数据的存储
    • 栈存放:局部变量,操作数栈,返回结果。该区更关注的是程序方法的执行。
      PS:
      1. 静态变量放在方法区
      2. 静态的对象还是放在堆。
  • 程序的可见度

    • 堆对于整个应用程序都是共享、可见的。
    • 栈只对于线程是可见的。所以也是线程私有。他的生命周期和线程相同。

1.7 队列和栈是什么?有什么区别?

队列和栈都是被用来预存储数据的。

  • 操作的名称不同。队列的插入称为入队,队列的删除称为出队。栈的插入称为进栈,栈的删除称为出栈。
  • 可操作的方式不同。队列是在队尾入队,队头出队,即两边都可操作。而栈的进栈和出栈都是在栈顶进行的,无法对栈底直接进行操作。
  • 操作的方法不同。队列是先进先出(FIFO),即队列的修改是依先进先出的原则进行的。新来的成员总是加入队尾(不能从中间插入),每次离开的成员总是队列头上(不允许中途离队)。而栈为后进先出(LIFO),即每次删除(出栈)的总是当前栈中最新的元素,即最后插入(进栈)的元素,而最先插入的被放在栈的底部,要到最后才能删除。

1.8 Java会存在内存泄漏吗?请简单描述

内存泄漏是指不再被使用的对象或者变量一直被占据在内存中。理论上来说,Java是有GC垃圾回收机制的,也就是说,不再被使用的对象,会被GC自动回收掉,自动从内存中清除。

但是,即使这样,Java也还是存在着内存泄漏的情况,java导致内存泄露的原因很明确:长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是java中内存泄露的发生场景。

1.9 Java对象结构是什么?

Java对象由三个部分组成:对象头实例数据对齐填充

  • 对象头由两部分组成

    • 第一部分存储对象自身的运行时数据:
      • 哈希码;
      • GC分代年龄;
      • 锁标识状态;
      • 线程持有的锁;
      • 偏向线程ID(一般占32/64 bit)。
    • 第二部分是指针类型,指向对象的类元数据类型(即对象代表哪个类)。如果是数组对象,则对象头中还有一部分用来记录数组长度。
  • 实例数据用来存储对象真正的有效信息(包括父类继承下来的和自己定义的)

  • 对齐填充:JVM要求对象起始地址必须是8字节的整数倍(8字节对齐)

1.10 引用的分类有几种?

  • 强引用:GC时不会被回收
  • 软引用:描述有用但不是必须的对象,在发生内存溢出异常之前被回收
  • 弱引用:描述有用但不是必须的对象,在下一次GC时被回收
  • 虚引用(幽灵引用/幻影引用):无法通过虚引用获得对象,用PhantomReference实现虚引用,虚引用用来在GC时返回一个通知。

2. JVM 垃圾回收

2.1 如和判断一个对象是否存活?(或者 GC 对象的判定方法)

判断一个对象是否存活有两种方法:

引用计数法

所谓引用计数法就是给每一个对象设置一个引用计数器,每当有一个地方引用这个对象时,就将计数器加一,引用失效时,计数器就减一。当一个对象的引用计数器为零时,说明此对象没有被引用,也就是"死对象",将会被垃圾回收.

引用计数法有一个缺陷就是无法解决循环引用问题,也就是说当对象 A 引用对象 B,对象 B 又引用者对象 A,那么此时 A,B 对象的引用计数器都不为零,也就造成无法完成垃圾回收,所以主流的虚拟机都没有采用这种算法。

可达性算法(引用链法)

该算法的思想是:从一个被称为 GC Roots 的对象开始向下搜索,如果一个对象到 GC Roots 没有任何引用链相连时,则说明此对象不可用。在 java 中可以作为 GC Roots 的对象有以下几种:

  • 虚拟机栈中引用的对象
  • 方法区类静态属性引用的对象
  • 方法区常量池引用的对象
  • 本地方法栈 JNI 引用的对象

虽然这些算法可以判定一个对象是否能被回收,但是当满足上述条件时,一个对象不一定会被回收。当一个对象不可达 GC Root 时,这个对象并不会立马被回收,而是出于一个死缓的阶段,若要被真正的回收需要经历两次标记

如果对象在可达性分析中没有与 GC Root 的引用链,那么此时就会被第一次标记并且进行一次筛选,筛选的条件是是否有必要执行 finalize()方法。当对象没有覆盖 finalize()方法或者已被虚拟机调用过,那么就认为是没必要的。

如果该对象有必要执行 finalize()方法,那么这个对象将会放在一个称为 F-Queue 的对队列中,虚拟机会触发一个 Finalize()线程去执行,此线程是低优先级的,并且虚拟机不会承诺一直等待它运行完,这是因为如果 finalize()执行缓慢或者发生了死锁,那么就会造成 F-Queue 队列一直等待,造成了内存回收系统的崩溃。GC 对处于 F-Queue 中的对象进行第二次被标记,这时,该对象将被移除"即将回收"集合,等待回收。

2.2 简述 java 垃圾回收机制?

在 java 中,程序员是不需要显示的去释放一个对象的内存的,而是由虚拟机自行执行。在JVM 中,有一个垃圾回收线程,它是低优先级的,在正常情况下是不会执行的,只有在虚拟机空闲或者当前堆内存不足时,才会触发执行,扫面那些没有被任何引用的对象,并将它们添加到要回收的集合中,进行回收。

2.3 垃圾回收有什么目的?什么时候进行垃圾回收?

垃圾回收是在内存中存在没有引用的对象或超过作用域的对象时进行的。

垃圾回收的目的是识别并且丢弃应用不再使用的对象来释放和重用资源。

2.4 如果对象的引用被置为null,垃圾收集器是否会立即释放对象占用的内存?

不会,在下一个垃圾回调周期中,这个对象将是被可回收的。

也就是说并不会立即被垃圾收集器立刻回收,而是在下一次垃圾回收时才会释放其占用的内存。

2.5 GC是什么?为什么要GC?

GC是垃圾收集的意思,内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的显示操作方法。Java程序员不用担心内存管理,因为垃圾收集器会自动进行管理。要请求垃圾收集,可以调用下面的方法之一:System.gc() 或Runtime.getRuntime().gc() ,但JVM可以屏蔽掉显示的垃圾回收调用。 垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。垃圾回收器通常是作为一个单独的低优先级的线程运行,不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收,程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。在Java诞生初期,垃圾回收是Java最大的亮点之一,因为服务器端的编程需要有效的防止内存泄露问题,然而时过境迁,如今Java的垃圾回收机制已经成为被诟病的东西。移动智能终端用户通常觉得iOS的系统比Android系统有更好的用户体验,其中一个深层次的原因就在于android系统中垃圾回收的不可预知性。

补充:垃圾回收机制有很多种,包括:分代复制垃圾回收、标记垃圾回收、增量垃圾回收等方式。标准的Java进程既有栈又有堆。栈保存了原始型局部变量,堆保存了要创建的对象。Java平台对堆内存回收和再利用的基本算法被称为标记和清除,但是Java对其进行了改进,采用"分代式垃圾收集"。这种方法会跟Java对象的生命周期将堆内存划分为不同的区域,在垃圾收集过程中,可能会将对象移动到不同区域:

  • 伊甸园(Eden):这是对象最初诞生的区域,并且对大多数对象来说,这里是它们唯一存在过的区域。
  • 幸存者乐园(Survivor):从伊甸园幸存下来的对象会被挪到这里。
  • 终身颐养园(Tenured):这是足够老的幸存对象的归宿。年轻代收集(Minor-GC)过程是不会触及这个地方的。当年轻代收集不能把对象放进终身颐养园时,就会触发一次完全收集(Major-GC),这里可能还会牵扯到压缩,以便为大对象腾出足够的空间。 与垃圾回收相关的JVM参数:

-Xms / -Xmx:堆的初始大小 / 堆的最大大小

-Xmn:堆中年轻代的大小

-XX:-DisableExplicitGC:让System.gc()不产生任何作用

-XX:+PrintGCDetails:打印GC的细节

-XX:+PrintGCDateStamps:打印GC操作的时间戳

-XX:NewSize / XX:MaxNewSize: 设置新生代大小/新生代最大大小

-XX:NewRatio :可以设置老生代和新生代的比例

-XX:PrintTenuringDistribution :设置每次新生代GC后输出幸存者乐园中对象年龄的分布

-XX:InitialTenuringThreshold / -XX:MaxTenuringThreshold:设置老年代阀值的初始值和最大值

-XX:TargetSurvivorRatio:设置幸存区的目标使用率

2.6 垃圾回收的优点有那些?

  • java语言最显著的特点就是引入了垃圾回收机制,它使java程序员在编写程序时不再考虑内存管理的问题。
  • 由于有这个垃圾回收机制,java中的对象不再有"作用域"的概念,只有引用的对象才有"作用域"。
  • 垃圾回收机制有效的防止了内存泄露,可以有效的使用可使用的内存。
  • 垃圾回收器通常作为一个单独的低级别的线程运行,在不可预知的情况下对内存堆中已经死亡的或很长时间没有用过的对象进行清除和回收。

2.7 垃圾回收器的基本原理是什么?

对于GC来说,当程序员创建对象时,GC就开始监控这个对象的地址、大小以及使用情况。

2.8 垃圾回收器可以马上回收内存吗?

不会;通常,GC采用有向图的方式记录和管理堆(heap)中的所有对象。通过这种方式确定哪些对象是"可达的",哪些对象是"不可达的"。当GC确定一些对象为"不可达"时,GC就有责任回收这些内存空间。而这个回收操作时达到一定阈值或者条件之后才会触发回收;并不是实时的。

2.9 有什么办法主动通知虚拟机进行垃圾回收?

可以。程序员可以手动执行System.gc(),通知GC运行,但是Java语言规范并不保证GC一定会执行

2.10 Java 中都有哪些引用类型?

  • 强引用:发生 gc 的时候不会被回收。
  • 软引用:有用但不是必须的对象,在发生内存溢出之前会被回收。
  • 弱引用:有用但不是必须的对象,在下一次GC时会被回收。
  • 虚引用(幽灵引用/幻影引用):无法通过虚引用获得对象,用 PhantomReference 实现虚引用,虚引用的用途是在 gc 时返回一个通知。

2.11 JVM中的永久代中会发生垃圾回收吗?

垃圾回收不会发生在永久代,如果永久代满了或者是超过了临界值,会触发完全垃圾回收(Full GC)。如果你仔细查看垃圾收集器的输出信息,就会发现永久代也是被回收的。这就是为什么正确的永久代大小对避免Full GC是非常重要的原因。

java 8中,永久代被移除,取而代之的为元空间。

2.12 JVM 有哪些垃圾回收算法?

  1. 标记-清除:
    这是垃圾收集算法中最基础的,根据名字就可以知道,它的思想就是标记哪些要被回收的对象,然后统一回收。这种方法很简单,但是会有两个主要问题:1.效率不高,标记和清除的效率都很低;2.会产生大量不连续的内存碎片,导致以后程序在分配较大的对象时,由于没有充足的连续内存而提前触发一次 GC 动作。

  2. 复制算法:
    为了解决效率问题,复制算法将可用内存按容量划分为相等的两部分,然后每次只使用其中的一块,当一块内存用完时,就将还存活的对象复制到第二块内存上,然后一次性清楚完第一块内存,再将第二块上的对象复制到第一块。但是这种方式,内存的代价太高,每次基本上都要浪费一般的内存。
    于是将该算法进行了改进,内存区域不再是按照 1:1 去划分,而是将内存划分为 8:1:1 三部分,较大那份内存交 Eden 区,其余是两块较小的内存区叫 Survior 区。每次都会优先使用 Eden 区,若 Eden 区满,就将对象复制到第二块内存区上,然后清除 Eden 区,如果此时存活的对象太多,以至于 Survivor 不够时,会将这些对象通过分配担保机制复制到老年代中。(java 堆又分为新生代和老年代)

  3. 标记-整理
    该算法主要是为了解决标记-清除,产生大量内存碎片的问题;当对象存活率较高时,也解决了复制算法的效率问题。它的不同之处就是在清除对象的时候现将可回收对象移动到一端,然后清除掉端边界以外的对象,这样就不会产生内存碎片了。

  4. 分代收集
    现在的虚拟机垃圾收集大多采用这种方式,它根据对象的生存周期,将堆分为新生代和老年代。在新生代中,由于对象生存期短,每次回收都会有大量对象死去,那么这时就采用复制算法。老年代里的对象存活率较高,没有额外的空间进行分配担保,所以可以使用标记-整理 或者 标记-清除。

2.13 简述一下标记-清除算法

标记无用对象,然后进行清除回收。

标记-清除算法(Mark-Sweep)是一种常见的基础垃圾收集算法,它将垃圾收集分为两个阶段:

  • 标记阶段:标记出可以回收的对象。
  • 清除阶段:回收被标记的对象所占用的空间。

标记-清除算法之所以是基础的,是因为后面讲到的垃圾收集算法都是在此算法的基础上进行改进的。

优点:实现简单,不需要对象进行移动。

缺点:标记、清除过程效率低,产生大量不连续的内存碎片,提高了垃圾回收的频率。

标记-清除算法的执行的过程如下图所示
标记清除.png

2.14 简述一下复制算法

为了解决标记-清除算法的效率不高的问题,产生了复制算法。它把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾收集时,遍历当前使用的区域,把存活对象复制到另外一个区域中,最后将当前使用的区域的可回收的对象进行回收。

  • 优点
    按顺序分配内存即可,实现简单、运行高效,不用考虑内存碎片。

  • 缺点
    可用的内存大小缩小为原来的一半,对象存活率高时会频繁进行复制。

复制算法的执行过程如下图所示
复制算法.jpg

2.15 简述一下标记-整理算法

在新生代中可以使用复制算法,但是在老年代就不能选择复制算法了,因为老年代的对象存活率会较高,这样会有较多的复制操作,导致效率变低。标记-清除算法可以应用在老年代中,但是它效率不高,在内存回收后容易产生大量内存碎片。因此就出现了一种标记-整理算法(Mark-Compact)算法,与标记-整理算法不同的是,在标记可回收的对象后将所有存活的对象压缩到内存的一端,使他们紧凑的排列在一起,然后对端边界以外的内存进行回收。回收后,已用和未用的内存都各自一边。

  • 优点
    解决了标记-清理算法存在的内存碎片问题。

  • 缺点
    仍需要进行局部对象移动,一定程度上降低了效率。

标记-整理算法的执行过程如下图所示
标记清除压缩算法.jpg

2.16 简述一下分代收集算法

当前商业虚拟机都采用分代收集的垃圾收集算法。分代收集算法,顾名思义是根据对象的存活周期将内存划分为几块。一般包括年轻代老年代永久代

如图所示:
分代回收算法.jpg

2.17 JVM 有哪些垃圾回收器?

如果说垃圾收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。下图展示了7种作用于不同分代的收集器,其中用于回收新生代的收集器包括Serial、PraNew、Parallel Scavenge,回收老年代的收集器包括Serial Old、Parallel Old、CMS,还有用于回收整个Java堆的G1收集器。不同收集器之间的连线表示它们可以搭配使用。
垃圾回收期.jpg

  • Serial收集器(复制算法):
    新生代单线程收集器,标记和清理都是单线程,优点是简单高效;
  • ParNew收集器 (复制算法):
    新生代收并行集器,实际上是Serial收集器的多线程版本,在多核CPU环境下有着比Serial更好的表现;
  • Parallel Scavenge收集器 (复制算法):
    新生代并行收集器,追求高吞吐量,高效利用 CPU。吞吐量 = 用户线程时间/(用户线程时间+GC线程时间),高吞吐量可以高效率的利用CPU时间,尽快完成程序的运算任务,适合后台应用等对交互相应要求不高的场景;
  • Serial Old收集器 (标记-整理算法):
    老年代单线程收集器,Serial收集器的老年代版本;
  • Parallel Old收集器 (标记-整理算法):
    老年代并行收集器,吞吐量优先,Parallel Scavenge收集器的老年代版本;
  • CMS(Concurrent Mark Sweep)收集器(标记-清除算法):
    老年代并行收集器,以获取最短回收停顿时间为目标的收集器,具有高并发、低停顿的特点,追求最短GC回收停顿时间。
  • G1(Garbage First)收集器 (标记-整理算法):
    Java堆并行收集器,G1收集器是JDK1.7提供的一个新收集器,G1收集器基于"标记-整理"算法实现,也就是说不会产生内存碎片。此外,G1收集器不同于之前的收集器的一个重要特点是:G1回收的范围是整个Java堆(包括新生代,老年代),而前六种收集器回收的范围仅限于新生代或老年代。

2.18 详细介绍一下 CMS 垃圾回收器?

CMS 是英文 Concurrent Mark-Sweep 的简称,是以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾回收器。对于要求服务器响应速度的应用上,这种垃圾回收器非常适合。在启动 JVM 的参数加上"-XX:+UseConcMarkSweepGC"来指定使用 CMS 垃圾回收器。

CMS 使用的是标记-清除的算法实现的,所以在 gc 的时候回产生大量的内存碎片,当剩余内存不能满足程序运行要求时,系统将会出现 Concurrent Mode Failure,临时 CMS 会采用 Serial Old 回收器进行垃圾清除,此时的性能将会被降低。

2.19 新生代垃圾回收器和老年代垃圾回收器都有哪些?有什么区别?

  • 新生代回收器:Serial、ParNew、Parallel Scavenge
  • 老年代回收器:Serial Old、Parallel Old、CMS
  • 整堆回收器:G1

新生代垃圾回收器一般采用的是复制算法,复制算法的优点是效率高,缺点是内存利用率低;老年代回收器一般采用的是标记-整理的算法进行垃圾回收。

2.20 简述分代垃圾回收器是怎么工作的?

分代回收器有两个分区:老生代和新生代,新生代默认的空间占比总空间的 1/3,老生代的默认占比是 2/3。

新生代使用的是复制算法,新生代里有 3 个分区:Eden、To Survivor、From Survivor,它们的默认占比是 8:1:1,它的执行流程如下:

  • 把 Eden + From Survivor 存活的对象放入 To Survivor 区;
  • 清空 Eden 和 From Survivor 分区;
  • From Survivor 和 To Survivor 分区交换,From Survivor 变 To Survivor,To Survivor 变 From Survivor。

每次在 From Survivor 到 To Survivor 移动时都存活的对象,年龄就 +1,当年龄到达 15(默认配置是 15)时,升级为老生代。大对象也会直接进入老生代。

老生代当空间占用到达某个值之后就会触发全局垃圾收回,一般使用标记整理的执行算法。以上这些循环往复就构成了整个分代垃圾回收的整体执行流程。

3. JVM 类加载

3.1 JVM类加载的时机?

5种场景会触发类加载:

  1. 遇到new,getstaticputstaticinvokestatic这四条字节码指令时,如果类没有进行过初始化,则需要先触发初始化

  2. 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化,

  3. 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化

  4. 当虚拟机启动时,用户需要指定一个要执行的主类(包含main方法的类),虚拟机会先初始化这个类

  5. 当时用,JDK1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例后的解析结果是REF_getStaticREF_putStaticREF_invokeStatic的方法句柄,并且这个方法句柄对应的类还没有进行过初始化,则需要先触发其初始化

3.2 JVM类加载过程?

类从被加载到虚拟机内存开始,到卸载出内存为止,整个生命周期包括:加载验证准备解析初始化使用卸载

加载

加载是类加载过程的一个阶段,在加载阶段虚拟机需要完成三件事

  1. 通过一个类的全限定名来获取定义此类的辅而进之字节流
  2. 将字节流所代表的的静态存储结构转化为方法区的运行时数据结构
  3. 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口

验证

验证就是确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全

  1. 文件格式验证
    验证字节流是否符合Class文件格式的规范
  2. 元数据验证
    对字节码描述的信息进行语义分析(注意:对比javac编译阶段的语义分析),以保证其描述的信息符合Java语言规范的要求
  3. 字节码验证
    通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的
  4. 符号引用验证
    确保解析动作能正确执行。

准备

准备阶段是正式为类静态变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都在方法区中进行分配

public static int value = 123 //在准备阶段 value的值是 0 并不是123public static final int value = 123 // 准备阶段value 的值为123

如果属性有Constant Value 属性,那么在准备阶段变量就会被初始化为所指定的值

这时候进行内存分配的仅包括类变量(static),而不包括实例变量,实例变量会在对象实例化时随着对象一块分配在Java堆中

这里所设置的初始值通常情况下是数据类型默认的零值(如0、0L、null、false等),而不是被在Java代码中被显式地赋予的值

解析

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程

  • 直接引用
    直接引用可以使直接指向目标的指针,相对偏移量或是一个能间接定位到目标的句柄

  • 符号引用
    符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义的定位到目标即可

主要包含:

  • 类或接口的解析

  • 字段解析

  • 类方法解析

  • 接口方法解析

初始化

初始化,为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始化。在Java中对类变量进行初始值设定有两种方式:

  1. 声明类变量是指定初始值
  2. 使用静态代码块为类变量指定初始值

JVM初始化步骤:

  1. 假如这个类还没有被加载和连接,则程序先加载并连接该类
  2. 假如该类的直接父类还没有被初始化,则先初始化其直接父类
  3. 假如类中有初始化语句,则系统依次执行这些初始化语句

类的初始化

  1. 创建类的实例,也就是new的方式
  2. 访问某个类或接口的静态变量,或者对该静态变量赋值
  3. 调用类的静态方法
  4. 反射(如Class.forName("com.shengsiyuan.Test"))
  5. 初始化某个类的子类,则其父类也会被初始化
  6. Java虚拟机启动时被标明为启动类的类(Java Test),直接使用java.exe命令来运行某个主类

卸载

  1. 执行了 System.exit()方法
  2. 程序正常执行结束
  3. 程序在执行过程中遇到了异常或错误而异常终止
  4. 由于操作系统出现错误而导致Java虚拟机进程终止

3.3 JVM加载Class文件的原理机制是什么?

Java中的所有类,都需要由类加载器装载到JVM中才能运行。类加载器本身也是一个类,而它的工作就是把class文件从硬盘读取到内存中。在写程序的时候,我们几乎不需要关心类的加载,因为这些都是隐式装载的,除非我们有特殊的用法,像是反射,就需要显式的加载所需要的类。

类装载方式,有两种 :

  1. 隐式装载
    程序在运行过程中当碰到通过new 等方式生成对象时,隐式调用类装载器加载对应的类到jvm中,

  2. 显式装载,
    通过class.forname()等方法,显式加载需要的类

Java类的加载是动态的,它并不会一次性将所有类全部加载后再运行,而是保证程序运行的基础类(像是基类)完全加载到jvm中,至于其他类,则在需要的时候才加载。这当然就是为了节省内存开销。

3.4 什么是类加载器,类加载器有哪些?

实现通过类的权限定名获取该类的二进制字节流的代码块叫做类加载器。

主要有一下四种类加载器:

  1. 启动类加载器(Bootstrap ClassLoader)用来加载java核心类库,无法被java程序直接引用。
  2. 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
  3. 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。
  4. 用户自定义类加载器,通过继承 java.lang.ClassLoader类的方式实现。

3.5 什么是双亲委派模型?

在介绍双亲委派模型之前先说下类加载器。对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立在 JVM 中的唯一性,每一个类加载器,都有一个独立的类名称空间。类加载器就是根据指定全限定名称将 class 文件加载到 JVM 内存,然后再转化为 class 对象。
双亲委派.png

类加载器分类:

  • 启动类加载器(Bootstrap ClassLoader),是虚拟机自身的一部分,用来加载Java_HOME/lib/目录中的,或者被 -Xbootclasspath 参数所指定的路径中并且被虚拟机识别的类库;
  • 其他类加载器:
  • 扩展类加载器(Extension ClassLoader):负责加载\lib\ext目录或Java. ext. dirs系统变量指定的路径中的所有类库;
  • 应用程序类加载器(Application ClassLoader)。负责加载用户类路径(classpath)上的指定类库,我们可以直接使用这个类加载器。一般情况,如果我们没有自定义类加载器默认就是用这个加载器。

双亲委派模型:如果一个类加载器收到了类加载的请求,它首先不会自己去加载这个类,而是把这个请求委派给父类加载器去完成,每一层的类加载器都是如此,这样所有的加载请求都会被传送到顶层的启动类加载器中,只有当父加载无法完成加载请求(它的搜索范围中没找到所需的类)时,子加载器才会尝试去加载类。

当一个类收到了类加载请求时,不会自己先去加载这个类,而是将其委派给父类,由父类去加载,如果此时父类不能加载,反馈给子类,由子类去完成类的加载。

4. JVM调优

4.1 用过那些JVM 调优的工具?

常用调优工具分为两类,jdk自带监控工具:jconsole和jvisualvm,第三方有:MAT(Memory Analyzer Tool)、GChisto。

  • jconsole
    Java Monitoring and Management Console是从java5开始,在JDK中自带的java监控和管理控制台,用于对JVM中内存,线程和类等的监控
  • jvisualvm
    jdk自带全能工具,可以分析内存快照、线程快照;监控内存变化、GC变化等。
  • MAT
    Memory Analyzer Tool,一个基于Eclipse的内存分析工具,是一个快速、功能丰富的Java heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗
  • GChisto
    一款专业分析gc日志的工具

4.2 常用的 JVM 调优的参数都有哪些?

  • -Xms2g
    初始化推大小为 2g;
  • -Xmx2g
    堆最大内存为 2g;
  • -XX:NewRatio=4
    设置年轻的和老年代的内存比例为 1:4;
  • -XX:SurvivorRatio=8
    设置新生代 Eden 和 Survivor 比例为 8:2;
  • –XX:+UseParNewGC
    指定使用 ParNew + Serial Old 垃圾回收器组合;
  • -XX:+UseParallelOldGC
    指定使用 ParNew + ParNew Old 垃圾回收器组合;
  • -XX:+UseConcMarkSweepGC
    指定使用 CMS + Serial Old 垃圾回收器组合;
  • -XX:+PrintGC
    开启打印 gc 信息;
  • -XX:+PrintGCDetails
    打印 gc 详细信息。

4.3 如何分析GC日志?

摘录GC日志一部分(前部分为年轻代gc回收;后部分为full gc回收):

20xx-0x-0xT10:43:18.093+0800: 25.395: [GC [PSYoungGen: 274931K->10738K(274944K)] 371093K->147186K(450048K), 0.0668480 secs] [Times: user=0.17 sys=0.08, real=0.07 secs] 20xx-0x-0xT10:43:18.160+0800: 25.462: [Full GC [PSYoungGen: 10738K->0K(274944K)] [ParOldGen: 136447K->140379K(302592K)] 147186K->140379K(577536K) [PSPermGen: 85411K->85376K(171008K)], 0.6763541 secs] [Times: user=1.75 sys=0.02, real=0.68 secs]

通过上面日志分析得出,PSYoungGen、ParOldGen、PSPermGen属于Parallel收集器。其中PSYoungGen表示gc回收前后年轻代的内存变化;ParOldGen表示gc回收前后老年代的内存变化;PSPermGen表示gc回收前后永久区的内存变化。young gc 主要是针对年轻代进行内存回收比较频繁,耗时短;full gc 会对整个堆内存进行回城,耗时长,因此一般尽量减少full gc的次数

4.4 JVM调优命令有那些?

Sun JDK监控和故障处理命令有jps jstat jmap jhat jstack jinfo

  • jps
    JVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程。
  • jstat
    JVM statistics Monitoring是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。
  • jmap
    JVM Memory Map命令用于生成heap dump文件
  • jhat
    JVM Heap Analysis Tool命令是与jmap搭配使用,用来分析jmap生成的dump,jhat内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看
  • jstack
    用于生成java虚拟机当前时刻的线程快照。
  • jinfo
    JVM Configuration info 这个命令作用是实时查看和调整虚拟机运行参数。

4.5 你知道哪些JVM性能调优方式有那些?

  • 设定堆内存大小
    -Xmx:堆内存最大限制。

  • 设定新生代大小。 新生代不宜太小,否则会有大量对象涌入老年代
    -XX:NewSize:新生代大小

    -XX:NewRatio 新生代和老生代占比

    -XX:SurvivorRatio:伊甸园空间和幸存者空间的占比

  • 设定垃圾回收器
    年轻代用 -XX:+UseParNewGC 年老代用-XX:+UseConcMarkSweepGC

本文由博客一文多发平台 OpenWrite 发布!









原文转载:http://www.shaoqun.com/a/521201.html

跨境电商:https://www.ikjzd.com/

上海跨境通:https://www.ikjzd.com/w/1329

淘粉:https://www.ikjzd.com/w/1725


推荐使用小程序阅读为了能让您更加方便的阅读本文所有的面试题目均已整理至小程序《面试手册》可以通过微信扫描(或长按)下图的二维码享受更好的阅读体验!目录推荐使用小程序阅读1.JVM基础1.1JVM内存分哪几个区,每个区的作用是什么?方法区虚拟机栈:本地方法栈堆程序计数器1.2对象的访问定位有几种方式?句柄访问直接指针访问1.3JVM内存模型是什么?1.4finalize()方法什么时候被调用?析构函
吴佳:吴佳
亚马逊全球开店制造:亚马逊全球开店制造
写给新手卖家,这些Wish的基本算法你得get到:写给新手卖家,这些Wish的基本算法你得get到
凭技术做外贸:公司官网内容优化的4个关键点 :凭技术做外贸:公司官网内容优化的4个关键点
关于PB的一些基础知识点详解:关于PB的一些基础知识点详解

篮网好狠!半场76分,哈登有了出手权真无解,双巨头才更恐怖?_雷霆

原标题:篮网好狠!半场76分,哈登有了出手权真无解,双巨头才更恐怖?

NBA常规赛,篮网与雷霆的比赛,杜兰特缺席比赛的情况下,篮网半场拿下了76分,不得不说,篮网的双巨头,有时候比三巨头还恐怖,因为出手权更平衡?

开场后篮网0-5落后,很快哈登率队反扑,哈里斯三分命中实现反超。哈登同样命中三分,欧文高位跳投继续领先。布鲁斯-布朗上篮不中,小乔丹抢到前场篮板双手暴扣。篮网的进攻渐入佳境,哈登借掩护后蛇形突破上篮打进并造成对手犯规,此役没有杜兰特,因此哈登获得了更多的出手机会,首节哈登表现不错,得到10分。欧文超强滞空精彩上进,单节篮网36分。

第二节开场后短短2分17秒,哈登迎来了一个小爆发,他连得7分,还助攻沙梅特投进三分,带队打出一波10-0的攻势瞬间拉开分差,雷霆落后接近20分只能被迫叫暂停。回到比赛后沙梅特又一个三分命中,哈登助力杰夫-格林冲击篮筐拉开24分的分差,多特依靠三分与三罚帮助雷霆止血,哈登甩篮下,佩里暴扣得分!

篮网的攻势一浪高过一浪,哈里斯命中第三记三分,欧文随后续上,雷霆还不容易缩小的分差又被拉开到超过二十分。半场前杰夫-格林三分命中,而雷霆抓住最后的机会缩小比分,先是打成1个2分,随后开球杰夫-格林失误,雷霆又制造犯规2罚全中,一口气追回4分。即便如此,篮网超强的火力让球队依然在半场拿下76分,领先17分。

获得了球权的哈登半场拿下17分,有了出手的哈登不会让人失望。或许,篮网的"双巨头"才是最可怕的?毕竟,三巨头都是得分高手,谁少了出手都是损失。接下来,纳什要考虑的问题依然是出手权与头疼的防守。返回搜狐,查看更多

责任编辑:

原文转载:http://sport.shaoqun.com/a/389626.html

跨境电商:https://www.ikjzd.com/

贝恩:https://www.ikjzd.com/w/1336

亿恩:https://www.ikjzd.com/w/1461


原标题:篮网好狠!半场76分,哈登有了出手权真无解,双巨头才更恐怖?NBA常规赛,篮网与雷霆的比赛,杜兰特缺席比赛的情况下,篮网半场拿下了76分,不得不说,篮网的双巨头,有时候比三巨头还恐怖,因为出手权更平衡?开场后篮网0-5落后,很快哈登率队反扑,哈里斯三分命中实现反超。哈登同样命中三分,欧文高位跳投继续领先。布鲁斯-布朗上篮不中,小乔丹抢到前场篮板双手暴扣。篮网的进攻渐入佳境,哈登借掩护后蛇形
www.made-in-china.com:www.made-in-china.com
feedback:feedback
关于亚马逊账号关联大家知道多少?这些一定要避免:关于亚马逊账号关联大家知道多少?这些一定要避免
UPS欲与阿里、亚马逊、京东上演印度、东南亚大乱斗!:UPS欲与阿里、亚马逊、京东上演印度、东南亚大乱斗!
谷歌与亚马逊的七年之战,终于动摇亚马逊"世界第一"地位?:谷歌与亚马逊的七年之战,终于动摇亚马逊"世界第一"地位?

null调整为not null default xxx,不得不注意的坑

最近碰到一个case,值得分享一下。

 

现象

一个DDL,将列的属性从null调整为not null default xxx,

alter table slowtech.t1 modify name varchar(10) not null default 'slowtech';

通过平台执行(平台调用的是pt-online-schema-change)。

但在执行的过程中,业务SQL报错,提示"ERROR 1048 (23000): Column 'name' cannot be null"。

 

在剖析具体的问题之前,首先,我们看看pt-online-schema-change的原理。

 

PT-OSC的实现原理

 

从原理图中可以看到,

1.  对于全量数据的同步,pt-online-schema-change是以chunk为单位分批来拷贝的。

2.  对于增量数据的同步,pt-online-schema-change是通过触发器来实现的。

 

结合pt-online-schema-change的原理,我们来重现下问题场景。

mysql> create table slowtech.t1(id int primary key,name varchar(10));mysql> create table slowtech._t1_new(id int primary key,name varchar(10));mysql> alter table slowtech._t1_new modify name varchar(10) not null default 'slowtech';mysql> create trigger slowtech.`pt_osc_slowtech_t1_ins` after insert on `slowtech`.`t1` for each row replace into `slowtech`.`_t1_new` (`id`, `name`) values (new.`id`, new.`name`);mysql> insert into slowtech.t1(id) values(1);ERROR 1048 (23000): Column 'name' cannot be null

问题完美呈现,有的童鞋可能会有疑问,t1的name列默认不是null么?为什么不允许null值的插入?

 

问题原因

问题出在触发器上面。

触发器会将业务SQL("insert into slowtech.t1(id) values(1)")和触发操作("replace into slowtech._t1_new (id, name) values(1, null)")放到一个事务内执行。

"insert into slowtech.t1(id) values(1)"并不违反t1表的约束,但违反了_t1_new表的约束。

 

通过上面的分析,我们得到了两点启示:

1.  类似DDL(将列的属性从null修改为not null default 'abc')要注意。

从原理上看,既然涉及到全量数据+增量数据的同步,都会存在这种问题,不单单是pt-online-schema-change,包括Online DDL,gh-ost同样如此。

只不过,触发器这种方案会将业务SQL和触发操作耦合在一起,相对来说,对业务有一定的侵入性。

 

2. 既然触发器会将业务SQL和触发操作放到一个事务内执行,如果pt-online-schema-change异常退出,留下了触发器和中间表(_t1_new),在清理现场时,应首先删除触发器,再删除中间表。

如果首先删除中间表,会导致针对原表的所有DML操作失败。

mysql> drop table slowtech._t1_new;mysql> insert into slowtech.t1 values(1,'victor');ERROR 1146 (42S02): Table 'slowtech._t1_new' doesn't exist

 

数据拷贝也有坑

在执行DDL之前,还有一段小插曲。

在执行DDL之前,开发提单将该列的null值修改为了默认值。这样就导致了,问题是在业务SQL插入的过程中暴露的,而不是在数据拷贝过程中暴露。

在数据拷贝的过程中,如果拷贝的数据中,该列存在null值,pt-online-schema-change会直接报错退出。

mysql> create table slowtech.t1(id int primary key,name varchar(10));mysql> insert into slowtech.t1(id) values(1);# pt-online-schema-change h=xxxxx,u=root,p=123456,D=slowtech,t=t1 --alter "modify name varchar(10) not null default 'slowtech'" --executeNo slaves found. See --recursion-method if host xxxx has slaves.Not checking slave lag because no slaves were found and --check-slave-lag was not specified.Operation, tries, wait: analyze_table, 10, 1 copy_rows, 10, 0.25 create_triggers, 10, 1 drop_triggers, 10, 1 swap_tables, 10, 1 update_foreign_keys, 10, 1Altering `slowtech`.`t1`...Creating new table...Created new table slowtech._t1_new OK.Altering new table...Altered `slowtech`.`_t1_new` OK.2020-09-07T09:13:25 Creating triggers...2020-09-07T09:13:25 Created triggers OK.2020-09-07T09:13:25 Copying approximately 1 rows...2020-09-07T09:13:25 Dropping triggers...2020-09-07T09:13:25 Dropped triggers OK.2020-09-07T09:13:25 Dropping new table...2020-09-07T09:13:25 Dropped new table OK.`slowtech`.`t1` was not altered.  (in cleanup) 2020-09-07T09:13:25 Error copying rows from `slowtech`.`t1` to `slowtech`.`_t1_new`: 2020-09-07T09:13:25 Copying rows caused a MySQL error 1048: Level: Warning  Code: 1048 Message: Column 'name' cannot be null Query: INSERT LOW_PRIORITY IGNORE INTO `slowtech`.`_t1_new` (`id`, `name`) SELECT `id`, `name` FROM `slowtech`.`t1` LOCK IN SHARE MODE /*pt-online-schema-change 9234 copy table*/2020-09-07T09:13:25 Dropping triggers...2020-09-07T09:13:25 Dropped triggers OK.`slowtech`.`t1` was not altered.

 

上述报错,pt-online-schema-change加个参数即可规避(--null-to-not-null)。

在实现上,该参数会忽略1048错误,此时,对于字符类型的列,会填充空字符,对于数字类型的列,会填充0。

mysql> create table slowtech.t1(id int primary key,name varchar(10));mysql> create table slowtech._t1_new(id int primary key,name varchar(10));mysql> alter table slowtech._t1_new modify name varchar(10) not null default 'slowtech';mysql> insert into slowtech.t1(id) values(1);mysql> select * from slowtech.t1;+----+------+| id | name |+----+------+| 1 | NULL |+----+------+1 row in set (0.00 sec)mysql> insert low_priority ignore into slowtech._t1_new (id, name) select id, name from slowtech.t1 lock in share mode;Query OK, 1 row affected, 1 warning (0.01 sec)Records: 1 Duplicates: 0 Warnings: 1mysql> show warnings;+---------+------+------------------------------+| Level | Code | Message      |+---------+------+------------------------------+| Warning | 1048 | Column 'name' cannot be null |+---------+------+------------------------------+1 row in set (0.00 sec)mysql> select * from slowtech._t1_new;+----+------+| id | name |+----+------+| 1 |  |+----+------+1 row in set (0.00 sec)

所以,线上使用该参数要注意,要确认被填充的值是否符合自己的预期行为。

 

从目前的分析来看,要将一个列的属性从null直接修改为not null default xxx,几乎是不可能的,除非:

1.  该列不存在null值。

2.  在DDL的过程中,没有类似于"insert into slowtech.t1(id) values(1)"的业务SQL出现。

 

结论

很显然,这两个条件很难同时满足。既然如此,这个需求还能实现吗?能!只不过比较复杂。

下面,看看具体的实施步骤。

1. 首先,将列的属性调整为null default xxx,这样做的目的是为了避免增量同步过程中,类似"insert into slowtech.t1(id) values(1)"的业务SQL,产生新的null值。

2. 其次,手动将null值调整为默认值。需要注意的是,如果记录数较多,这一步的操作难度也是极大的。

3. 最后,将列的属性调整为not null default xxx。

 

对于not null default xxx的正确理解

在很多数据库规范里面,都推荐将列定义为not null default xxx,但很多童鞋,对这段定义的实际效果却相当模糊。

下面具体来说说,这段定义的实际作用。这段定义实际上由两部分组成:

1.  not null,约束,指的是不可显式插入null值,如,

mysql> create table slowtech.t1(id int primary key,name varchar(10) not null default 'slowtech');mysql> insert into slowtech.t1 values(1,null);ERROR 1048 (23000): Column 'name' cannot be null

2.  default 'slowtech',如果在插入时,没有显式指定值,则以默认值填充。

mysql> insert into slowtech.t1(id) values(1);mysql> select * from slowtech.t1;+----+----------+| id | name  |+----+----------+| 1 | slowtech |+----+----------+1 row in set (0.00 sec)

可以看到,这两部分其实没有任何关系,对于一个列,我们同样可以定义为null default xxx。

 









原文转载:http://www.shaoqun.com/a/521177.html

跨境电商:https://www.ikjzd.com/

易速:https://www.ikjzd.com/w/2389

文化衫事件:https://www.ikjzd.com/w/1932


最近碰到一个case,值得分享一下。现象一个DDL,将列的属性从null调整为notnulldefaultxxx,altertableslowtech.t1modifynamevarchar(10)notnulldefault'slowtech';通过平台执行(平台调用的是pt-online-schema-change)。但在执行的过程中,业务SQL报错,提示"ERROR1048(
stylenanda官网:stylenanda官网
acedota:acedota
Wish写产品描述的时候一定要谨慎,违反政策可是要罚款的!:Wish写产品描述的时候一定要谨慎,违反政策可是要罚款的!
亚马逊店铺如何运营?亚马逊店铺注意事项有哪些?:亚马逊店铺如何运营?亚马逊店铺注意事项有哪些?
跨境卖家注意:利用Review提高转化率,不要忽略这5个窍门!:跨境卖家注意:利用Review提高转化率,不要忽略这5个窍门!

2021年1月29日星期五

我们,让9300万人办事少跑一趟

科技发展的好处,体现在很多方面,其中就包括出门办事。

随着政府推行数字化政务建设,利用网络技术的力量,群众办事麻烦的问题得到了很大改善,群众出门办事可以少跑一趟了。

而少跑的这一趟,可能就是腾讯云数据库替你完成的。

20年12月24日,腾讯云数据库2020年度盛典上,我们有幸请到了数字广东网络建设有限公司运维部总经理徐哲为大家分享《政务数据库从应用到平台的演进》,以下是演讲全文:

各位来宾大家下午好,我是来自于数字广东网络建设有限公司的徐哲,来自于建设运维部,非常荣幸今天下午有这样一个机会跟大家分享一下国产数据库在数字广东的政务云平台上的应用以及一些实例的分享。

我今天演讲的题目是《政务数据库从应用到平台的演进》,本次演讲主要是基于我们在政务行业应用数据库的实践进行一些案例的分享。现在政务行业的数据库应用已经从原来的建设模式到了平台的模式,也就是说现在政府它的数据库应用建设已经从建设的模式到了购买服务的模式,所以这就要求我们在政务行业方面要迎合政府的集约化的要求来进行建设。

本次演讲会分四个方面进行介绍,首先介绍一下数字广东这个公司,数字广东公司成立于2017年,大背景是我们的领导人在《全国网络安全和信息化会议》的工作会议中指出我们要运用信息化的手段进行政务公开、党务公开,以推进电子政务,构建全流程一体化的在线服务平台,更好解决群众反映强烈的办事难、办事慢、办事繁的问题。克强总理也提出来深化放管服改革,推进数字政务建设,在这个背景下数字广东这个公司就成立了,我们积极探索政务信息化和数字化建设的模式。

数字广东成立三年以来我们有过很多的里程碑,包括我们的政务云的建设以及核心产品粤系列:粤省事、粤政易和粤商通。

我公司成立以来我们独创性提出基础平台,应用平台和用户"3+3+3"的模式,首先在基础平台方面我们依靠政务云,政务大数据和公共支撑平台的支撑,自主开发了粤省事、粤政易、粤商通这三个拳头产品,粤省事针对的服务对象就是民众和公众,粤商通针对的是企业和法人,粤政易针对的是公务员以及事业单位人员。

随着政务信息化,数字化的不断推进,政府对数据库产品的需求也在持续的提升,相关的应用也在不断的升级和演进,就我个人理解在政务行业里边,数据库的应用大致经历了这样四个阶段。

第一个阶段就是大杂烩的阶段,产品的种类非常繁多,版本型号也非常多,数据的烟囱也是非常的多。

第二个阶段就是数据库的应用慢慢的向Oracle和SQL Server等收拢和聚合。但是各个单位内部仍然没有整体数据库方面的规划,数据的共享效果也不是很好。

第三个阶段虽然各个单位的各个部门对于数据库有了一定的整体的规划,但是单位作为一个整体,它仍然没有一个统一的数据库规划,数据共享的效率虽然有了一定的提升,但是壁垒依然存在。

第四个阶段也就是现在我们在政务云上我们的状态,我们对数据库的应用进行了统一的规划,数据库的应用已经平台化了,也就是说各个厅局委办,他们的数据库应用应该是尽可能的向我们的公共资源平台,公共的数据库资源平台去部署,这样的话数据共享的效率就会大幅的提升,在一定程度上也打破了数据的壁垒,同时国产化的替代也大大地加速。

就目前来讲广东省的数字政府建设的基础平台是我们的政务云平台,它上面跑着五十多个省直单位的数据库,数据库的规模已经超过了两万多核,内存超过了13万GB,存储超过4个PB,我们政务云机房采用两地三中心的模式。

接下来我跟大家分享一下腾讯公司的数据库在我们政务行业应用方面的一些实际案例。

第一个案例是在政务大数据中心方面,我们的政务大数据中心用的是腾讯公司的TBase和TBDS,每秒处理的交易量超过五万个,通过这张图可以看到我们以应用为驱动,建设了省市一体化的大数据中心,大数据中心每秒处理交易量超过5万次,同时通过流程再造和决策优化,实现民众少跑路,数据多跑路。

第二个案例是高并发的应用,代表应用是数字广东公司的粤省事产品,这个应用是针对一般的普通民众,我们的口号是服务民生,指间可达,这个产品目前来讲我们的实名用户接近了9300万,疫情期间高峰时段的访问量超过了1700万次每小时,一天总访问量达到一亿八千万次,在行业内处于领先水平。

第二个应用的场景应该是工作流型的workflow,我们的广东政务服务网背后支撑的是腾讯公司的TData跟TDSQL,底层数据库的支撑数字广东开展政务数字化建设,这也是数字广东公司开展数字化政务平台建设的一个探索。

第三个是针对企业和法人的应用"粤商通",服务了近600万个商事主体,每秒查询量可以达到14万次,每秒事务量可以达到2.6万次。而这背后,均离不开腾讯云TDSQL提供的底层技术支持。

最后一个是粤政易平台,这个是针对政府单位公务员移动办公平台,截止到昨天,它的用户量已经接近了180万,大家可以看到无论是刚才我讲到的,无论是分析型、高并发型,还有我们工作流型、综合型,所有的这些应用背后都是要靠着强大的数据库来支撑我们这些业务。国产化数据库重点替代代表就是TDSQL,从数据库到中间件,都可以实现全部的国产化替代了。

所以在新的环境下,政务数据库管理模式,我总结了以下这几个特点:

第一,政务环境里面首先要实现管运分离。

第二,政府对我们的要求是要实现集约化,集约化的部署,集约化的运维,要提高效率。

第三,租户隔离,在安全性方面,要有实质性的提升。要实现平台和数据库双层等保评测,和全平台的安全管控。

随着政府业务需求的不断变化,我认为数据库应用也会持续迭代。在政务行业发展趋势已经初见端倪,未来,在政务行业数据库有这样四个方向:一个是云化,第二是国产化,第三是海量高并发,因为它集约了,最后也是一个可见的趋势,就是要人工智能化,与人工智能高度融合

以上就是我在政务行业的经验一些个人的体会,希望后续能够跟业内的人士更多的交流,我们一起共同进步,推动数据库的国产化走向一个新的台阶,谢谢大家!

本文由博客一文多发平台 OpenWrite 发布!









原文转载:http://www.shaoqun.com/a/521159.html

跨境电商:https://www.ikjzd.com/

gem:https://www.ikjzd.com/w/1997

eori:https://www.ikjzd.com/w/499


科技发展的好处,体现在很多方面,其中就包括出门办事。随着政府推行数字化政务建设,利用网络技术的力量,群众办事麻烦的问题得到了很大改善,群众出门办事可以少跑一趟了。而少跑的这一趟,可能就是腾讯云数据库替你完成的。20年12月24日,腾讯云数据库2020年度盛典上,我们有幸请到了数字广东网络建设有限公司运维部总经理徐哲为大家分享《政务数据库从应用到平台的演进》,以下是演讲全文:各位来宾大家下午好,我是
retriever:retriever
hunter:hunter
收藏|2019年eBay卖家日历:收藏|2019年eBay卖家日历
市值1200万的洗漱用品品牌,背后是这样的一个故事:市值1200万的洗漱用品品牌,背后是这样的一个故事
突发!运抵海外防疫物资出现被无端征用现象!多国"紧急状态法"生效:突发!运抵海外防疫物资出现被无端征用现象!多国"紧急状态法"生效