我有一台服务器为每个连接它的客户端创建一个线程。主服务器类称为" Server"而每个客户端都由一个类的线程管理" ClientManager"源自Server。服务器有一个静态的磁贴列表(用于麻将游戏),当每个客户端绘制时,ClientManager从arraylist中删除了许多磁贴。我有同步(可能不正确)的方法,但是当我运行程序时,就好像第一个播放器没有正确地从列表中删除tile。当我查看我的调试列表时,它说"播放器1绘制,剩下144个磁贴"当它应该说131.如果我在调试模式下运行程序,它的工作完全正常。如果我将Thread.sleep添加到处理所有这些的服务器中的main方法,它可以工作,但我不喜欢漫长的等待,并希望让arraylists正确同步并正确更新。第一次运行应该使列表减少13,然后下一个字符减少13,依此类推。在那之后他们每人都会画1,但是移除并没有在游戏中反映出来。
以下是来自客户端,服务器和客户经理的相关代码
主:
public class Main {
static GamePanel gameGUI = new GamePanel();
static Client client;
static Player me = new Player();
//!!? is used to mark a username command
@SuppressWarnings("unchecked")
public static void main(String[] args) throws IOException{
String host = "107.199.245.55";
//String host = "107.199.244.144";
//String host = "localhost";
boolean created = false;
ArrayList<?> Temp = new ArrayList<Object>();
ArrayList<Tile> TileStack = new ArrayList<Tile>();
Object object = null;
int tiles = 0;
String username = "!!?" + JOptionPane.showInputDialog("Username:");
if(username.substring(3, username.length()).equals("null")){return;}
while(username.substring(3, username.length()).isEmpty()){
username = "!!?" + JOptionPane.showInputDialog("Must have an actual name\nUsername:");
}
int port = 27016;
//int port = 2626;
//int port = 4444;
client = new Client(host, port, username);
client.send(username);
gameGUI.initialize();
gameGUI.lblPlayerNameYou.setText(username.substring(3, username.length()));
waitForPlayers();
while(true)
{
try {
object = client.receive();
if(object instanceof ArrayList<?>)
{
Temp = (ArrayList<?>) object;
if(Temp.get(0) instanceof String)
{
setUpNames(username, Temp);
}
else if(Temp.get(0) instanceof Tile)
{
if(!created){
TileStack = (ArrayList<Tile>) Temp;
created = true;
for (int i = 0; i < 13; i++){me.drawOneTile(TileStack);}
client.send(13);
}
else if(created){
me.drawOneTile(TileStack);
client.send(1);
}
gameGUI.displayHand(me.hand);
}
}
else if(object instanceof Integer){
tiles = (int) object;
gameGUI.tilesRemaining.setText("Remaining Tiles: " + tiles);
}
} catch (IOException e) {
e.printStackTrace();
JOptionPane.showMessageDialog(null, "You were disconnected. Exiting game.");
gameGUI.dispose();
break;
}
}
}
服务器:
public class Server {
public static ArrayList<ObjectOutputStream> ConnectionArray = new ArrayList<ObjectOutputStream>();
public ArrayList<String> CurrentUsers = new ArrayList<String>();
public static ArrayList<Socket> ConnectionArraySOCKET = new ArrayList<Socket>();
public ArrayList<Tile> TileStack = new ArrayList<Tile>();
public static ServerGUI serverGUI;
public int port;
public ServerSocket SERVERSOCK;
public static GameLoop game = new GameLoop();
public static Server server;
public Server(int port){
this.port = port;
try {
SERVERSOCK = new ServerSocket(port);
} catch (IOException e) {
System.out.println("Unable to start");
e.printStackTrace();
}
serverGUI = new ServerGUI();
serverGUI.initialize();
}
public ArrayList<String> getCurrentUsers(){return CurrentUsers;}
public ArrayList<Tile> getTileStack(){return TileStack;}
public void waitForClients(ServerSocket SERVERSOCK)
{
serverGUI.addText(serverGUI.getDebugPane(), "Waiting for clients...");
while(ConnectionArray.size() < 4 && CurrentUsers.size() < 4)
{
try{
if (!(ConnectionArray.size() == 0)) shareToAll(ConnectionArray.size());
Socket client = SERVERSOCK.accept();
ConnectionArray.add(new ObjectOutputStream(client.getOutputStream()));
ConnectionArraySOCKET.add(client);
Thread t = new Thread(new ClientManager(client));
t.start();
if (!(ConnectionArray.size() == 0)) shareToAll(ConnectionArray.size());
}
catch(IOException e){e.printStackTrace();}
}
}
public static void shareToAll(Object o){
for(ObjectOutputStream stream : ConnectionArray)
{
try{
Thread.sleep(100);
stream.writeObject(o);
stream.reset();
stream.flush();
}
catch(Exception e){
e.printStackTrace();
}
}
}
public static void distributeTileStack(Object o, int playerNum){
try {
ConnectionArray.get(playerNum).writeObject(o);
ConnectionArray.get(playerNum).reset();
ConnectionArray.get(playerNum).flush();
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
}
public static boolean checkConnection()
{
if(ConnectionArray.size() < 4) return false;
else return true;
}
public static void main(String[] args) throws InterruptedException
{
server = new Server(Integer.parseInt(JOptionPane.showInputDialog("Port:")));
ArrayList<Tile> temp = new ArrayList<Tile>();
while(!serverGUI.started){
System.out.println("LOOP");
}
server.waitForClients(server.SERVERSOCK);
Server.shareToAll(server.getCurrentUsers());
game.createStack();
server.TileStack = game.getStack();
for (int i = 0; i <= 3; i++){
serverGUI.addText(serverGUI.getDebugPane(), "Player " + (i + 1) + " drawing tiles.");
temp = server.getTileStack();
Server.distributeTileStack(server.TileStack, i);
serverGUI.addText(serverGUI.getDebugPane(), "Tilestack size: " + server.getTileStack().size());
Server.shareToAll(server.getTileStack().size());
}
while(checkConnection()){
// game logic here
}
JOptionPane.showMessageDialog(null, "Player disconnected. The server will now close.");
serverGUI.btnStopServer.doClick();
serverGUI.dispose();
}
}
ClientManager:
public class ClientManager extends Thread implements Runnable {
Socket SOCK;
String username;
public ClientManager(Socket SOCK)
{
this.SOCK = SOCK;
}
public void run(){
boolean working = true;
try{
ObjectInputStream inStream = new ObjectInputStream(SOCK.getInputStream());
while(working){
working = handle(inStream);
}
}
catch(SocketException e){
e.printStackTrace();
System.out.println("Cannot get inputstream");
}
catch(IOException e){
e.printStackTrace();
}
}
public boolean handle(ObjectInputStream inStream){
Object object = null;
String string;
try{
object = inStream.readObject();
if(object instanceof String)
{
string = (String)object;
if(string.startsWith("!!?")){
username = string.substring(3, string.length());
synchronized (Server.class){
Server.server.CurrentUsers.add(username);
}
Server.serverGUI.addText(Server.serverGUI.getDebugPane(), "User connected: " + username + SOCK.getRemoteSocketAddress());
Server.serverGUI.addText(Server.serverGUI.getUsersPane(), username);
Server.serverGUI.addText(Server.serverGUI.getDebugPane(), "Number of users: " + Server.server.CurrentUsers.size());
}
}
if (object instanceof Integer)
{
synchronized(Server.class){
for (int i = 0; i < (int) object; i++)
Server.server.TileStack.remove(0);
}
}
}
catch(ClassNotFoundException ce){ce.printStackTrace();}
catch(IOException e){
e.printStackTrace();
for(int i = Server.ConnectionArray.size() - 1; i >= 0; i--)
{
if(Server.ConnectionArraySOCKET.get(i).equals(SOCK))
{
Server.serverGUI.addText(Server.serverGUI.getDebugPane(), "User " + Server.server.CurrentUsers.get(i) + SOCK.getRemoteSocketAddress() + " disconnected");
Server.serverGUI.clearText(Server.serverGUI.getUsersPane());
Server.server.CurrentUsers.remove(i);
Server.serverGUI.addText(Server.serverGUI.getDebugPane(), "Number of users: " + Server.server.CurrentUsers.size());
Server.ConnectionArraySOCKET.remove(i);
Server.ConnectionArray.remove(i);
for (int i2 = 1; i2 <= Server.server.CurrentUsers.size(); i2++){
Server.serverGUI.addText(Server.serverGUI.getUsersPane(), Server.server.CurrentUsers.get(i2-1));
}
if (!(Server.ConnectionArray.size() == 0)) Server.shareToAll(Server.ConnectionArray.size());
}
}
return false;
}
return true;
}
}
答案 0 :(得分:0)
创建一个接口,其实现强制执行列表的单个实例(单例)。
示例界面
public List<Item> getInstance(){
if(myList == null){
myList = Collections.synchronizedList(TileStack);
}
return myList;
}
在其实施中强制执行单个实例
public class MyResource{
List<Item> myList;
private MyResource(){}
public List<Item> getInstance(){
if(myList == null){
myList = Collections.synchronizedList(new ArrayList<Item>());
}
return myList;
}
}
通过这种方式,您可以确保每个客户端使用相同的列表,这将消除每个客户端拥有自己的同步列表副本。
或者,如果你只是想上课
public void CustomNotification(String title_notify,String image_notify) {
// Using RemoteViews to bind custom layouts into Notification
RemoteViews remoteViews = new RemoteViews(getPackageName(),
R.layout.customnotification);
// Set Notification Title
String strtitle = title_notify;
// Set Notification Text
String strtext = "";
// Open NotificationView Class on Notification Click
Intent intent = new Intent(this, Notification_ListView.class);
// Send data to NotificationView Class
intent.putExtra("title", strtitle);
intent.putExtra("location", location);
intent.putExtra("text", strtext);
intent.putExtra("user_id", user_id);
intent.putExtra("country_id", country_id);
// Open NotificationView.java Activity
PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
bitmap = getBitmapFromURL(image_notify);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
// Set Icon
.setSmallIcon(getNotificationIcon())
// Set Ticker Message
.setTicker("")
// Dismiss Notification
.setAutoCancel(true)
// Set PendingIntent into Notification
.setContentIntent(pIntent)
// Set RemoteViews into Notification
.setContent(remoteViews);
// Locate and set the Image into customnotificationtext.xml ImageViews
remoteViews.setImageViewBitmap(R.id.imagenotileft,bitmap);
//remoteViews.setImageViewResource(R.id.imagenotiright,R.drawable.androidhappy);
// Locate and set the Text into customnotificationtext.xml TextViews
remoteViews.setTextViewText(R.id.title,strtitle);
remoteViews.setTextViewText(R.id.text,strtext);
// Create Notification Manager
NotificationManager notificationmanager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
// Build Notification with Notification Manager
notificationmanager.notify(0, builder.build());
}
答案 1 :(得分:0)
您可以按如下方式简化:
private ArrayDeque<Tile> tiles;
private ConcurrentHashMap<Integer, ArrayList<Tile>> preallocate;
private void preAllocate() {
for(int i = 0; i < 4; i++) {
ArrayList<Tile> temp = new ArrayList<>();
for(int j = 0; j < 13; j++) {
temp.add(tiles.pop());
}
preallocate.put(i, temp);
}
}
public ArrayList<Tile> get(int playerId) {
return preallocate.get(playerId);
}
您在一个线程中预先分配所有玩家的手,因此您不需要同步。然后使用线程安全的ConcurrentHashMap
来存储玩家的手,这样你就不需要同步了(地图会照顾它)。
另一种方法是将ArrayList<tile>
替换为ConcurrentLinkedQueue<Tile>
,您再次无需担心同步,因为数据结构正在为您处理。但请注意,此实现将“作弊”,因为Player1可能无法获得前13个区块,Player2可能无法获得第二个13区块,依此类推 - 区块分配将是不确定的。但是,我假设你正在洗牌,在这种情况下,它不会对玩家的顺序产生任何影响。如果这是不可接受的,那么我建议使用预先分配的ConcurrentHashMap
(您也可以使用ConcurrentLinkedQueue
完成此操作,但它会像使用全局ArrayList
)一样复杂