I developed an application using Java socket. I am exchanging messages with this application with the help of byte arrays. I have a message named M1, 1979 bytes long. My socket buffer length is 512 bytes. I read this message in 4 parts, each with 512 bytes, but the last one is of course 443 bytes. I will name these parts like A, B, C, and D. So ABCD is a valid message of mine respectively.
I have a loop with a thread which is like below.
BlockingQueue<Chunk> queue = new LinkedBlockingQueue<>();
InputStream in = socket.getInputStream()
byte[] buffer = new byte[512];
while(true) {
int readResult = in.read(buffer);
if(readResult != -1) {
byte[] arr = Arrays.copyOf(buffer, readResult);
Chunk c = new Chunk(arr);
queue.put(c);
}
}
I'm filling the queue with the code above. When the message sending starts, I see the queue fill up in ABCD form but sometimes I put the data in the queue as a BACD. But I know that this is impossible because the TCP connection guarantees the order.
I looked at the dumps with Wireshark. This message comes correctly with a single tcp package. So there is no problem on the sender side. I am 100% sure that the message has arrived correctly but the read method does not seem to read in the correct order and this situation doesn't always happen. I could not find a valid reason for what caused this.
When I tried the same code on two different computers I noticed that the problem was in only one. The jdk versions on these computers are different. I looked at the version differences between the two jdk versions. When the Jdk version is "JDK 8u202", I am getting the situation where it works incorrectly. When I tried it with jdk 8u271, there was no problem. Maybe it is related to that but I wasn't sure. Because I have no valid evidence.
I am open to all kinds of ideas and suggestions. It's really on its way to being the most interesting problem I've ever encountered.
Thank you for your help.
EDIT: I found similar question.
Blocking Queue Take out of Order
EDIT:
Ok, I have read all the answers given below. Thank you for providing different perspectives for me. I will try to supplement some missing information.
Actually I have 2 threads. Thread 1(SocketReader) is responsible for reading socket. It wraps the information it reads with a Chunk class and puts it on the queue in the other Thread 2. So queue is in Thread 2. Thread 2(MessageDecoder) is consuming the blocking queue. There are no threads other than these. Actually this is a simple example of a "producer consumer design patter".
And yes, other messages are sent, but other messages take up less than 512 bytes. Therefore, I can read in one go. I do not encounter any sort problem.
MessageDecoder.java
public class MessageDecoder implements Runnable{
private BlockingQueue<Chunk> queue = new LinkedBlockingQueue<>();
public MessageDecoder() {
}
public void run() {
while(true) {
Chunk c;
try {
c = queue.take();
System.out.println(c.toString());
} catch (InterruptedException e) {
e.printStackTrace();
}
decodeMessageChunk(c);
}
}
public void put(Chunk c) {
try {
queue.put(c);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
SocketReader.java
public class SocketReader implements Runnable{
private final MessageDecoder msgDec;
private final InputStream in;
byte[] buffer = new byte[512];
public SocketReader(InputStream in, MessageDecoder msgDec) {
this.in = in;
this.msgDec = msgDec;
}
public void run() {
while(true) {
int readResult = in.read(buffer);
if(readResult != -1) {
byte[] arr = Arrays.copyOf(buffer, readResult);
Chunk c = new Chunk(arr);
msgDec.put(c);
}
}
}
}
Even if it's a FIFO queue, the locking of the LinkedBloquingQueue is unfair, so you can't guarantee the ordering of elements. More info regarding this here
I'd suggest using an ArrayBlockingQueue instead. Like the LinkedBloquingQueue, the order is not guaranteed but offers a slightly different locking mechanism.
This class supports an optional fairness policy for ordering waiting
producer and consumer threads. By default, this ordering is not
guaranteed. However, a queue constructed with fairness set to true
grants threads access in FIFO order. Fairness generally decreases
throughput but reduces variability and avoids starvation.
In order to set fairness, you must initialize it using this constructor:
So, for example:
ArrayBlockingQueue<Chunk> fairQueue = new ArrayBlockingQueue<>(1000, true);
/*.....*/
Chunk c = new Chunk(arr);
fairQueue.add(c);
As the docs state, this should grant thread access in FIFO order, guaranteeing the retrievement of the elements to be consistent while avoiding possible locking robbery happening in LinkedBloquingQueue's lock mechanism.
I have two functions which must run in a critical section:
public synchronized void f1() { ... }
public synchronized void f2() { ... }
Assume that the behavior is as following:
f1 is almost never called. Actually, under normal conditions, this method is never called. If f1 is called anyway, it should return quickly.
f2 is called at a very high rate. It returns very quickly.
These methods never call each other and there is no reentrancy as well.
In other words, there is very low contention. So when f2 is called, we have some overhead to acquire the lock, which is granted immediately in 99,9% of the cases. I am wondering if there are approaches to avoid this overhead.
I came up with the following alternative:
private final AtomicInteger lock = new AtomicInteger(0);
public void f1() {
while (!lock.compareAndSet(0, 1)) {}
try {
...
} finally {
lock.set(0);
}
}
public void f2() {
while (!lock.compareAndSet(0, 2)) {}
try {
...
} finally {
lock.set(0);
}
}
Are there other approaches? Does the java.util.concurrent package offer something natively?
update
Although my intention is to have a generic question, some information regarding my situation:
f1: This method creates a new remote stream, if for some reason the current one becomes corrupt, for example due to a timeout. A remote stream could be considered as a socket connection which consumes a remote queue starting from a given location:
private Stream stream;
public synchronized void f1() {
final Stream stream = new Stream(...);
if (this.stream != null) {
stream.setPosition(this.stream.getPosition());
}
this.stream = stream;
return stream;
}
f2: This method advances the stream position. It is a plain setter:
public synchronized void f2(Long p) {
stream.setPosition(p);
}
Here, stream.setPosition(Long) is implemented as a plain setter as well:
public class Stream {
private volatile Long position = 0;
public void setPosition(Long position) {
this.position = position;
}
}
In Stream, the current position will be sent to the server periodically asynchronously. Note that Stream is not implemented by myself.
My idea was to introduce compare-and-swap as illustrated above, and mark stream as volatile.
Your example isn't doing what you want it to. You are actually executing your code when the lock is being used. Try something like this:
public void f1() {
while (!lock.compareAndSet(0, 1)) {
}
try {
...
} finally {
lock.set(0);
}
}
To answer your question, I don't believe that this will be any faster than using synchronized methods, and this method is harder to read and comprehend.
From the description and your example code, I've inferred the following:
Stream has its own internal position, and you're also tracking the most recent position externally. You use this as a sort of 'resume point': when you need to reinitialize the stream, you advance it to this point.
The last known position may be stale; I'm assuming this based on your assertion that the stream periodically does asynchronously notifies the server of its current position.
At the time f1 is called, the stream is known to be in a bad state.
The functions f1 and f2 access the same data, and may run concurrently. However, neither f1 nor f2 will ever run concurrently against itself. In other words, you almost have a single-threaded program, except for the rare cases when both f1 and f2 are executing.
[Side note: My solution doesn't actually care if f1 gets called concurrently with itself; it only cares that f2 is not called concurrently with itself]
If any of this is wrong, then the solution below is wrong. Heck, it might be wrong anyway, either because of some detail left out, or because I made a mistake. Writing low-lock code is hard, which is exactly why you should avoid it unless you've observed an actual performance issue.
static class Stream {
private long position = 0L;
void setPosition(long position) {
this.position = position;
}
}
final static class StreamInfo {
final Stream stream = new Stream();
volatile long resumePosition = -1;
final void setPosition(final long position) {
stream.setPosition(position);
resumePosition = position;
}
}
private final Object updateLock = new Object();
private final AtomicReference<StreamInfo> currentInfo = new AtomicReference<>(new StreamInfo());
void f1() {
synchronized (updateLock) {
final StreamInfo oldInfo = currentInfo.getAndSet(null);
final StreamInfo newInfo = new StreamInfo();
if (oldInfo != null && oldInfo.resumePosition > 0L) {
newInfo.setPosition(oldInfo.resumePosition);
}
// Only `f2` can modify `currentInfo`, so update it last.
currentInfo.set(newInfo);
// The `f2` thread might be waiting for us, so wake them up.
updateLock.notifyAll();
}
}
void f2(final long newPosition) {
while (true) {
final StreamInfo s = acquireStream();
s.setPosition(newPosition);
s.resumePosition = newPosition;
// Make sure the stream wasn't replaced while we worked.
// If it was, run again with the new stream.
if (acquireStream() == s) {
break;
}
}
}
private StreamInfo acquireStream() {
// Optimistic concurrency: hope we get a stream that's ready to go.
// If we fail, branch off into a slower code path that waits for it.
final StreamInfo s = currentInfo.get();
return s != null ? s : acquireStreamSlow();
}
private StreamInfo acquireStreamSlow() {
synchronized (updateLock) {
while (true) {
final StreamInfo s = currentInfo.get();
if (s != null) {
return s;
}
try {
updateLock.wait();
}
catch (final InterruptedException ignored) {
}
}
}
}
If the stream has faulted and is being replaced by f1, it is possible that an earlier call to f2 is still performing some operations on the (now defunct) stream. I'm assuming this is okay, and that it won't introduce undesirable side effects (beyond those already present in your lock-based version). I make this assumption because we've already established in the list above that your resume point may be stale, and we also established that f1 is only called once the stream is known to be in a bad state.
Based on my JMH benchmarks, this approach is around 3x faster than the CAS or synchronized versions (which are pretty close themselves).
Another approach is to use a timestamp lock which works like a modification count. This works well if you have a high read to write ratio.
Another approach is to have an immutable object which stores state via an AtomicReference. This works well if you have a very high read to write ratio.
I alway thought concurrently threads writing to one same file needs synchronization.
What happend when multi-threads writing same thing to the same file without synchronization? I imagined the output file must be incomplete or corrupted.
public class Test
{
public Runnable createLayoutRunnable() {
return new Runnable() {
public void run() {
try {
FileInputStream inputStream = new FileInputStream("mov.mp4");
FileOutputStream outputStream = new FileOutputStream("mov_co.mp4");
//IOUtils.copy(inputStream, outputStream);
//synchronized ("lock"){
int read = 0;
byte[] bytes = new byte[1024];
while ((read = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read);
}
//}
System.out.println(Thread.currentThread().getName() + " is done");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
}
public static void main(String[] args) {
Test test = new Test();
//Create Thread Pool for parallel layout
ExecutorService executor = Executors.newFixedThreadPool(9);
//Run Tasks and wait for termination in the current thread
Future<?> f1 = executor.submit(test.createLayoutRunnable());
Future<?> f2 = executor.submit(test.createLayoutRunnable());
Future<?> f3 = executor.submit(test.createLayoutRunnable());
Future<?> f4 = executor.submit(test.createLayoutRunnable());
Future<?> f5 = executor.submit(test.createLayoutRunnable());
Future<?> f6 = executor.submit(test.createLayoutRunnable());
Future<?> f7 = executor.submit(test.createLayoutRunnable());
Future<?> f8 = executor.submit(test.createLayoutRunnable());
Future<?> f9 = executor.submit(test.createLayoutRunnable());
try {
f1.get();
f2.get();
f3.get();
f4.get();
f5.get();
f6.get();
f7.get();
f8.get();
f9.get();
} catch (Exception ex) {
ex.printStackTrace();
}
executor.shutdown();
System.out.println("all done");
}
}
Surprise! The output mov is good to play!
How come? Please help!
Edit: Before all, I'm terribly sorry about the confusion. Yes the first-time code I posted was synchronised as opposed to what I said. I have commented it off now. This is because I was playing with the code and that's where I found whether it's synchronised or not doesn't matter and was wondering why.
In this particular case, you're writing identical content from an input file to identical locations in an output file. This is what's called an idempotent operation, and it doesn't matter whether you synchronize or not.
If each thread wrote its own source file (and you eliminated synchronization), you'd see that either (1) one thread would win, or (2, more likely) you'd get interleaved (corrupted) content.
In your example, even if you took out the synchronisation, each thread is writing the same content to the same file. Because each thread is using a separate OutputStream (and InputStream) the threads do not interfere with each other's file position. Thus the output is a copy of the input file.
It's analogous to this:
public static int a;
public static int b;
public static int c;
With the threaded code being:
a = 1;
b = 2;
c = 3;
Imagine you have two threads, A and B. The sequence of execution might run as follows, for example:
A sets a = 1;
A sets b = 2;
B sets a = 1;
A sets c = 3;
B sets b = 2;
B sets c = 3;
It doesn't matter how many threads run that sequence nor whether they are synchronised, once they are finished the contents of {a,b,c} will always be {1,2,3} (with some caveats that don't apply when writing to an external file). It's the same with your example copying a file - the contents of the output file are always the same; the exact sequence of execution in the threads doesn't matter.
Multithreaded access does not mean that you will get garbage. It means that result is inpredictable. Some systems may just synchronize something themselves and result may seem like it is accessed under mutex.
Secondly, your code is synchronized, as opposed to what you say. Do you see that section sync("lock")? At first you say that it is not synchronized. Then, we look into the code and see that it is synchronized. Then we think that "lock", visible to the first thread is different from what another sees. But it is the same object because in java "static string" == "static string". So, the thread makes the full copy under lock. Then another comes and makes the full copy. Sure, the playback will be uninterrupted.
The only thing in file manipulation that goes outside synchronization is file open/close. (close?) Try in Linux. This may make a big difference there.
I have written a class in java that implements a double buffer.
The class has two methods to write into the two buffers and a method to clear them.
Then I have three thread: one that writes on the first buffer, another that writes on the second buffer and a third one that clears the buffers.
Here, I paste (a piece of) the code that causes the problem (I know that it is not correct, but i've simplified it for debugging purposes):
public void addAlpha(int toAdd){
synchronized (alphaCount) {
while(alphaCount >= alpha.length){
try {
alphaCount.wait();
} catch (InterruptedException e) {
}
}
alpha[alphaCount] = toAdd;
alphaCount++;
}
}
And here the piece in which i call the notifyAll():
public void clear(){
synchronized (alphaCount) {
alphaCount = 0;
alphaCount.notifyAll();
}
}
As you can see, in the addAlpha method, I get the lock on alphaCount, test the condition and then wait on the alphaCount object.
In the clear method, i get the lock on alphaCount and I call notifyAll() on it.
At runtime, I get the IllegalStateMonitorException...
But I really don't know where the error is: I checked the documentation and more than one forum, without any luck...
Thanks for your time and your attention,
Rick.
As a rule, you should make field used as a lock final otherwise you can get bugs like this. IMHO You should make as many field final as you can. ;)
synchronized (alphaCount) { // alphaCount == 1 which is locked.
alphaCount = 0; // alphaCount == 0 which is not locked.
alphaCount.notifyAll(); // fails.
}
Additionally I wouldn't recommend using Integer or String or any wrapper type for a lock. As there are many confusing and surprising consequences. e.g.
Integer i1 = 127;
Integer i2 = 127; // same object due to the auto-boxing cache.
i1 == i2;
Integer i1 = 128;
Integer i2 = 128; // not the same object.
i1 != i2; // may or may not be the same object depending on the cache size.
Another problem is that you could get a deadlock with a completely unrelated library which also happens to be using integer as a lock.
The solution is to use a dedicated lock object.
private final Object alphaCountLock = new Object();
private int alphaCount = 0; // don't use an object when a primitive will do.
synchronized (alphaCountLock ) {
alphaCount = 0;
alphaCountLock .notifyAll();
}
There are multiple threads, say B, C and D, each writing small packets of data to a buffer at a high frequency. They own their buffer and nobody else ever writes to it. Writing must be as fast as possible, and I've determined that using synchronized makes it unacceptably slow.
The buffers are simply byte arrays, along with the index of the first free element:
byte[] buffer;
int index;
public void write(byte[] data) {
// some checking that the buffer won't overflow... not important now
System.arraycopy(data, 0, buffer, index, data.length);
index += data.length;
}
Every once in a while, thread A comes along to flush everybody's buffer to a file. It's okay if this part has some overhead, so using synchronized here is no problem.
Now the trouble is, that some other thread might be writing to a buffer, while thread A is flushing it. This means that two threads attempt to write to index around the same time. That would lead to data corruption, which I would like to prevent, but without using synchronized in the write() method.
I've got the feeling that, using the right order of operations and probably some volatile fields, this must be possible. Any bright ideas?
Have you tried a solution which uses synchronization, and found it doesn't perform well enough? You say you've determined that it's unacceptably slow - how slow was it, and do you already have a performance budget? Normally, obtaining an uncontested lock is extremely cheap, so I wouldn't expect it to be a problem.
There may well be some clever lock-free solution - but it's likely to be significantly more complicated than just synchronizing whenever you need to access shared data. I understand that lock-free coding is all the rage, and scales beautifully when you can do it - but if you've got one thread interfering with another's data, it's very hard to do it safely. Just to be clear, I like using lock-free code when I can use high-level abstractions created by experts - things like the Parallel Extensions in .NET 4. I just don't like working with low-level abstractions like volatile variables if I can help it.
Try locking, and benchmark it. Work out what performance is acceptable, and compare the performance of a simple solution with that goal.
Of course, one option is redesigning... does the flushing have to happen actively in a different thread? Could the individual writer threads not just hand off the buffer to the flushing thread (and start a different buffer) periodically? That would make things a lot simpler.
EDIT: Regarding your "flush signal" idea - I'd been thinking along similar lines. But you need to be careful about how you do it so that the signal can't get lost even if one thread takes a long time to process whatever it's doing. I suggest you make thread A publish a "flush counter"... and each thread keeps its own counter of when it last flushed.
EDIT: Just realized this is Java, not C# - updated :)
Use AtomicLong.incrementAndGet() to increment from thread A, and AtomicLong.get() to read from the other threads. Then in each thread, compare whether you're "up to date", and flush if necessary:
private long lastFlush; // Last counter for our flush
private Flusher flusher; // The single flusher used by all threads
public void write(...)
{
long latestFlush = flusher.getCount(); // Will use AtomicLong.get() internally
if (latestFlush > lastFlush)
{
flusher.Flush(data);
// Do whatever else you need
lastFlush = latestFlush; // Don't use flusher.getCount() here!
}
// Now do the normal write
}
Note that this assumes you only ever need to check for flushing in the Write method. Obviously that may not be the case, but hopefully you can adapt the idea.
You can use volatile alone to safely read/write to a buffer (if you have only one writer) however, only one thread can safely flush the data. To do this you can use a ring buffer.
I would add to #Jon's comment that this is significantly more complicated to test. e.g. I had one "solution" which worked for 1 billion messages consistently one day but kept breaking the next because the box was more loaded.
With synchronized your latency should be below 2 micro-seconds. With Lock, you could get this down to 1 micro-second. with busy waiting on a volatile you can get this down to 3-6 ns per byte (The time it takes to transfer data between threads becomes important)
Note: as the volume of data increases the relative cost of the lock becomes less important. e.g. if you are typically writing 200 bytes or more I wouldn't worry about the difference.
One approach I take is to use the Exchanger with two direct ByteBuffers and avoid writing any data in the critical path (i.e. only write the data after I have processed everything and it doesn't matter so much)
Invert control. Rather than having A poll the other threads, let them push.
I suppose LinkedBlockingQueue might be the most simple thing to go with.
Pseudocode:
LinkedBlockingQueue<byte[]> jobs;//here the buffers intended to be flushed are pushed into
LinkedBlockingQueue<byte[]> pool;//here the flushed buffers are pushed into for reuse
Writing thread:
while (someCondition) {
job = jobs.take();
actualOutput(job);
pool.offer(job);
}
Other threads:
void flush() {
jobs.offer(this.buffer);
this.index = 0;
this.buffer = pool.poll();
if (this.buffer == null)
this.buffer = createNewBuffer();
}
void write(byte[] data) {
// some checking that the buffer won't overflow... not important now
System.arraycopy(data, 0, buffer, index, data.length);
if ((index += data.length) > threshold)
this.flush();
}
LinkedBlockingQueue basically encapsulates the technical means to pass messages safely between threads.
Not only is it simpler this way round, but it clearly seperates concerns, because the threads that actually generate the output determine when they want to flush their buffers and they are the only ones to maintain their own state.
The buffers that are in both queues present a memory overhead, but that should be acceptable. The pool is unlikely to grow signifficantly bigger than the total number of threads and unless actual output presents a bottleneck, the jobs queue should be empty most of the time.
Volatile Variables And A Circular Buffer
Use a circular buffer, and make the flushing thread "chase" the writes around the buffer instead of resetting the index to zero after each flush. This allows writes to occur during a flush without any locking.
Use two volatile variables - writeIndex for where the writing thread is up to, and flushIndex for where the flushing thread is up to. These variables are each updated by only one thread, and can be read atomically by the other thread. Use these variables to keep the threads constrained to separate sections of the buffer. Do not allow the flushing thread to go past where the writing thread is up to (i.e. flush an unwritten part of the buffer). Do not allow the writing thread to go past where the flushing thread is up to (i.e. overwrite an unflushed part of the buffer).
Writing thread loop:
Read writeIndex (atomic)
Read flushIndex (atomic)
Check that this write will not overwrite unflushed data
Write to the buffer
Calculate the new value for writeIndex
Set writeIndex (atomic)
Flushing thread loop:
Read writeIndex (atomic)
Read flushIndex (atomic)
Flush the buffer from flushIndex to writeIndex - 1
Set flushIndex (atomic) to the value that was read for writeIndex
But, WARNING: for this to work, the buffer array elements might also need to be volatile, which you can't do in Java (yet). See http://jeremymanson.blogspot.com/2009/06/volatile-arrays-in-java.html
Nevertheless, here's my implementation (changes are welcome):
volatile int writeIndex = 0;
volatile int flushIndex = 0;
byte[] buffer = new byte[268435456];
public void write(byte[] data) throws Exception {
int localWriteIndex = writeIndex; // volatile read
int localFlushIndex = flushIndex; // volatile read
int freeBuffer = buffer.length - (localWriteIndex - localFlushIndex +
buffer.length) % buffer.length;
if (data.length > freeBuffer)
throw new Exception("Buffer overflow");
if (localWriteIndex + data.length <= buffer.length) {
System.arraycopy(data, 0, buffer, localWriteIndex, data.length);
writeIndex = localWriteIndex + data.length;
}
else
{
int firstPartLength = buffer.length - localWriteIndex;
int secondPartLength = data.length - firstPartLength;
System.arraycopy(data, 0, buffer, localWriteIndex, firstPartLength);
System.arraycopy(data, firstPartLength, buffer, 0, secondPartLength);
writeIndex = secondPartLength;
}
}
public byte[] flush() {
int localWriteIndex = writeIndex; // volatile read
int localFlushIndex = flushIndex; // volatile read
int usedBuffer = (localWriteIndex - localFlushIndex + buffer.length) %
buffer.length;
byte[] output = new byte[usedBuffer];
if (localFlushIndex + usedBuffer <= buffer.length) {
System.arraycopy(buffer, localFlushIndex, output, 0, usedBuffer);
flushIndex = localFlushIndex + usedBuffer;
}
else {
int firstPartLength = buffer.length - localFlushIndex;
int secondPartLength = usedBuffer - firstPartLength;
System.arraycopy(buffer, localFlushIndex, output, 0, firstPartLength);
System.arraycopy(buffer, 0, output, firstPartLength, secondPartLength);
flushIndex = secondPartLength;
}
return output;
}
Perhaps:
import java.util.concurrent.atomic;
byte[] buffer;
AtomicInteger index;
public void write(byte[] data) {
// some checking that the buffer won't overflow... not important now
System.arraycopy(data, 0, buffer, index, data.length);
index.addAndGet(data.length);
}
public int getIndex() {
return index.get().intValue();
}
otherwise the lock classes in the java.util.concurrent.lock package are more lightweight than the synchronized keyword...
so:
byte[] buffer;
int index;
ReentrantReadWriteLock lock;
public void write(byte[] data) {
lock.writeLock().lock();
// some checking that the buffer won't overflow... not important now
System.arraycopy(data, 0, buffer, index, data.length);
index += data.length;
lock.writeLock.unlock();
}
and in the flushing thread:
object.lock.readLock().lock();
// flush the buffer
object.index = 0;
object.lock.readLock().unlock();
UPDATE:
The pattern you describe for reading and writing to the buffer will not benefit from using a ReadWriteLock implementation, so just use a plain ReentrantLock:
final int SIZE = 99;
byte[] buffer = new byte[SIZE];
int index;
// Use default non-fair lock to maximise throughput (although some writer threads may wait longer)
ReentrantLock lock = new ReentrantLock();
// called by many threads
public void write(byte[] data) {
lock.lock();
// some checking that the buffer won't overflow... not important now
System.arraycopy(data, 0, buffer, index, data.length);
index += data.length;
lock.unlock();
}
// Only called by 1 thread - or implemented in only 1 thread:
public byte[] flush() {
byte[] rval = new byte[index];
lock.lock();
System.arraycopy(buffer, 0, rval, 0, index);
index = 0;
lock.unlock();
return rval;
}
As you describe usage as many write threads with a single reader/flusher thread, a ReadWriteLock is not neccessary, Infact I beleive it is more heavyweight than a simple ReentrantLock (?). ReadWriteLocks are useful for many reader threads, with few write threads - the opposite of the situation you describe.
You can try implementing semaphores.
I like the lock-free stuff, it's addictive :). And rest ensured: they remove a lot locking shortcomings, coming w/ some steep learning curve. Still they're and error-prone.
Read few articles, perhaps a book and try it home 1st.
How to handle your case? You can't atomically copy data (and update size), but you can atomically update a reference to that data.
simple way to do it; Note: you can ALWAYS read from the buffer w/o holding a lock which is the entire point.
final AtomicReference<byte[]> buffer=new AtomicReference<byte[]>(new byte[0]);
void write(byte[] b){
for(;;){
final byte[] cur = buffer.get();
final byte[] copy = Arrays.copyOf(cur, cur.length+b.length);
System.arraycopy(b, 0, cur, cur.length, b.length);
if (buffer.compareAndSet(cur, copy)){
break;
}
//there was a concurrent write
//need to handle it, either loop to add at the end but then you can get out of order
//just as sync
}
}
You actually you can still use a larger byte[] and append to it but I leave the exercise for yourself.
Continued
I had to write the code in a pinch. A short description follows:
The code is lock-free but not-obstruction free due to use of the CLQ. As you see the code always continues regardless of the conditions taken and practically doesn't loop (busy wait) anywhere besides the CLQ, itself.
Many lock-free algorithms rely on the help of all the threads to properly finish the task(s).
There might be some mistake but I hope the main idea is sort of clear:
The algorithm allows many writers, many readers
If the main state cannot be changed so there is a single writer only, append the byte[] into a queue.
Any writer (that succeeded on the CAS) must attempt to flush the queue prior to writing its own data.
A reader must check for pending writes and flush them before using the main buffer
If enlarging (current byte[] not enough) the buffer and the size must be thrown away and new generation of Buffer+Size is to be used. Otherwise only size is increased. The operation again requires to hold the lock (i.e. the CAS succeeded)
Please, any feedback is welcome.
Cheers and hopefully people can warm up to the lock-free structures algorithms.
package bestsss.util;
import java.util.Arrays;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
//the code uses ConcurrentLinkedQueue to simplify the implementation
//the class is well - know and the main point is to demonstrate the lock-free stuff
public class TheBuffer{
//buffer generation, if the room is exhaused need to update w/ a new refence
private static class BufGen{
final byte[] data;
volatile int size;
BufGen(int capacity, int size, byte[] src){
this.data = Arrays.copyOf(src, capacity);
this.size = size;
}
BufGen append(byte[] b){
int s = this.size;
int newSize = b.length+s;
BufGen target;
if (newSize>data.length){
int cap = Integer.highestOneBit(newSize)<<1;
if (cap<0){
cap = Integer.MAX_VALUE;
}
target = new BufGen(cap, this.size, this.data);
}
else if(newSize<0){//overflow
throw new IllegalStateException("Buffer overflow - over int size");
} else{
target = this;//if there is enough room(-service), reuse the buffer
}
System.arraycopy(b, 0, target.data, s, b.length);
target.size = newSize;//'commit' the changes and update the size the copy part, so both are visible at the same time
//that's the volatile write I was talking about
return target;
}
}
private volatile BufGen buffer = new BufGen(16,0,new byte[0]);
//read consist of 3 volatile reads most of the time, can be 2 if BufGen is recreated each time
public byte[] read(int[] targetSize){//ala AtomicStampedReference
if (!pendingWrites.isEmpty()){//optimistic check, do not grab the look and just do a volatile-read
//that will serve 99%++ of the cases
doWrite(null, READ);//yet something in the queue, help the writers
}
BufGen buffer = this.buffer;
targetSize[0]=buffer.size;
return buffer.data;
}
public void write(byte[] b){
doWrite(b, WRITE);
}
private static final int FREE = 0;
private static final int WRITE = 1;
private static final int READ= 2;
private final AtomicInteger state = new AtomicInteger(FREE);
private final ConcurrentLinkedQueue<byte[]> pendingWrites=new ConcurrentLinkedQueue<byte[]>();
private void doWrite(byte[] b, int operation) {
if (state.compareAndSet(FREE, operation)){//won the CAS hurray!
//now the state is held "exclusive"
try{
//1st be nice and poll the queue, that gives fast track on the loser
//we too nice
BufGen buffer = this.buffer;
for(byte[] pending; null!=(pending=pendingWrites.poll());){
buffer = buffer.append(pending);//do not update the global buffer yet
}
if (b!=null){
buffer = buffer.append(b);
}
this.buffer = buffer;//volatile write and make sure any data is updated
}finally{
state.set(FREE);
}
}
else{//we lost the CAS, well someone must take care of the pending operation
if (b==null)
return;
pendingWrites.add(b);
}
}
public static void main(String[] args) {
//usage only, not a test for conucrrency correctness
TheBuffer buf = new TheBuffer();
buf.write("X0X\n".getBytes());
buf.write("XXXXXXXXXXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXXXXXXXXXXXXXXXXXXX\n".getBytes());
buf.write("Hello world\n".getBytes());
int[] size={0};
byte[] bytes = buf.read(size);
System.out.println(new String(bytes, 0, size[0]));
}
}
Simplistic case
Another far simpler solution that allows many writers but single reader. It postpones the writes into a CLQ and the reader just reconstructs 'em. The construction code is omiitted this time.
package bestsss.util;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentLinkedQueue;
public class TheSimpleBuffer {
private final ConcurrentLinkedQueue<byte[]> writes =new ConcurrentLinkedQueue<byte[]>();
public void write(byte[] b){
writes.add(b);
}
private byte[] buffer;
public byte[] read(int[] targetSize){
ArrayList<byte[]> copy = new ArrayList<byte[]>(12);
int len = 0;
for (byte[] b; null!=(b=writes.poll());){
copy.add(b);
len+=b.length;
if (len<0){//cant return this big, overflow
len-=b.length;//fix back;
break;
}
}
//copy, to the buffer, create new etc....
//...
///
targetSize[0]=len;
return buffer;
}
}