I am not able to get why I am getting exception with both : class level lock as well as with object level lock in below code :
It seems object level locking should work here as we are changing and accessing hm(Object) value using different threads, but still we are getting exception(java.util.ConcurrentModificationException).
I tried with all three locking commented in the code.
I know using Hashtable or ConcurrentHashMap we can resolve this problem, but I want to know the concept which is missing using HashMap.
import java.util.HashMap;
class A{
StringBuilder str = new StringBuilder("ABCD");
StringBuilder exception = new StringBuilder("");
HashMap hm = new HashMap();
public void change() {
//synchronized(A.class) {
//synchronized (this){
synchronized (hm){
(this.str).append(Thread.currentThread().getName().toString());
System.out.println(Thread.currentThread().getName()+"::::::"+str);
hm.put(Thread.currentThread(), Thread.currentThread());
}
}
public void impact() {
//synchronized(A.class) {
//synchronized(this) {
synchronized(hm) {
System.out.println(Thread.currentThread()+"...Inside impact :::"+hm.get(Thread.currentThread()));
}
}
public void print() {
System.out.println("Inside print :::"+str);
System.out.println("Inside print :::exception--"+exception);
}
}
class B extends Thread{
A a;
B(A a){
this.a=a;
}
public void run() {
try {
System.out.println("Inside B run::"+a.hm);
a.change();
a.impact();
}
catch(Exception e){
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
System.out.println(sw.toString());
a.exception.append(sw.toString());
try {
sw.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
class C extends Thread{
A a;
C(A a){
this.a=a;
}
public void run() {
try {
System.out.println("Inside C run::"+a.hm);
a.change();
a.impact();
}
catch(Exception e){
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
System.out.println(sw.toString());
a.exception.append(sw.toString());
try {
sw.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
public class multiTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
A a = new A();
for(int i=0;i<=100;i++) {
B b = new B(a);
C c = new C(a);
b.start();
c.start();
}
try {
Thread.currentThread().sleep(5000);
}catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
a.print();
}
}
The problem is on this line:
System.out.println("Inside B run::"+a.hm);
There is sneaky implicit invocation of a.hm.toString() here, and that does sneaky iteration of the map's entries; but you aren't synchronizing on anything, so you don't have exclusive access to the hashmap.
Put it in a synchronized block:
synchronized (a.hm) {
System.out.println("Inside B run::"+a.hm);
}
(And make hm final; and don't use raw types).
Related
I visited interview some recently. Interviewer asked me to write guaranteed deadlock.
I have wrote following:
public class DeadLockThreadSleep {
private static class MyThread implements Runnable {
private Object o1;
private Object o2;
#Override
public void run() {
try {
test(o1, o2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public MyThread(Object o1, Object o2) {
this.o1 = o1;
this.o2 = o2;
}
public void test(Object o1, Object o2) throws InterruptedException {
synchronized (o1) {
System.out.println("1.acquired: " + o1);
Thread.sleep(1000);
synchronized (o2) {
System.out.println("2.acquired: " + o2);
}
}
}
}
public static void main(String[] args) {
Object o1 = new Object();
Object o2 = new Object();
new Thread(new MyThread(o1, o2)).start();
new Thread(new MyThread(o2, o1)).start();
}
}
Then he asked if I sure that it is guaranted. I rememebered that Thread.sleep nothing guaranteed.
Then I wrote this code:
public static void main(String[] args) {
final Thread mainThread = Thread.currentThread();
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
try {
mainThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this answer was accepted.
Also he asked to write analog via wait/notify. I thought a lot and I cannot imagine how to write this.
Is it possible?
This may be done by creating a cycle where one thread holds a resource and waits for another resource whereas the other thread does the same but in reverse order.
Thread tholds resourceOne and waits for resourceTwo , whereas t1holds resourceTwo and waits for resourceOne
Below is a sample code:
public class WaitNotifyLock {
boolean isONHold = false;
public synchronized void hold(){
while(isONHold){
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
isONHold = true;
System.out.println(Thread.currentThread().getId() + " : Holded");
}
public synchronized void unHold(){
while(!isONHold){
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getId() + " : Produced");
isONHold = false;
notify();
}
public static void main(String[] args) {
WaitNotifyLock resourceOne = new WaitNotifyLock();
WaitNotifyLock resourceTwo = new WaitNotifyLock();
Thread t = new Thread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
resourceOne.hold();
try {
Thread.sleep(2);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
resourceTwo.hold();
resourceOne.unHold();
resourceTwo.unHold();
}
});
Thread t1 = new Thread(new Runnable() {
#Override
public void run() {
resourceTwo.hold();
try {
Thread.sleep(2);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
resourceOne.hold();
resourceTwo.unHold();
resourceOne.unHold();
}
});
t.start();
t1.start();
}
}
A deadlock is a so-called liveness hazard (others are starvation, poor responsiveness, or livelocks), where two main types can be considered:
Lock-ordering deadlocks
Resource deadlocks
However, the Java documentation simplifies this as follows:
Deadlock describes a situation where two or more threads are blocked forever, waiting for each other.
Hence, IMHO you could simply enforce a deadlock with this:
public class DeadlockDemo {
public static void main(String[] args) {
Object a = new Object();
Object b = new Object();
new Thread(() -> waitLeftNotifyRight(a, b)).start();
waitLeftNotifyRight(b, a);
}
public static void waitLeftNotifyRight(Object left, Object right) {
synchronized (left) {
try {
System.out.println("Wait");
left.wait();
} catch (InterruptedException e) { /* NOP */ }
}
synchronized (right) {
System.out.println("Notify");
right.notify();
}
}
}
This demo never terminates because the created thread waits on a's monitor, whereas the main thread waits on b's monitor. As a result, the corresponding notify() methods aren't invoked (which would terminate the program).
I have a class user which writes json objects to a file
public class User {
public static void main(String args[]) throws IOException, org.json.simple.parser.ParseException{
writetofile();
Q q= new Q();
Writer write = new Writer("write",q);
// System.out.println(q.queue.poll());
Reader reader = new Reader("read",q);
}
public static void writetofile() throws IOException{
FileWriter file = new FileWriter("file1.txt");
for(int i=0;i<3;++i){
JSONObject obj = new JSONObject();
obj.put("Name", rand_s());
obj.put("Age", rand_i());
file.write(obj.toJSONString());
file.flush();
file.write("\r\n");
// System.out.println("Successfully Copied JSON Object to File...");
// System.out.println("\nJSON Object: " + obj);
}
}
public static String rand_s(){
final String AB = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
SecureRandom rnd = new SecureRandom();
StringBuilder sb = new StringBuilder( 12 );
for( int i = 0; i < 12; i++ )
sb.append( AB.charAt( rnd.nextInt(AB.length()) ) );
return sb.toString();
}
public static String rand_i(){
final String AB = "0123456789";
SecureRandom rnd = new SecureRandom();
StringBuilder sb = new StringBuilder( 2 );
for( int i = 0; i < 2; i++ )
sb.append( AB.charAt( rnd.nextInt(AB.length()) ) );
return sb.toString();
}
}
I have a class writer which writes the Json documents from file to a queue and a class reader which reads from queue and prints the objects and deletes them from queue
Below is writer class
package org.mmt;
import java.io.File;
import java.io.FileNotFoundException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Scanner;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
public class Writer implements Runnable {
Thread t;
Q q;
Writer(String name,Q q){
t= new Thread(this,name);
this.q = q;
t.start();
}
#Override
public void run(){
String FileName="file1.txt";
try {
ArrayList<JSONObject> jsons=ReadJSON(new File(FileName),"UTF-8");
for(JSONObject ob1 : jsons){
q.put(ob1);
notifyAll();
// System.out.println(q.queue.poll());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
} catch (org.json.simple.parser.ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static synchronized ArrayList<JSONObject> ReadJSON(File MyFile,String Encoding) throws FileNotFoundException, ParseException, org.json.simple.parser.ParseException {
Scanner scn=new Scanner(MyFile,Encoding);
ArrayList<JSONObject> json=new ArrayList<JSONObject>();
//Reading and Parsing Strings to Json
while(scn.hasNext()){
JSONObject obj= (JSONObject) new JSONParser().parse(scn.nextLine());
json.add(obj);
}
return json;
}
}
Below is reader class
package org.mmt;
import java.util.Queue;
import org.json.simple.JSONObject;
public class Reader implements Runnable {
Thread t;
Q q;
Reader(String name,Q q){
t=new Thread(this,name);
this.q=q;
t.start();
}
public void run() {
// TODO Auto-generated method stub
while(!q.empty()){
JSONObject obj = new JSONObject();
obj = q.get();
System.out.println(obj);
}
while(q.empty()){
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
and below is the Q class which contains the queue in which data has to be written
package org.mmt;
import java.util.LinkedList;
import java.util.Queue;
import org.json.simple.JSONObject;
public class Q {
public Queue<JSONObject> queue = new LinkedList<JSONObject>();
public synchronized JSONObject get(){
return queue.poll();
}
public synchronized void put(JSONObject obj){
try{
queue.add(obj);
}
catch (Exception e){
System.out.println(e);
}
}
public boolean empty(){
return queue.isEmpty();
}
}
I have started threads in reader and writer for simultaneous reading and writing and whenever queue is empty reader class waits() and whenever writer writes an element to queue I use notifyall() for reader to resume but I am getting Illegal monitor state exception. I have searched the internet and found that this occurs whenever thread tries to take lock of monitor which it does not own but I am not able to resolve the issue
Your Reader and Writer classes need to share the monitor object. In your example Reader is using itself as a monitor, and Writer is using itself as a monitor.
In your case you could use the Queue q itself as a monitor because that is your state that needs synchonization.
Also the callers need to own the monitor, they usually take ownership like this:
syncronized (q) { //do stuff on q}
In other words, wait/notify should be called only in a synchronized block, which is synchronized on the object.
More about wait/notify here
You're getting that exception because you aren't in a monitor.
public class User {
public static final Object lock = new Object();
...
synchronized(User.lock) {
while(q.empty()){
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class Writer implements Runnable {
...
for(JSONObject ob1 : jsons){
q.put(ob1);
synchronized(User.lock) {
notifyAll();
}
// System.out.println(q.queue.poll());
}
Please note I haven't worked with synchronized and concurrency like this in a while, so I'm not sure if this is entirely thread-safe or not.
Mostly because if you end up with
synchronized(lock) {
synchronized(Q) {
and
synchronized(Q) {
synchronized(lock) {
Then you'll get a deadlock at some point, and your app will freeze. That's why I'm personally wary of synchronized methods.
let's say i have 3 classes:
1. Storage which contains just one integer.
2. Counter which contains a thread inside who's responsible for counting (0,1,..,k) and stores each iteration of the loop index in Storage class.
3.Printer which contains a thread who's responsible for reading the value in class Storage and print it.
now i have to create a main class which creates these 3 objects runs the threads of Counter and Printer , and everynumber from(0,1,..,k) has to be printed just once and in the right order.
how do i synchronize the access to my Storage class so first i put a number inside Storage with Counter ,than print it with my Printer class ?
here's what i've wrote so far:
public class Storage {
private int num;
public Storage(){
}
public synchronized void setNum(int num){
this.num = num;
}
public synchronized int getNum(){
return num;
}
public class Counter implements Runnable {
Storage s;
public Counter(Storage t){
s = t;
}
#Override
public void run() {
int i = 0;
while(true){
s.setNum(i++);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class Printer implements Runnable {
Storage s;
public Printer(Storage s){
this.s= s;
}
#Override
public void run() {
while(true){
System.out.println(s.getNum());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class mainProg {
public static void main(String[] args){
Storage s = new Storage();
Counter c = new Counter(s);
Printer p = new Printer(s);
Thread c1 = new Thread(c);
Thread p2 = new Thread(p);
c1.start();
p2.start();
}
}
EDIT: i found out a solution, here it is:
public class Storage {
private int num;
private boolean available = false;
public Storage(){
}
public synchronized void setNum(int num){
while(available){
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
available = true;
notifyAll();
this.num = num;
}
public synchronized int getNum(){
while(!available){
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
available = false;
notifyAll();
return num;
}
}
This approach won't work, because it's not guaranteed that for every cycle of Counter a cycle of Printer will be executed in a parallel thread. You need to be able to store more than a one value in your Storage.
You can use BlockingQueue here and rewrite your Storage class like this:
public class Storage {
private BlockingQueue<Integer> numbers = new LinkedBlockingQueue<Integer>();
public void setNum(int num) {
try {
this.numbers.put(num);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public int getNum() {
try {
return numbers.take();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
Note that if BlockingQueue is empty and Printer wants to get a new value, it will wait while a new element occurrs in the queue.
I have tried to create a method to load files but it is not working the way it should. Why do I get this error? Is there a problem with my try-catch block?
NamnMetod.java:157: error: unreported exception InterruptedException; must be caught or declared to be thrown
EventQueue.invokeAndWait(new Runnable() {
This is my code:
public static void hämtaFrånText() {
EventQueue.invokeAndWait(new Runnable() {
#Override
public void run() {
try {
String aktuellMapp = System.getProperty("user.dir");
JFileChooser fc = new JFileChooser(aktuellMapp);
int resultat = fc.showOpenDialog(null);
if (resultat != JFileChooser.APPROVE_OPTION) {
JOptionPane.showMessageDialog(null, "Ingen fil valdes!");
System.exit(0);
}
String fil = fc.getSelectedFile().getAbsolutePath();
String[] namn = new String[3];
String output ="";
BufferedReader inFil = new BufferedReader(new FileReader(fil));
String rad = inFil.readLine();
int antal = 0;
while(rad != null) {
namn[antal] = rad;
rad = inFil.readLine();
antal++;
}
inFil.close();
}catch(FileNotFoundException e1) {
JOptionPane.showMessageDialog(null,"Filen hittades inte!");
}
catch(IOException e2) {
JOptionPane.showMessageDialog(null,"Det misslyckades");
}
}
});
}
It's got nothing to do with the try/catch block in the run() method. The problem is with the method that calls invokeAndWait... EventQueue.invokeAndWait() is declared to throw InterruptedException, which is a checked exception... so either you need another try/catch block (around the call) or your hämtaFrånText method should declare that it can throw InterruptedException too.
As per the JavaDoc (emphasis my own):
public static void invokeAndWait(Runnable runnable)
throws InterruptedException,
InvocationTargetException
The invokeAndWait can throw two types of exception. In your method, you do not have a try-catch segment to cater with these errors, thus your method must specify that it can potentially throw these exceptions itself because they are not handled internally.
You would need to either:
Add throws InterruptedException to your method signature OR
Have a try-catch block which envelopes EventQueue.invokeAndWait(new Runnable() {... so that any exceptions can be dealt with.
Defining an anonymous class:
new Runnable() {
#Override public void run() { ... }
};
is basically a shorthand for defining a local class:
class MyAnonymousRunnable implements Runnable {
#Override public void run() { ... }
}
and then creating an instance of that class:
new MyAnonymousRunnable();
As such, your code could be written as:
EventQueue.invokeAndWait(new MyAnonymousRunnable());
provided you have a suitable definition of MyAnonymousRunnable.
If you did this, you'd get exactly the same compilation error on that line. However, you know how to catch an exception in code without an anonymous class:
try {
EventQueue.invokeAndWait(new MyAnonymousRunnable());
} catch (InterruptedException e) {
Thread.currentThread().interrrupt();
// Do whatever to handle the exception.
}
So there is no real difference if you define the class anonymously:
try {
EventQueue.invokeAndWait(new Runnable() {
#Override public void run() { ... }
});
} catch (InterruptedException e) {
Thread.currentThread().interrrupt();
// Do whatever to handle the exception.
}
You could envelope your entire EventQueue.invokeAndWait(new Runnable(){...}); code inside another try-catch block like so:
public static void hämtaFrånText() {
try {
EventQueue.invokeAndWait(new Runnable() {
#Override
public void run() {
try {
String aktuellMapp = System.getProperty("user.dir");
JFileChooser fc = new JFileChooser(aktuellMapp);
int resultat = fc.showOpenDialog(null);
if (resultat != JFileChooser.APPROVE_OPTION) {
JOptionPane.showMessageDialog(null, "Ingen fil valdes!");
System.exit(0);
}
String fil = fc.getSelectedFile().getAbsolutePath();
String[] namn = new String[3];
String output = "";
BufferedReader inFil = new BufferedReader(new FileReader(fil));
String rad = inFil.readLine();
int antal = 0;
while(rad != null) {
namn[antal] = rad;
rad = inFil.readLine();
antal++;
}
inFil.close();
} catch(FileNotFoundException e1) {
JOptionPane.showMessageDialog(null, "Filen hittades inte!");
} catch(IOException e2) {
JOptionPane.showMessageDialog(null, "Det misslyckades");
}
}
});
} catch(InterruptedException e3) {
// your catch code here
}
}
I have a program that performs lots of calculations and reports them to a file frequently. I know that frequent write operations can slow a program down a lot, so to avoid it I'd like to have a second thread dedicated to the writing operations.
Right now I'm doing it with this class I wrote (the impatient can skip to the end of the question):
public class ParallelWriter implements Runnable {
private File file;
private BlockingQueue<Item> q;
private int indentation;
public ParallelWriter( File f ){
file = f;
q = new LinkedBlockingQueue<Item>();
indentation = 0;
}
public ParallelWriter append( CharSequence str ){
try {
CharSeqItem item = new CharSeqItem();
item.content = str;
item.type = ItemType.CHARSEQ;
q.put(item);
return this;
} catch (InterruptedException ex) {
throw new RuntimeException( ex );
}
}
public ParallelWriter newLine(){
try {
Item item = new Item();
item.type = ItemType.NEWLINE;
q.put(item);
return this;
} catch (InterruptedException ex) {
throw new RuntimeException( ex );
}
}
public void setIndent(int indentation) {
try{
IndentCommand item = new IndentCommand();
item.type = ItemType.INDENT;
item.indent = indentation;
q.put(item);
} catch (InterruptedException ex) {
throw new RuntimeException( ex );
}
}
public void end(){
try {
Item item = new Item();
item.type = ItemType.POISON;
q.put(item);
} catch (InterruptedException ex) {
throw new RuntimeException( ex );
}
}
public void run() {
BufferedWriter out = null;
Item item = null;
try{
out = new BufferedWriter( new FileWriter( file ) );
while( (item = q.take()).type != ItemType.POISON ){
switch( item.type ){
case NEWLINE:
out.newLine();
for( int i = 0; i < indentation; i++ )
out.append(" ");
break;
case INDENT:
indentation = ((IndentCommand)item).indent;
break;
case CHARSEQ:
out.append( ((CharSeqItem)item).content );
}
}
} catch (InterruptedException ex){
throw new RuntimeException( ex );
} catch (IOException ex) {
throw new RuntimeException( ex );
} finally {
if( out != null ) try {
out.close();
} catch (IOException ex) {
throw new RuntimeException( ex );
}
}
}
private enum ItemType {
CHARSEQ, NEWLINE, INDENT, POISON;
}
private static class Item {
ItemType type;
}
private static class CharSeqItem extends Item {
CharSequence content;
}
private static class IndentCommand extends Item {
int indent;
}
}
And then I use it by doing:
ParallelWriter w = new ParallelWriter( myFile );
new Thread(w).start();
/// Lots of
w.append(" things ").newLine();
w.setIndent(2);
w.newLine().append(" more things ");
/// and finally
w.end();
While this works perfectly well, I'm wondering:
Is there a better way to accomplish this?
Your basic approach looks fine. I would structure the code as follows:
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
public interface FileWriter {
FileWriter append(CharSequence seq);
FileWriter indent(int indent);
void close();
}
class AsyncFileWriter implements FileWriter, Runnable {
private final File file;
private final Writer out;
private final BlockingQueue<Item> queue = new LinkedBlockingQueue<Item>();
private volatile boolean started = false;
private volatile boolean stopped = false;
public AsyncFileWriter(File file) throws IOException {
this.file = file;
this.out = new BufferedWriter(new java.io.FileWriter(file));
}
public FileWriter append(CharSequence seq) {
if (!started) {
throw new IllegalStateException("open() call expected before append()");
}
try {
queue.put(new CharSeqItem(seq));
} catch (InterruptedException ignored) {
}
return this;
}
public FileWriter indent(int indent) {
if (!started) {
throw new IllegalStateException("open() call expected before append()");
}
try {
queue.put(new IndentItem(indent));
} catch (InterruptedException ignored) {
}
return this;
}
public void open() {
this.started = true;
new Thread(this).start();
}
public void run() {
while (!stopped) {
try {
Item item = queue.poll(100, TimeUnit.MICROSECONDS);
if (item != null) {
try {
item.write(out);
} catch (IOException logme) {
}
}
} catch (InterruptedException e) {
}
}
try {
out.close();
} catch (IOException ignore) {
}
}
public void close() {
this.stopped = true;
}
private static interface Item {
void write(Writer out) throws IOException;
}
private static class CharSeqItem implements Item {
private final CharSequence sequence;
public CharSeqItem(CharSequence sequence) {
this.sequence = sequence;
}
public void write(Writer out) throws IOException {
out.append(sequence);
}
}
private static class IndentItem implements Item {
private final int indent;
public IndentItem(int indent) {
this.indent = indent;
}
public void write(Writer out) throws IOException {
for (int i = 0; i < indent; i++) {
out.append(" ");
}
}
}
}
If you do not want to write in a separate thread (maybe in a test?), you can have an implementation of FileWriter which calls append on the Writer in the caller thread.
One good way to exchange data with a single consumer thread is to use an Exchanger.
You could use a StringBuilder or ByteBuffer as the buffer to exchange with the background thread. The latency incurred can be around 1 micro-second, doesn't involve creating any objects and which is lower using a BlockingQueue.
From the example which I think is worth repeating here.
class FillAndEmpty {
Exchanger<DataBuffer> exchanger = new Exchanger<DataBuffer>();
DataBuffer initialEmptyBuffer = ... a made-up type
DataBuffer initialFullBuffer = ...
class FillingLoop implements Runnable {
public void run() {
DataBuffer currentBuffer = initialEmptyBuffer;
try {
while (currentBuffer != null) {
addToBuffer(currentBuffer);
if (currentBuffer.isFull())
currentBuffer = exchanger.exchange(currentBuffer);
}
} catch (InterruptedException ex) { ... handle ... }
}
}
class EmptyingLoop implements Runnable {
public void run() {
DataBuffer currentBuffer = initialFullBuffer;
try {
while (currentBuffer != null) {
takeFromBuffer(currentBuffer);
if (currentBuffer.isEmpty())
currentBuffer = exchanger.exchange(currentBuffer);
}
} catch (InterruptedException ex) { ... handle ...}
}
}
void start() {
new Thread(new FillingLoop()).start();
new Thread(new EmptyingLoop()).start();
}
}
Using a LinkedBlockingQueue is a pretty good idea. Not sure I like some of the style of the code... but the principle seems sound.
I would maybe add a capacity to the LinkedBlockingQueue equal to a certain % of your total memory.. say 10,000 items.. this way if your writing is going too slow, your worker threads won't keep adding more work until the heap is blown.
I know that frequent write operations
can slow a program down a lot
Probably not as much as you think, provided you use buffering.