Java 7:HashMap与ConcurrentHashMap

从我过去有关性能的文章和HashMap案例研究中可能已经看到,Java线程安全性问题可以很轻松地使Java EE应用程序和Java EE容器崩溃。 在对Java EE性能问题进行故障排除时,我观察到的最常见问题之一是由非线程安全的HashMap get()和put()操作触发的无限循环。 这个问题已经有好几年了,但是最近的生产问题迫使我不得不再次讨论这个问题。

本文将重温这个经典的线程安全问题,并使用一个简单的Java程序演示与并发线程上下文中涉及的普通旧java.util.HashMap数据结构的错误使用有关的风险。

此概念验证练习将尝试实现以下三个目标:

  • 重新访问和比较非线程安全和线程安全Map数据结构实现(HashMap,Hashtable,同步的HashMap,ConcurrentHashMap)之间的Java程序性能级别
  • 使用每个人都可以编译,运行和理解的简单Java程序,复制并演示HashMap无限循环问题
  • 回顾上述Map数据结构在现实和现代Java EE容器实现(例如JBoss AS7)中的用法

有关ConcurrentHashMap实现策略的更多详细信息,我强烈推荐Brian Goetz撰写的出色文章。

工具和服务器规格

首先,请找到以下用于练习的不同工具和软件:

  • Sun / Oracle JDK和JRE 1.7 64位
  • Eclipse Java EE IDE
  • Windows Process Explorer(每个Java线程关联的CPU)
  • JVM线程转储(阻塞的线程分析和每个线程的CPU相关性)

以下本地计算机用于问题复制过程和性能测量:

  • 英特尔(R)酷睿TM i5-2520M CPU @ 2.50Ghz(2个CPU内核,4个逻辑内核)
  • 8 GB内存
  • Windows 7 64位

* Java程序的结果和性能可能会因您的工作站或服务器规格而异。

Java程序

为了帮助我们实现上述目标,按如下方式创建了一个简单的Java程序:

  • Java主程序是HashMapInfiniteLoopSimulator.java
  • 还创建了一个工作线程类WorkerThread.java

该程序正在执行以下操作:

  • 初始化大小为2的不同静态Map数据结构
  • 将选定的Map分配给工作线程(您可以在4个Map实现中进行选择)
  • 创建一定数量的工作线程(根据标头配置)。 为此概念证明创建了3个工作线程NB_THREADS = 3;
  • 这些工作线程中的每一个都有相同的任务:使用介于1到1000000之间的随机 Integer元素查找并在分配的Map数据结构中插入新元素。
  • 每个辅助线程执行此任务共计500K次迭代
  • 整个程序执行50次迭代,以便为HotSpot JVM提供足够的启动时间
  • 并发线程上下文是使用JDK ExecutorService实现的

如您所见,Java程序任务相当简单,但是足够复杂以生成以下关键条件:

  • 针对共享/静态Map数据结构生成并发
  • 混合使用get()和put()操作,以尝试触发内部锁和/或内部损坏(对于非线程安全的实现)
  • 使用较小的Map初始大小2,强制内部HashMap触发内部重新哈希/调整大小

最后,可以方便地修改以下参数:

##工作线程数

private static final int NB_THREADS = 3;

## Java程序迭代次数

private static final int NB_TEST_ITERATIONS = 50;

##地图数据结构分配。 您可以选择4种结构

// Plain old HashMap (since JDK 1.2)
threadSafeMap1 = new Hashtable<String, Integer>(2);// Plain old Hashtable (since JDK 1.0)
threadSafeMap1 = new Hashtable<String, Integer>(2);// Fully synchronized HashMap
threadSafeMap2 = new HashMap<String, Integer>(2);
threadSafeMap2 = Collections.synchronizedMap(threadSafeMap2);// ConcurrentHashMap (since JDK 1.5)
threadSafeMap3 = new ConcurrentHashMap<String, Integer>(2);/*** Assign map at your convenience ****/
assignedMapForTest = threadSafeMap3;

现在,在下面找到我们示例程序的源代码。

#### HashMapInfiniteLoopSimulator.java
package org.ph.javaee.training4;import java.util.Collections;
import java.util.Map;
import java.util.HashMap;
import java.util.Hashtable;import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** HashMapInfiniteLoopSimulator* @author Pierre-Hugues Charbonneau**/
public class HashMapInfiniteLoopSimulator {private static final int NB_THREADS = 3;private static final int NB_TEST_ITERATIONS = 50;private static Map<String, Integer> assignedMapForTest = null;private static Map<String, Integer> nonThreadSafeMap = null;private static Map<String, Integer> threadSafeMap1 = null;private static Map<String, Integer> threadSafeMap2 = null;private static Map<String, Integer> threadSafeMap3 = null;/*** Main program* @param args*/public static void main(String[] args) {System.out.println("Infinite Looping HashMap Simulator");System.out.println("Author: Pierre-Hugues Charbonneau");System.out.println("http://javaeesupportpatterns.blogspot.com");for (int i=0; i<NB_TEST_ITERATIONS; i++) {// Plain old HashMap (since JDK 1.2)nonThreadSafeMap = new HashMap<String, Integer>(2);// Plain old Hashtable (since JDK 1.0)threadSafeMap1 = new Hashtable<String, Integer>(2);// Fully synchronized HashMapthreadSafeMap2 = new HashMap<String, Integer>(2);threadSafeMap2 = Collections.synchronizedMap(threadSafeMap2);// ConcurrentHashMap (since JDK 1.5)threadSafeMap3 = new ConcurrentHashMap<String, Integer>(2); // ConcurrentHashMap/*** Assign map at your convenience ****/assignedMapForTest = threadSafeMap3;long timeBefore = System.currentTimeMillis();long timeAfter = 0;Float totalProcessingTime = null;ExecutorService executor = Executors.newFixedThreadPool(NB_THREADS);for (int j = 0; j < NB_THREADS; j++) {/** Assign the Map at your convenience **/Runnable worker = new WorkerThread(assignedMapForTest);executor.execute(worker);              }// This will make the executor accept no new threads// and finish all existing threads in the queueexecutor.shutdown();// Wait until all threads are finishwhile (!executor.isTerminated()) {}timeAfter = System.currentTimeMillis();totalProcessingTime = new Float( (float) (timeAfter - timeBefore) / (float) 1000);System.out.println("All threads completed in "+totalProcessingTime+" seconds");}}}
#### WorkerThread.java
package org.ph.javaee.training4;import java.util.Map;/*** WorkerThread** @author Pierre-Hugues Charbonneau**/
public class WorkerThread implements Runnable {private Map<String, Integer> map = null;public WorkerThread(Map<String, Integer> assignedMap) {this.map = assignedMap;}@Overridepublic void run() {for (int i=0; i<500000; i++) {// Return 2 integers between 1-1000000 inclusiveInteger newInteger1 = (int) Math.ceil(Math.random() * 1000000);Integer newInteger2 = (int) Math.ceil(Math.random() * 1000000);                    // 1. Attempt to retrieve a random Integer elementInteger retrievedInteger = map.get(String.valueOf(newInteger1));// 2. Attempt to insert a random Integer elementmap.put(String.valueOf(newInteger2), newInteger2);                }}}


线程安全的Map实现之间的性能比较

第一个目标是比较使用不同线程安全的Map实现时我们程序的性能水平:

  • 普通的旧哈希表(自JDK 1.0起)
  • 完全同步的HashMap(通过Collections.synchronizedMap())
  • ConcurrentHashMap(自JDK 1.5起)

在下面找到每个迭代的Java程序执行的图形结果以及程序控制台输出示例。

#使用ConcurrentHashMap时的输出

Infinite Looping HashMap Simulator
Author: Pierre-Hugues Charbonneau
http://javaeesupportpatterns.blogspot.com
All threads completed in 0.984 seconds
All threads completed in 0.908 seconds
All threads completed in 0.706 seconds
All threads completed in 1.068 seconds
All threads completed in 0.621 seconds
All threads completed in 0.594 seconds
All threads completed in 0.569 seconds
All threads completed in 0.599 seconds
………………

如您所见,ConcurrentHashMap在这里显然是赢家,所有3个工作线程平均仅花费半秒(在初始启动后)就可以针对指定的共享Map并在500K循环语句中同时读取和插入数据。 请注意,程序执行没有问题,例如没有挂起情况。

性能的提高肯定是由于ConcurrentHashMap性能的提高,例如无阻塞的get()操作。

其他2个Map实现的性能水平非常相似,但对于同步的HashMap而言却具有很小的优势。

HashMap无限循环问题复制

下一个目标是复制从Java EE生产环境中经常观察到的HashMap无限循环问题。 为此,您只需要按照下面的代码片段分配非线程安全的HashMap实现即可:

/*** Assign map at your convenience ****/
assignedMapForTest = nonThreadSafeMap;

使用非线程安全的HashMap按原样运行程序应导致:

  • 除程序头外无输出
  • 从系统观察到的CPU大量增加
  • Java程序有时会挂起,您将被迫杀死Java进程

发生了什么? 为了了解这种情况并确认问题,我们将使用Process Explorer和JVM Thread Dump从Windows操作系统执行每个线程的CPU分析。
1 –再次运行程序,然后按照以下方法从Process Explorer快速捕获每个CPU数据的线程。 在explorer.exe下,您需要右键单击javaw.exe并选择属性。 将显示“线程”选项卡。 我们可以看到几乎所有系统CPU都使用了4个线程。

2 –现在,您必须使用JDK 1.7 jstack实用程序快速捕获JVM线程转储。 对于我们的示例,我们可以看到我们的3个工作线程,它们似乎忙/忙于执行get()和put()操作。

..\jdk1.7.0\bin>jstack 272
2012-08-29 14:07:26
Full thread dump Java HotSpot(TM) 64-Bit Server VM (21.0-b17 mixed mode):"pool-1-thread-3" prio=6 tid=0x0000000006a3c000 nid=0x18a0 runnable [0x0000000007ebe000]java.lang.Thread.State: RUNNABLEat java.util.HashMap.put(Unknown Source)at org.ph.javaee.training4.WorkerThread.run(WorkerThread.java:32)at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)at java.lang.Thread.run(Unknown Source)"pool-1-thread-2" prio=6 tid=0x0000000006a3b800 nid=0x6d4 runnable [0x000000000805f000]java.lang.Thread.State: RUNNABLEat java.util.HashMap.get(Unknown Source)at org.ph.javaee.training4.WorkerThread.run(WorkerThread.java:29)at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)at java.lang.Thread.run(Unknown Source)"pool-1-thread-1" prio=6 tid=0x0000000006a3a800 nid=0x2bc runnable [0x0000000007d9e000]java.lang.Thread.State: RUNNABLEat java.util.HashMap.put(Unknown Source)at org.ph.javaee.training4.WorkerThread.run(WorkerThread.java:32)at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)at java.lang.Thread.run(Unknown Source)
..............

现在该按照以下方法将Process Explorer线程ID DECIMAL格式转换为HEXA格式。 HEXA值使我们可以按照以下方式映射和标识每个线程:

## TID:1748(nid = 0X6D4)

  • 线程名称:pool-1-thread-2
  • CPU @ 25.71%
  • 任务:工作线程执行HashMap.get()操作
at java.util.HashMap.get(Unknown Source)at org.ph.javaee.training4.WorkerThread.run(WorkerThread.java:29)at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)at java.lang.Thread.run(Unknown Source)

## TID:700(nid = 0X2BC)

  • 线程名称:pool-1-thread-1
  • CPU @ 23.55%
  • 任务:工作线程执行HashMap.put()操作
at java.util.HashMap.put(Unknown Source)at org.ph.javaee.training4.WorkerThread.run(WorkerThread.java:32)at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)at java.lang.Thread.run(Unknown Source)

## TID:6304(nid = 0X18A0)

  • 线程名称:pool-1-thread-3
  • CPU @ 12.02%
  • 任务:工作线程执行HashMap.put()操作
at java.util.HashMap.put(Unknown Source)at org.ph.javaee.training4.WorkerThread.run(WorkerThread.java:32)at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)at java.lang.Thread.run(Unknown Source)

## TID:5944(nid = 0X1738)

  • 线程名称:pool-1-thread-1
  • CPU @ 20.88%
  • 任务:主Java程序执行
"main" prio=6 tid=0x0000000001e2b000 nid=0x1738 runnable [0x00000000029df000]java.lang.Thread.State: RUNNABLEat org.ph.javaee.training4.HashMapInfiniteLoopSimulator.main(HashMapInfiniteLoopSimulator.java:75)

如您所见,上面的相关性和分析非常有启发性。 我们的主要Java程序处于挂起状态,因为我们的3个工作线程正在占用大量CPU,并且无法正常运行。 它们在执行HashMap get()和put()时可能看起来“卡住”,但实际上它们都涉及无限循环条件。 这正是我们想要复制的内容。

HashMap无限循环深入探究

现在,让我们进一步分析,以更好地了解这种循环条件。 为此,我们在JDK 1.7 HashMap Java类本身中添加了跟踪代码,以了解正在发生的情况。 为put()操作添加了类似的日志记录,还添加了一条跟踪,指示内部和自动重新哈希/调整大小已触发。
在get()和put()操作中添加的跟踪使我们能够确定for()循环是否正在处理循环依赖关系,这将解释无限循环条件。

#### HashMap.java get() operation
public V get(Object key) {if (key == null)return getForNullKey();int hash = hash(key.hashCode());/*** P-H add-on- iteration counter ***/int iterations = 1;for (Entry<K,V> e = table[indexFor(hash, table.length)];e != null;e = e.next) {/*** Circular dependency check ***/Entry<K,V> currentEntry = e;Entry<K,V> nextEntry = e.next;Entry<K,V> nextNextEntry = e.next != null?e.next.next:null;K currentKey = currentEntry.key;              K nextNextKey = nextNextEntry != null?(nextNextEntry.key != null?nextNextEntry.key:null):null;System.out.println("HashMap.get() #Iterations : "+iterations++);if (currentKey != null && nextNextKey != null ) {       if (currentKey == nextNextKey || currentKey.equals(nextNextKey))System.out.println(" ** Circular Dependency detected! ["+currentEntry+"]["+nextEntry+"]"+"]["+nextNextEntry+"]");}/***** END ***/Object k;if (e.hash == hash && ((k = e.key) == key || key.equals(k)))return e.value;}return null;}
HashMap.get() #Iterations : 1
HashMap.put() #Iterations : 1
HashMap.put() #Iterations : 1
HashMap.put() #Iterations : 1
HashMap.put() #Iterations : 1
HashMap.resize() in progress...
HashMap.put() #Iterations : 1
HashMap.put() #Iterations : 2
HashMap.resize() in progress...
HashMap.resize() in progress...
HashMap.put() #Iterations : 1
HashMap.put() #Iterations : 2
HashMap.put() #Iterations : 1
HashMap.get() #Iterations : 1
HashMap.get() #Iterations : 1
HashMap.put() #Iterations : 1
HashMap.get() #Iterations : 1
HashMap.get() #Iterations : 1
HashMap.put() #Iterations : 1
HashMap.get() #Iterations : 1
HashMap.put() #Iterations : 1** Circular Dependency detected! [362565=362565][333326=333326]][362565=362565]
HashMap.put() #Iterations : 2** Circular Dependency detected! [333326=333326][362565=362565]][333326=333326]
HashMap.put() #Iterations : 1
HashMap.put() #Iterations : 1
HashMap.get() #Iterations : 1
HashMap.put() #Iterations : 1
.............................
HashMap.put() #Iterations : 56823

再次,添加的日志记录非常有启发性。 我们可以看到,在几个内部HashMap.resize()之后,内部结构受到了影响,创建了循环依赖条件,并触发了这个无限循环条件(#iterations不断增加和增加……)而没有退出条件。
这也表明resize()/ rehash操作最容易遭受内部损坏,尤其是当使用默认的HashMap大小16时。这意味着HashMap的初始大小似乎是造成风险的重要因素。问题复制。
最后,有趣的是,我们能够通过将初始大小设置为1000000来成功运行非线程安全HashMap的测试用例,从而完全避免了任何调整大小。 在合并图结果下方找到:

HashMap是我们表现最好的,但是仅在防止内部调整大小时才使用。 同样,这绝对不是解决线程安全风险的方法,而只是一种方法,表明考虑到当时执行的HashMap的整个操作,调整大小操作的风险最大。
到目前为止,ConcurrentHashMap是我们的整体赢家,因为它针对该测试用例提供了快速的性能和线程安全性。

JBoss AS7 Map数据结构用法

现在,我们将通过研究现代Java EE容器实现(例如JBoss AS 7.1.2)中的不同Map实现来结束本文。 您可以从github master分支获取最新的源代码。
在报告下方找到:

  • JBoss AS7.1.2 Java文件总数(2012年8月28日快照):7302
  • 使用java.util.Hashtable的Java类总数:72
  • 使用java.util.HashMap的Java类总数:512
  • 使用同步的HashMap的Java类总数:18
  • 使用ConcurrentHashMap的Java类总数:46

哈希表引用主要在测试套件组件中以及命名和与JNDI相关的实现中找到。 这种低使用率在这里不足为奇。

从512个Java类中找到了对java.util.HashMap的引用。 考虑到自从最近几年以来这种实现方式的普及程度,这再次不足为奇。 但是,重要的是要提到,从局部变量(未在线程间共享),同步的HashMap或手动同步防护措施中找到了很好的比率,因此“技术上”使线程安全,并且不会暴露于上述无限循环条件(待处理/隐藏的错误)考虑到Java并发编程的复杂性,这仍然是一个现实……涉及Oracle Service Bus 11g的案例研究就是一个很好的例子)。

发现JMS,EJB3,RMI和集群等软件包中只有18个Java类,使用的同步HashMap使用率较低。

最后,在下面找到ConcurrentHashMap用法的细分,这是我们主要的兴趣所在。 正如您将在下面看到的那样,关键的JBoss组件层(例如Web容器,EJB3实现等)使用此Map实现。

## JBoss单点登录
用于管理涉及并发线程访问的内部SSO ID
合计:1

## JBoss Java EE和Web容器
这并不奇怪,因为许多内部Map数据结构用于管理http会话对象,
部署注册表,群集和复制,统计信息等,并发线程访问量大。 总数:11

## JBoss JNDI和安全层
由高度并发的结构(例如内部JNDI安全管理)使用。
合计:4

## JBoss域和受管服务器管理,推出计划…
合计:7

## JBoss EJB3
由数据结构使用,例如文件计时器持久性存储,应用程序异常,实体Bean缓存,序列化,钝化…
合计:8

## JBoss内核,线程池和协议管理
由高并发线程数映射数据结构使用,这些数据结构涉及处理和分派/处理传入请求(例如HTTP)。
合计:3

## JBoss连接器,例如JDBC / XA DataSources…
合计:2

## Weld(JSR-299的参考实现:JavaTM EE平台的上下文和依赖注入)用于ClassLoader和涉及并发线程访问的并发静态Map数据结构的上下文。
合计:3

## JBoss测试套件用于某些集成测试用例,例如内部数据存储,ClassLoader测试等。
合计:3

最后的话

我希望本文能帮助您重新研究这个经典问题,并理解与错误使用非线程安全HashMap实现有关的常见问题和风险之一。 我的主要建议是在并发线程上下文中使用HashMap时要小心。 除非您是Java并发专家,否则我建议您改用ConcurrentHashMap,它在性能和线程安全性之间提供了很好的平衡。

像往常一样,总是建议进行额外的尽职调查,例如执行负载和性能测试周期。 这将使您能够在将解决方案推广到客户生产环境之前检测线程安全和/或性能问题。

参考: Java 7:我们的JCG合作伙伴 Pierre-Hugues Charbonneau的HashMap与ConcurrentHashMap ,位于Java EE支持模式和Java教程博客。


翻译自: https://www.javacodegeeks.com/2012/08/java-7-hashmap-vs-concurrenthashmap.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/371442.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【2017-03-02】集合、结构体、枚举

集合和数组的差别&#xff1a; 数组&#xff1a;同一类型&#xff0c;固定长度集合&#xff1a;不同类型&#xff0c;不固定长度 一、普通集合&#xff08;弱类型&#xff09; 1、ArryList 使用集合首先要引用命名空间。 或者在ArryList上右键找“解析”。 2、集合的定义&#…

Mathematica图片局部变色

这篇博客来源于Stack-Exchange上的一个帖子&#xff0c;问题描述如下&#xff1a;如何将图中的红球变为蓝球&#xff1f; 这个问题下面有很多答案&#xff0c;我选了最好的一个答案&#xff0c;代码如下 img Import["C:/Users/1/Desktop/red.jpg"]; getReds[x_Image…

在WebLogic 12c上运行RichFaces

我最初以为我可以在几个月前写这篇文章。 但是我最终被不一样的事情所淹没。 其中之一是&#xff0c;它无法像我在4.0版本中那样简单地启动RichFaces展示柜。 有了所有的JMS magic和不同的提供者检查&#xff0c;这已经成为简单构建和部署它的挑战。 无论如何&#xff0c;我愿意…

Spring Boot系列教程一:Eclipse安装spring-tool-suite插件

一.前言 一直使用eclipse&#xff0c;个人习惯选用Eclipsespring-tool-suite进行开发&#xff0c;特别注意Eclipse要选用对应的spring-tool-suite进行安装&#xff0c;这点笔者浪费了好长时间&#xff0c;以下为对应的版本。eclipse-kepler.4.3.1–>springsource-tool-suite…

湖南工程学院计算机网络考试,湖南工程学院 计算机网络期末试卷试题

湖南工程学院 计算机网络期末试卷试题湖南工程学院 计算机网络 期末试题(计算机10级&#xff0c;90%的题目)1 从逻辑功能上看,计算机网络可分为哪两个子网?答&#xff1a;通信子网和资源子网 2 数据链路层的最基本功能答&#xff1a;数据链路层的最基本的功能是向该层用户提供…

C#设计模式(11)——外观模式(Facade Pattern)

一、引言 在软件开发过程中&#xff0c;客户端程序经常会与复杂系统的内部子系统进行耦合&#xff0c;从而导致客户端程序随着子系统的变化而变化&#xff0c;然而为了将复杂系统的内部子系统与客户端之间的依赖解耦&#xff0c;从而就有了外观模式&#xff0c;也称作 ”门面“…

OS X Mountain Lion上的多个Java版本

在Mountain Lion之前&#xff0c;Java被捆绑在OS X中。似乎在升级期间&#xff0c;我在计算机上安装的Java 6版本被删除了。 显然&#xff0c;在升级过程中卸载Java的原因是Java运行时存在的安全问题。通过这种方式&#xff0c;您不得不安装可解决此安全问题的最新版本。 所以我…

2020暨阳学院园林计算机考研考场,【图片】2020考研,老学长教你如何规划!【计算机考研吧】_百度贴吧...

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼二、关键一步——院校选择我把各位同学的院校选择阶段分为以上几个阶段&#xff0c;因为考研这一年中&#xff0c;很多人的目标院校并不是固定不变的&#xff0c;而是随着不同阶段而改变的。学长我在大三下学期这一时间段内也多次更…

JavaOne 2012:向上,向上和向外:使用Akka扩展软件

在最后的社区主题演讲后&#xff0c;我前往希尔顿金门大桥3/4/5观看了维克多巴生 &#xff08; Viktor Klang &#xff09;的&#xff08; Typesafe &#xff09;“上&#xff0c;下&#xff0c;外&#xff1a;Akka”演讲。 巴生&#xff08;Klang&#xff09;是Akka的技术主管…

Spring测试支持和上下文缓存

Spring为单元测试和集成测试提供了全面的支持-通过注释来加载Spring应用程序上下文&#xff0c;并与JUnit和TestNG等单元测试框架集成。 由于为每个测试加载大型应用程序上下文需要时间&#xff0c;因此Spring智能地为测试套件缓存应用程序上下文–通常&#xff0c;当我们通过a…

perl6正则 4: before / after 代码断言: ?{} / !{}

<?before> <? befor XXX> 某字符在 xxx 之前 <?after > <?after XXX> 某字符之后有XXX 对应的取反分别为: <!before > <!before XXX> XXX之前没有 <!after> <!after xxx> 某字符后面不是 xxx say "foobar" ~~…

如何写出安全的API接口(参数加密+超时处理+私钥验证+Https)- 续(附demo)

转载&#xff1a;http://www.cnblogs.com/codeon/p/6123863.html 上篇文章说到接口安全的设计思路&#xff0c;如果没有看到上篇博客&#xff0c;建议看完再来看这个。 通过园友们的讨论&#xff0c;以及我自己查了些资料&#xff0c;然后对接口安全做一个相对完善的总结&#…

PrimeFaces在GlassFish 3.1.2.2上推动大气

PrimeFaces 3.4在三天前问世。 除了通常令人敬畏的新组件和更新组件之外&#xff0c;它还包括新的PrimeFaces Push框架。 基于Atmosphere&#xff0c;这为您的应用程序提供了简单的推送机制。 这是在最新的GlassFish 3.1.2.2上配置和运行它的方法。 准备工作 像往常一样&#…

JBoss AS 7类加载说明

这是示例章节&#xff0c;摘自Francesco Marchioni编辑的JBoss AS 7 Configuration Deployment and Administration一书&#xff0c;该书正在运行一个名为mastertheboss.com的JBoss门户。 根据Java EE规范的要求&#xff0c;理想情况下&#xff0c;应用程序服务器应为其部署的应…

兵团职称计算机准考证查询,兵团初级会计准考证打印入口官网

兵团初级会计准考证打印入口官网为财政部会计资格评价中心网(http://kzp.mof.gov.cn)。准考证打印时间在4月16日前公布&#xff0c;考生需要在规定时间登录财政部会计资格评价中心网(http://kzp.mof.gov.cn)下载并打印准考证。未在上述时限内下载打印准考证的&#xff0c;视作放…

MySQL数据库的基本操作

-- 连接mysql 数据库(前提是配置好MySQL数据库的环境变量&#xff0c;加入path)mysql -uroot -p -- 设置文本的输入输出编码&#xff1a;cmd 使用的是gbk&#xff0c;不然显示乱码set names gbk; -- 创建数据库create database mydatabase charset utf8; -- 创建用户 user001cr…

使用NoSQL实施实体服务–第5部分:使用云提高自治性

在先前的文章中&#xff0c;我讨论了如何通过结合使用Java Web Services &#xff0c; Java EE和CouchDB NoSQL数据库为产品构建SOA“实体”服务。 在本系列的最后一篇文章中&#xff0c;我将利用我已经创建的一些技术资产&#xff0c;并使用一些流行的SOA模式实现一些新的用户…

乐高计算机发展史教程,【乐高产品发展史特别篇】乐高恐龙发展史

—— 写在前面 ——2018年6月22日&#xff0c;《侏罗纪世界2&#xff1a;失落王国》全球上映&#xff1b;4月16日&#xff0c;乐高同名系列套装全球发售。恐龙是一个伴随了乐高产品二十余年的主题&#xff0c;其实在一年以前就有这样一个计划完成乐高恐龙发展史的相关内容&…

mvc 连接数据库但单复值得问题

1. The model backing the ‘MusicStoreDBContext‘ context has changed since the database was created. Consider using Code First Migrations to update the database Movie这个表是用来记录Model的版本号的&#xff0c;你每次重新生成一次数据库它就会重新给ModelHash…

Mybatis处理表关联(懒加载)

1.关系型数据库&#xff1f; 数据库中的表对象之间是有关系的。 一对一&#xff0c;一对多&#xff0c;多对多。 ORM映射。数据库表映射到实体对象。 实体与实体之间是有关系的。 一对多的关系。 比如商品分类表与商品表之间的关系&#xff0c;就是一对多的关系。 入库主表与入…