java游戏开发
游戏开发很重要的一点就是系统的并发性能,体现在单位时间内处理的请求数,还有同时连接的最大用户数。
基于java语言实现的Netty不仅并发性能极高,内存的gc效率高,使得它成为java游戏服务器的首选。
java7之前的java.util.concurrent包就有了Executors线程池,ConcurrentHashMap、ConcurrentSkipListMap等线程安全又高效的数据结构, 基于CAS原理的Atomic原子类型能够避免线程频繁挂起和恢复的开销。
Java7有ForkJoinPool合并分支框架,能够把大的任务分割成许多小的任务并行执行。
Java8引入了函数式编程的思想,遵循函数式思想的数据结构和类都是不可变得,函数是无副作用的,也就是线程安全的。
Java8的parallelStream,基于ForkJoinPool,能够透明地把集合拆分成多个流并行地处理,以前要编写大量的多线程代码,现在一行就能实现。
Java8的CompletableFuture能够满足更加复杂的异步非阻塞的处理需求:
-
将两个异步的计算合并为一个,这两个异步计算之间相互独立,同时第二个又依赖于第一个的结果。
-
等待Future集合中的所有任务都完成。
-
等待Future集合中的所有任务都完成,并返回他的结果。
-
通过编程方式去完成一个Future任务的执行。
-
应对Future的完成时间,即当Future的完成时间发生时会收到通知,并能使用Future计算的结果进行下一步的操作,不只是简单地阻塞等待操作的结果。
Java8的Map接口新增了一些新的方法:
foreach、computeIfAbsent、computeIfPresent。
Java8的ConcurrentHashMap的性能已经被改善,当大量的key返回相同hash值的value,在以前,这些value会被存放在List里面的,但是现在,value会存放在一棵排序树上。
虽然Java8更加容易实现并行处理,但是我们仍然有可能要做一些取舍。
1、尽量不要使用parallelStream去做与顺序相关的方法或计算。
如果计算的结果要依赖于顺序,并行处理的时间成本会很大。比如Stream<T> limit(long maxSize);
2、正确使用ConcurrentHashMap,get的次数远远多于put的场合,使用双重检查锁的HashMap或者ConcurrentHashMap性能差异不大。
package com.server.game.attribute.fairy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.countdownlatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.Atomicinteger;
import java.util.function.Function;
public class MapTest {
private static Map<String, String> hashMapA = new HashMap<>();
private static Map<String, String> hashMapB = Collections.synchronizedMap(new HashMap<>());
private static Map<String, String> concurrentMapA = new ConcurrentHashMap<>();
private static Map<String, String> concurrentMapB = new ConcurrentHashMap<>();
private static AtomicInteger factor = new AtomicInteger(0);
public static String getTestStr() {
stringbuilder str = new StringBuilder().APPend(factor.getAndIncrement())
.append(":").append(factor.getAndIncrement())
.append("&").append(factor.getAndIncrement())
.append("&").append(factor.getAndIncrement());
return str.toString();
}
//构建测试数据
private static List<String> prepareData() {
int a = 1;
int b = 100000;
CountDownLatch countDownLatch = new CountDownLatch(a * b);
List<String> data = Collections.synchronizedList(new ArrayList<>(800000));
for (int i = 0; i < a; i++) {
String d = getTestStr();
executorService.execute(() -> {
for (int j = 0; j < b; j++) {
data.add(d);
countDownLatch.countDown();
}
});
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printstacktrace();
}
collections.shuffle(data);
return data;
}
private final static ExecutorService executorService = Executors.newFixedThreadPool(runtime.getRuntime().availableProcessors());
public static String getFromHashMap(String key) {
String v = hashMapA.get(key);
if (v == null) {
synchronized (hashMapA) {
v = hashMapA.get(key);
if (v == null) {
v = new String(key);
hashMapA.put(key, v);
}
}
}
return v;
}
public static String getFromSynchronizedMap(String key) {
String v = hashMapB.get(key);
if (v == null) {
v = new String(key);
hashMapB.put(key, v);
}
return v;
}
public static String getFromConcurrentMap(String key) {
String v = concurrentMapA.get(key);
if (v == null) {
v = new String(key);
String previousVal = concurrentMapA.putIfAbsent(key, v);
//确保同一个key返回同一个value
if (previousVal != null) {
v = previousVal;
}
}
return v;
}
public static String getFromConcurrentMapByCompute(String key) {
return concurrentMapB.computeIfAbsent(key, k -> new String(k));
}
public static void singleThreadTest() {
List<String> datas = prepareData();
int n = 10000;
long startTime = system.currenttimemillis();
for (int i = 0; i < datas.size(); i++) {
String d = datas.get(i);
for (int j = 0; j < n; j++) {
getFromHashMap(d);
}
}
System.out.println("hashMap->" + (System.currentTimeMillis() - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < datas.size(); i++) {
String d = datas.get(i);
for (int j = 0; j < n; j++) {
getFromSynchronizedMap(d);
}
}
System.out.println("SynchronizedMap->" + (System.currentTimeMillis() - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < datas.size(); i++) {
String d = datas.get(i);
for (int j = 0; j < n; j++) {
getFromConcurrentMap(d);
}
}
System.out.println("concurrentMap putIfAbsent->" + (System.currentTimeMillis() - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < datas.size(); i++) {
String d = datas.get(i);
for (int j = 0; j < n; j++) {
getFromConcurrentMapByCompute(d);
}
}
System.out.println("concurrentMap computeIfAbsent->" + (System.currentTimeMillis() - startTime));
}
@SuppressWarnings("rawtypes")
public static void computeFunc(Function<String, String> f, List<String> datas) {
CompletableFuture[] futures = datas.stream().map(key -> CompletableFuture.supplyAsync(() -> {
for (int i = 0; i < 1000; i++) {
f.apply(key);
}
return "";
}, executorService))
.toArray(size -> new CompletableFuture[size]);
CompletableFuture.allOf(futures).join();
}
public static void manyThreadTest() {
List<String> datas = prepareData();
long startTime = System.currentTimeMillis();
computeFunc(MapTest::getFromHashMap, datas);
System.out.println("hashMap->" + (System.currentTimeMillis() - startTime));
startTime = System.currentTimeMillis();
computeFunc(MapTest::getFromSynchronizedMap, datas);
System.out.println("hashMap SynchronizedMap->" + (System.currentTimeMillis() - startTime));
startTime = System.currentTimeMillis();
computeFunc(MapTest::getFromConcurrentMap, datas);
System.out.println("concurrentMap putIfAbsent->" + (System.currentTimeMillis() - startTime));
startTime = System.currentTimeMillis();
computeFunc(MapTest::getFromConcurrentMapByCompute, datas);
System.out.println("concurrentMap computeIfAbsent->" + (System.currentTimeMillis() - startTime));
}
public static void main(String[] args) {
//manyThread();
//executorService.shutdown();
System.err.println("单线程性能对比:");
singleThreadTest();
System.err.println("");
System.err.println("多线程性能对比:");
manyThreadTest();
executorService.shutdown();
}
}
测试思路,使用不同的Map和不同的方法实现线程安全的getData(Object key)方法,prepareData方法预先准备数据,避免干扰测试目标,并且保证所有的Map都是独立相互不影响。
1.设置prepareData方法的局部变量a=1,b=100000时候,get的次数远远多于put:
2.设置prepareData方法的局部变量a=100000,b=1时候,put的次数比较多的时候:
Java8的computeIfAbsent()实现的getData( Object key)方法只需要一行代码,但是性能并不是很好,如果性能不是唯一指标的情况下,可以使用。
Java8的putIfAbsent实现的getData(Object key)并发读的性能十分接近双重检查锁同步的HashMap,但是并发写的性能更强。
相关阅读
记录在电脑中同时安装java7和java8的过程 1.下载并安装jdk1.7 和jdk1.8百度找资源或者直接官网下载: https://www.oracle.com/tech
分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow也欢迎大家转载本篇文章。分享知识,造福人民,
没玩过2048的可以自己下一个玩玩,挺有趣的益智游戏。 本文实现了其简单的游戏逻辑,能够进行记分,游戏结束判断功能。重新开始以及保
/d/file/news/20190606/6618313
前几天学习了ArrayList源码和迭代器模式在ArrayList源码中的使用,今天开始学习Vector源码。参考的JDK版本为1.8。 相信大家对Vecto