我目前在以下课程中工作,一个带有while循环的绘图程序,从客户端向服务器发送包:
FragmentPagerAdapter
因为我的while循环在每个循环中持续睡眠(100)。这可以按预期工作,但是为了有效,我希望每次在ArrayList位置更改某些内容时都会运行循环,因此它不会发送不相关的包。
我想要实现的目标:
public class TCPClient extends JPanel {
public static ArrayList<Point> location = new ArrayList<>();
private JTextArea consoleOutput = new JTextArea(1,20);
public void addComponentToPane(Container pane) {
consoleOutput.setEditable(false);
}
public TCPClient() {
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
location.add(e.getPoint());
}
});
addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseDragged(MouseEvent e) {
location.add(e.getPoint());
repaint();
}
});
setPreferredSize(new Dimension(800, 500));
setBackground(Color.WHITE);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if(location.isEmpty()){
return;
}
Point p = location.get(0);
for (int i = 1; i < location.size(); i++) {
Point q = location.get(i);
g.drawLine(p.x, p.y, q.x, q.y);
p = q;
}
}
public static void main(String argv[]) throws Exception {
InetAddress SERVERIP = InetAddress.getLocalHost();
JFrame frame = new JFrame("Drawing with friends");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TCPClient(), BorderLayout.CENTER);
JTextArea IPadress = new JTextArea(1,20);
IPadress.setEditable(false);
IPadress.append("DEVICE IP: " + SERVERIP.getHostAddress());
frame.add(IPadress, BorderLayout.SOUTH);
frame.setSize(new Dimension(800,600));
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setVisible(true);
while(true) {
try {
//BufferedReader inFromUser = new BufferedReader(new InputStreamReader(System.in));
Socket clientSocket = new Socket("localhost", 9000);
ObjectOutputStream outToServer = new ObjectOutputStream(clientSocket.getOutputStream());
//ObjectInputStream inFromServer = new ObjectInputStream(clientSocket.getInputStream());
outToServer.writeObject(location);
outToServer.flush();
clientSocket.close();
Thread.sleep(100);
} catch (SocketException e) {
System.err.println(e.toString());
}
}
}
}
答案 0 :(得分:1)
您需要将所有访问权限(read
和write
)包装到您的变量位置,并将synchronized
块包含notify()
,以防您更新它,然后在您的循环中,您将sleep
替换为下一个wait
:
修改位置
synchronized (location) {
location.add(e.getPoint());
location.notify();
}
读取位置访问权限:
synchronized (location) {
if(location.isEmpty()){
return;
}
Point p = location.get(0);
for (int i = 1; i < location.size(); i++) {
Point q = location.get(i);
g.drawLine(p.x, p.y, q.x, q.y);
p = q;
}
}
同步块内的最终循环:
synchronized (location) {
while(true) {
//BufferedReader inFromUser = new BufferedReader(new InputStreamReader(System.in));
Socket clientSocket = new Socket("localhost", 9000);
ObjectOutputStream outToServer = new ObjectOutputStream(clientSocket.getOutputStream());
//ObjectInputStream inFromServer = new ObjectInputStream(clientSocket.getInputStream());
outToServer.writeObject(location);
outToServer.flush();
clientSocket.close();
location.wait();
}
}
答案 1 :(得分:1)
基本上,你应该使用某种BlockingQueue
,这将允许你的&#34;套接字&#34;线程到&#34;阻止&#34;在等待新的Point
到达时。
您应该单独发送每个List
,而不是发送整个Point
,这样可以随着点数的增加而节省时间。
由于队列的性质,你可以继续为它添加更多的点和&#34; socket&#34;线程可以按照自己的步调处理它们,这将允许队列在用户绘制时增加大小,但允许&#34; socket&#34;线程在用户停止时赶上,没有你做任何特别的事情。
以下示例使用Thread.sleep
在队列中处理每个点之间产生人为延迟以用于演示目的,显然,您不应该使用它(Thread.sleep
),但它证明了上述几点,线程将在您停止绘制后继续将队列中的点转储到标准输出
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
private BlockingQueue<Point> queue;
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
queue = new LinkedBlockingQueue<>();
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TCPClient(queue));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Thread t = new Thread(new Consumer(queue));
t.setDaemon(true);
t.start();
}
});
}
public class TCPClient extends JPanel {
private JTextArea consoleOutput = new JTextArea(1, 20);
private Queue<Point> queue;
private List<Point> cache;
public TCPClient(Queue<Point> queue) {
this.queue = queue;
cache = new ArrayList<>(25);
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
queue.add(e.getPoint());
cache.add(e.getPoint());
}
});
addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseDragged(MouseEvent e) {
queue.add(e.getPoint());
cache.add(e.getPoint());
repaint();
}
});
setPreferredSize(new Dimension(800, 500));
setBackground(Color.WHITE);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (cache.isEmpty()) {
return;
}
Point p = cache.get(0);
for (int i = 1; i < cache.size(); i++) {
Point q = cache.get(i);
g.drawLine(p.x, p.y, q.x, q.y);
p = q;
}
}
}
public class Consumer implements Runnable {
private BlockingQueue<Point> queue;
public Consumer(BlockingQueue<Point> queue) {
this.queue = queue;
}
@Override
public void run() {
while (true) {
try {
Point p = queue.poll(Long.MAX_VALUE, TimeUnit.DAYS);
if (p != null) {
System.out.println("-> Got " + p);
Thread.sleep(125);
}
} catch (InterruptedException ex) {
}
}
}
}
}
答案 2 :(得分:0)
不要让其他对象直接修改您的ArrayList
,而是将其设为私有,并创建一个getter,一个setter,以及(因为您正在使用集合)加法器和移除方法。
在这些内容中,您可以通知程序的其他部分,ArrayList
已更改,您需要发送数据包。
答案 3 :(得分:0)
您可以创建自己的CallbackArrayList
,其范围为ArrayList
。
添加抽象回调方法,例如onAdded
和onRemoved
。
覆盖您要监视的ArrayList
的那些方法,并在里面调用您的回调方法,具体取决于结果。
abstract class CallbackArrayList<T> extends ArrayList<T> {
public abstract void onAddSuccess(T object);
public abstract void onAddFailure(T object);
@Override
public boolean add(T object) {
boolean success = super.add(object);
if(success) {
onAddSuccess(object);
}
else {
onAddFailure(object);
}
return success;
}
}
然后在分配列表时,您可以
location = new CallbackArrayList<>() {
@Override
public void onAddSuccess(Point object) {
// handle send information to server
}
@Override
public void onAddFailure(Point object) {
// handle failure
}
};
每当你调用location.add(e.getPoint())
时,都会调用其中一个回调方法。