我正在修改Java服务器软件。整个应用程序是单线程的。我的一个更改需要花费很多时间,所以我决定异步执行以避免冻结主线程。
这是原始代码的示例(不是真实代码,只是一个示例):
public class Packet {
private final byte[] data = new byte[1024];
public void setData(int index, byte data) {
this.data[index] = data;
}
public byte getData(int index) {
return data[index];
}
public void sendPacket(ClientConnection clientConnection) {
clientConnection.sendPacket(data);
}
}
目前这是我的代码(查看评论):
public class Packet {
private final byte[] data = new byte[1024];
public void setData(int index, byte data) {
synchronized (this) {
this.data[index] = data;
}
}
public byte getData(int index) {
return data[index];
}
public void sendPacket(final ClientConnection clientConnection) {
//This state of data should be sent
new Thread(new Runnable() {
@Override
public void run() {
//The thread is now running
//The main-thread can move on
//The main-thread can also modify data now because we are not inside the synchronized block
//But it should not because the state of data when the method sendPacket was called should be sent
synchronized (Packet.this) {
thisTakesMuchTime(data);
clientConnection.sendPacket(data);
}
}
}).start();
}
}
我实际上正在寻找的是:
public class Packet {
private final byte[] data = new byte[1024];
public void setData(int index, byte data) {
//wait for unlock
this.data[index] = data;
}
public byte getData(int index) {
return data[index];
}
public void sendPacket(final ClientConnection clientConnection) {
//lock
new Thread(new Runnable() {
@Override
public void run() {
thisTakesMuchTime(data);
clientConnection.sendPacket(data);
//unlock
}
}).start();
}
}
问题:Java中这种锁的最佳实现是什么?我应该自己使用AtomicInteger
来做这件事。
编辑:查看我当前实施的答案。
答案 0 :(得分:2)
您可以复制数据并发送副本,以避免并发。
public class Packet {
private final byte[] data = new byte[1024];
public void setData(final int index, final byte data) {
this.data[index] = data;
}
public byte getData(final int index) {
return data[index];
}
public void sendPacket(final ClientConnection clientConnection) {
byte[] dataToSend = new byte[1024];
System.arraycopy(data, 0, dataToSend, 0, 1024);
new Thread(new Runnable() {
@Override public void run() {
clientConnection.sendPacket(dataToSend);
}
}).start();
}
}
使用CopyOnWriteArrayList
类似于下面的代码,这也避免了并发性,但效率不高(假设您setData
比sendPacket
更频繁地调用public class Packet {
private byte[] data = new byte[1024];
public void setData(final int index, final byte data) {
byte[] newData = new byte[1024];
System.arraycopy(data, 0, newData, 0, 1024);
newData[index] = data;
this.data = newData;
}
public byte getData(final int index) {
return data[index];
}
public void sendPacket(final ClientConnection clientConnection) {
new Thread(new Runnable() {
@Override public void run() {
clientConnection.sendPacket(data);
}
}).start();
}
}
:< / p>
{{1}}
答案 1 :(得分:1)
您可以使用的最简单的锁是Reentrant Lock,可重入意味着,如果您在拥有锁时尝试获取锁,则操作将成功。
在您的代码中,为了实现您想要的线程,您还必须使用wait()
和notify()
来阻止主线程,直到您的子线程获得锁定为止:
public class Packet {
private final ReentrantLock lock = new ReentrantLock();
private final byte[] data = new byte[1024];
public void setData(int index, byte data) {
lock.lock(); //wait for unlock
try {
this.data[index] = data;
} finally {
lock.unlock();
}
}
public byte getData(int index) {
return data[index];
}
public void sendPacket(final ClientConnection clientConnection) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
lock.lock(); //lock
try {
synchronized(this) {
this.notify();
}
thisTakesMuchTime(data);
clientConnection.sendPacket(data);
} finally {
lock.unlock(); //unlock
}
}
}).start();
synchronized(thread) {
try {
thread.wait();
} catch (InterruptedException e) {
//handle
}
}
}
}
还要考虑使用ExecutorService而不是创建原始Thread对象。
答案 2 :(得分:0)
Java中这种锁的最佳实现是什么?我应该自己使用AtomicInteger来做这件事。
我认为@ ericbn的答案可行。使用主线程但仍然在Packet
内部抓取数据副本很好。
然而你担心1k缓冲区?这里的真实费用不是在主线程中创建数据的副本,而是每次分支线程分支发送数据包。与对象创建相比,这非常昂贵。我使用线程池并向其提交数据包作业。
// you might want this to be bounded so you don't queue up too many packets
private final ExecutorService threadPool = Executors.newSingleThreadExecutor();
...
public void sendPacket(ClientConnection clientConnection) {
byte[] dataToWrite = new byte[data.length];
System.arraycopy(data, 0, dataToWrite, 0, dataToWrite.length);
threadPool.submit(new PacketToWrite(dataToWrite, clientConnection));
// you can clear or reset the `data` array now
}
private static class PacketToWrite implements Runnable {
private final byte[] dataToWrite;
private final ClientConnection clientConnection;
public PacketToWrite(byte[] dataToWrite, ClientConnection clientConnection) {
this.dataToWrite = dataToWrite;
this.clientConnection = clientConnection;
}
public void run() {
thisTakesMuchTime(data);
clientConnection.sendPacket(data);
}
}
您正在通过网络发送数据,因此与网络延迟相比,额外的对象带宽无效。
答案 3 :(得分:0)
我目前的实施:
分组:
public class Packet {
private final Lock lock = new Lock();
private final byte[] data = new byte[1024];
public void setData(int index, byte data) {
lock.waitUntilUnlock();
this.data[index] = data;
}
public byte getData(int index) {
return data[index];
}
public void sendPacket(final ClientConnection clientConnection) {
lock.lock();
new Thread(new Runnable() { // I use an ExecutorService
@Override
public void run() {
thisTakesMuchTime(data);
clientConnection.sendPacket(data);
lock.unlock();
}
}).start();
}
}
锁定:
public class Lock {
private final AtomicInteger lockCount = new AtomicInteger();
public void lock() { // Main thread
lockCount.incrementAndGet();
}
public synchronized void unlock() {
lockCount.decrementAndGet();
notifyAll();
}
public synchronized void waitUntilUnlock() { // Main thread
try {
while (lockCount.get() > 0) {
wait();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}