背景:
之前爲了簡單,一直使用Node.js寫遊戲服務器,經過不斷的探索研究,發現對於現代多核心CPU,顯然是Java這種多線程支持的語言更適合寫遊戲服務器,使得多核CPU利用率高很多。
測試如下:
package com.mmall.mytest;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/*
一個桌子
*/
class Room {
Random random = new Random();
// 輪到第幾個人出牌了
private int nTurnTo = 0;
// 房間號
int roomId = 0;
Room(int roomId) {
this.roomId = roomId;
}
// 執行出牌操作
public void exec(int index) {
// 使得房間一次性只能處理一條消息(實際上也不可能一個房間多個人併發的發消息)
synchronized (this) {
if (index == nTurnTo) {
// 模擬複雜任務的計算過程
try {
Thread.sleep(random.nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("房間號:" + roomId + " 第" + nTurnTo + "個人出牌完畢");
nTurnTo++;
nTurnTo %= 4;
}
}
}
}
/**
* 模擬某一個房間中一個人出牌動作,被封裝成一個任務
*/
class Task implements Runnable {
// 第幾個人出牌
int i;
// 所在的房間
Room room;
Task(int i, Room room) {
this.i = i;
this.room = room;
}
@Override
public void run() {
this.room.exec(i);
}
}
/**
* 模擬併發執行Netty中NIO線程收到的任務
*/
public class Main2 {
// 業務線程池
static ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 50);
// 模擬隨機一個人出牌,但是鬥地主必須是0-1-2-3-0...這樣順序出牌,從而驗證服務器端可以對其中一個房間鎖住,保證順序出牌
static Random random = new Random();
// 10個房間
static Room room = new Room(0);
static Room room1 = new Room(1);
static Room room2 = new Room(2);
static Room room3 = new Room(3);
static Room room4 = new Room(4);
static Room room5 = new Room(5);
static Room room6 = new Room(6);
static Room room7 = new Room(7);
static Room room8 = new Room(8);
static Room room9 = new Room(9);
public static void main(String[] args) {
// 模擬每個房間隨機30次出牌
for (int i = 0; i < 30; i++) {
executorService.submit(new Task(random.nextInt(4), room));
executorService.submit(new Task(random.nextInt(4), room1));
executorService.submit(new Task(random.nextInt(4), room2));
executorService.submit(new Task(random.nextInt(4), room3));
executorService.submit(new Task(random.nextInt(4), room4));
executorService.submit(new Task(random.nextInt(4), room5));
executorService.submit(new Task(random.nextInt(4), room6));
executorService.submit(new Task(random.nextInt(4), room7));
executorService.submit(new Task(random.nextInt(4), room8));
executorService.submit(new Task(random.nextInt(4), room9));
}
executorService.shutdown();
}
}
/*
房間號:2 第0個人出牌完畢
房間號:9 第0個人出牌完畢
房間號:8 第0個人出牌完畢
房間號:6 第0個人出牌完畢
房間號:4 第0個人出牌完畢
房間號:3 第0個人出牌完畢
房間號:7 第0個人出牌完畢
房間號:2 第1個人出牌完畢
房間號:5 第0個人出牌完畢
房間號:1 第0個人出牌完畢
房間號:0 第0個人出牌完畢
房間號:8 第1個人出牌完畢
房間號:1 第1個人出牌完畢
房間號:6 第1個人出牌完畢
房間號:9 第1個人出牌完畢
房間號:3 第1個人出牌完畢
房間號:0 第1個人出牌完畢
房間號:7 第1個人出牌完畢
房間號:2 第2個人出牌完畢
房間號:4 第1個人出牌完畢
房間號:5 第1個人出牌完畢
房間號:5 第2個人出牌完畢
房間號:4 第2個人出牌完畢
房間號:0 第2個人出牌完畢
房間號:3 第2個人出牌完畢
房間號:9 第2個人出牌完畢
房間號:1 第2個人出牌完畢
房間號:0 第3個人出牌完畢
房間號:1 第3個人出牌完畢
房間號:1 第0個人出牌完畢
房間號:8 第2個人出牌完畢
房間號:2 第3個人出牌完畢
房間號:1 第1個人出牌完畢
房間號:7 第2個人出牌完畢
房間號:7 第3個人出牌完畢
房間號:0 第0個人出牌完畢
房間號:6 第2個人出牌完畢
房間號:5 第3個人出牌完畢
房間號:9 第3個人出牌完畢
房間號:1 第2個人出牌完畢
房間號:4 第3個人出牌完畢
房間號:1 第3個人出牌完畢
房間號:2 第0個人出牌完畢
房間號:7 第0個人出牌完畢
房間號:0 第1個人出牌完畢
房間號:4 第0個人出牌完畢
房間號:4 第1個人出牌完畢
房間號:9 第0個人出牌完畢
房間號:5 第0個人出牌完畢
房間號:9 第1個人出牌完畢
房間號:6 第3個人出牌完畢
房間號:8 第3個人出牌完畢
房間號:4 第2個人出牌完畢
房間號:3 第3個人出牌完畢
房間號:0 第2個人出牌完畢
房間號:2 第1個人出牌完畢
房間號:9 第2個人出牌完畢
房間號:7 第1個人出牌完畢
房間號:2 第2個人出牌完畢
房間號:7 第2個人出牌完畢
房間號:4 第3個人出牌完畢
房間號:9 第3個人出牌完畢
房間號:7 第3個人出牌完畢
房間號:0 第3個人出牌完畢
房間號:2 第3個人出牌完畢
房間號:9 第0個人出牌完畢
房間號:0 第0個人出牌完畢
房間號:9 第1個人出牌完畢
房間號:9 第2個人出牌完畢
*/
結論:可以看出,棋牌中房間之間完全是獨立的,每個房間打牌的消息只要保證順序一致就行了。 房間之間,完全可以併發處理。 你找出任何一個房間,他裏面打牌順序是一致的0-1-2-3-0...。 核心之處就在於 同步方法的使用,使得一個操作想進行時,鎖住當前所在這個房間,使得邏輯能夠正確的順序處理。
而不是像Node.js這樣,100個房間落在一個線程上,這樣真的就是1個房間邏輯在執行時,其它99個房間傻傻的等着。