file thread synchronization - java

How to perform read and write operation by using thread synchronization.
Condition: If one file exists where writers may write information to, only one writer may write at a time. Confusion may arise if a reader is trying read at the same as a writer is writing. Since readers only look at the data, but do not modify the data, we can allow more than one reader to read at the same time.
//reader thread
class Read extends Thread {
static File Reader fr1 = null;
static Buffered Reader br1 = null;
static synchronized void reader() throws IO Exception {
String path ="C:/Users/teja/Documents/file1.txt";
fr1 = new File Reader(path);
br1 = new Buffered Reader(fr);
int i;
while ((i = br. read()) != -1)
System .out. print((char) i);
System .out. print ln();
}
public void run() {
try {
reader();
} catch (IO Exception e) {
e. print Stack Trace();
}
}
}
//writer code
class Writer extends Thread {
static Buffered Writer bw1 = null;
static File Writer fw1 = null;
static synchronized void writer() throws IO Exception {
Scanner scanner = new Scanner(System.in);
System .out .print ln("enter data to be added:");
String data = scanner. nextLine();
String path = "C:/Users/vt/Documents/file1.txt";
fw1 = new File Writer(path, true);
bw1 = new Buffered Writer(fw1);
bw1.newLine();
bw1.write(data);
bw1.flush();
scanner. close();
System. out . println("data added");
}
public void run() {
try {
writer();
} catch (IO Exception e) {
e. print Stack Trace();
}
}
}
//main method
public class File Read Write {
public static void main(String[] args) {
Read rd1 =new Read();
Read rd2=new Read();
Writer wt1=new Writer();
rd1.run();
rd2.run();
wt1.run();
rd1. run();
}
}
I am new to files and threading in java. I know this is not correct approach. Guide me.
If one file exists where writers may write information to, only one writer may write at a time. Confusion may arise if a reader is trying read at the same as a writer is writing. Since readers only look at the data, but do not modify the data, we can allow more than one reader to read at the same time.

There are two approaches to this.
(1) Either lock the resource and have the readers wait until the writer has completed the writing operation (or likewise, have a writer waits until all readers are done). This approach guarantees consistency, but can be slow if a lot of writers/readers are working on the resource at the same time (see Lock in java.util.concurrent.locks package).
(2) Keep an in-memory-version of the contents of the file that is served to readers only. When a change is made, this in-memory version is updated. Here, you'll have more speed, but you lose consistency and you'll need more memory.

The condition you want to avoid is generally referred as race condition and what you want to avoid it is a synchronization method between threads. There are more choices available but the most suitable for your case are mutex and read-write lock.
A mutex basically just lock the resource before any operation on the shared resource is performed, independently from the type of operation and free it after the operation is terminated. So a read will block the resource and any other operation, read or write will be blocked.
A write will block the resource too so again no other read or write operation can be performed before the action is terminated and mutex unlocked. So basically a mutex has 2 states: locked and unlocked.
read-write lock gives you more freedom based on the fact that read only operations do not result in inconsistencies. A read-write lock has 3 states: unlocked, read lock, write lock. A write lock works as a regular mutex blocking any other operation. A read lock on the contrary blocks only write operations.
I am not a Java expert but from this answer mutex in Java can be used as the following:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
private final Lock lock = new ReentrantLock(true);
lock.lock()
/*open file and do stuff*/
try {
// do something
} catch (Exception e) {
// handle the exception
} finally {
lock.unlock();
}
Instead here you can find a description of a read-write lock class.
From the implementation point of view you can create an instance of one of the two synchronization method and have your read/write thread instances keeping a reference to it, as an instance variable.

Related

java multiple writers and multiple readers locks

I'm trying to implement a system that follows the following constraints :
I have a shared resource, for example Atomic array
I want to support multiple reads from the array simultaneously.
I want to support multiple writes to the array simultaneously
I dont want read and write operations to happen simultaneously.
I found [this][1] stackoverflow post regarding a similar goal but I think that the solution suggested there is allowing reads simultaneously to writes :
Class ReadAndWrite {
private ReentrantLock readLock;
private ReentrantLock writeLock;
private AtomicInteger readers;
private AtomicInteger writers;
private File file;
public void write() {
if (!writeLock.isLocked()) {
readLock.tryLock();
writers.incrementAndGet(); // Increment the number of current writers
// ***** Write your stuff *****
writers.decrementAndGet(); // Decrement the number of current writers
if (readLock.isHeldByCurrentThread()) {
while(writers != 0); // Wait until all writers are finished to release the lock
readLock.unlock();
}
} else {
writeLock.lock();
write();
}
}
public void read() {
if (!readLock.isLocked()) {
writeLock.tryLock();
readers.incrementAndGet();
// ***** read your stuff *****
readers.decrementAndGet(); // Decrement the number of current read
if (writeLock.isHeldByCurrentThread()) {
while(readers != 0); // Wait until all writers are finished to release the lock
writeLock.unlock();
}
} else {
readLock.lock();
read();
}
}
As I see it, this code allows reads and writes simultaneously, for example : two threads will try to read/writer at the same time. Each one of them will enter the first if in the write/read. How can I make sure that the writes blocks the reads and reads blocks writes ?
[1]: Multiple readers and multiple writers(i mean multiple) synchronization
Rather than checking the lock repeatedly, just attempt using it:
private void writeInternal() {
//thread-unsafe writing code
}
public void write() {
if (!writeLock.tryLock()) {
writeLock.lock();
}
try {
this.writeInternal(); //in try-block to ensure unlock is called
} finally {
writeLock.unlock();
}
}
Using the readLock would be a similar approach. You also want to ensure you're truly using Read/Write locks and not just two separate locks:
private final ReadWriteLock lock;
public ReadAndWrite() {
this.lock = new ReentrantReadWriteLock();
}
Then you would access read/write locks via this.lock.readLock(), etc.

ReadWriteLock for concurrency access to file

I have a class which implements read and write operation to file in a concurrent environment. I know BufferedInputStream and BufferedWriter are synchronized but in my case read and write operations can be used simultaneously. Now I use ReentrantReadWriteLock but I'm not confident about a solution correctly.
public class FileSource {
private final File file;
private final ReadWriteLock lock;
public FileWrapper(final File file) {
if (Objects.isNull(file)) {
throw new IllegalArgumentException("File can't be null!");
}
this.file = file;
this.lock = new ReentrantReadWriteLock();
}
public String getContent() {
final Lock readLock = lock.readLock();
readLock.lock();
final StringBuilder sb = new StringBuilder();
try (final BufferedInputStream in =
new BufferedInputStream(
new FileInputStream(file))) {
int data;
while ((data = in.read()) > 0) {
sb.append(data);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
readLock.unlock();
}
return sb.toString();
}
public void saveContent(final String content) {
final Lock writeLock = lock.writeLock();
writeLock.lock();
try (BufferedWriter out =
new BufferedWriter(
new FileWriter(file))) {
out.write(content);
} catch (IOException e) {
e.printStackTrace();
} finally {
writeLock.unlock();
}
}
}
ReentrantReadWriteLock is the correct solution in this case or I need to use ReentrantLock or something else? (with a reason)
This discussion not about class design like File as a state or send File directly in the method or using nio package or ext. It shouldn't be a utility class. Method signatures and File as a field must stay without changes. It is about potential concurrency problems with File and InputStream\OutputStream.
RRWL is fine here. Of course, if some code makes a new FileWrapper("/foo/bar.txt") and then some other code also makes a separate new FileWrapper("/foo/bar.txt") those two wrappers will be falling all over themselves and will cause things to go pearshaped; I assume you have some external mechanism to ensure this cannot happen. If you don't, some take on ConcurrentHashMap and its concurrency methods (such as computeIfAbsent; don't use the plain jane get/put for these) can help you out.
Note that your exception handling is bad. Exception messages should not end in punctuation (think about it: Without this rule, 80% of all exception messages would end in an exclamation mark and will make log files a fun exercise), and in general, if you ever write catch (Exception e) { e.printStackTrace(); }, you go to that special place reserved for people who talk in movie theaters, and people who write that.
I'd say a method called saveContent is justified in throwing some checked exception; after all, it's rather obvious that can fail, and code calling it can feasibly be expected to perhaps take some action if it does.
If you just can't get there, the proper ¯\(ツ)/¯ I dunno catch block handler is: throw new RuntimeException("uncaught", e);. Not e.printStackTrace();. The latter logs to an uncontrollable place, shaves off useful information, and crucially, keeps on running as if nothing is wrong, silently ignoring the fact that a save call just failed, whereas the former preserves all and will in fact abort code execution. It makes it hard to recover, but at least it's easier than e.printStackTrace, and if you wanted it to be easier, than make a special exception. Or just throw that IOException unmolested (you get way shorter code to boot!).
Another insiduous bug in this code is that it uses 'platform default charset encoding' to read your file, which is very rarely what you want.
The new Files API can also read the entire file in one go, saves you a ton of code on that read op. As you upgrade your code, you get the benefit of the Files API's unique take on charset encodings: Unlike most other places in the java libraries, java.nio.file.Files will assume you meant to use UTF-8 encoding if you fail to specify (instead of 'platform default', i.e. 'the thing that you cannot test for that will blow up in production and waste a week of your time chasing after it').

Java Problems of Multithreading

I have problem with synchronized reading of my file. The case is simple: to handle data in the file. My problem is synchronization. I try to make it on object reader of class BufferedReader, but I have two problems. If I initialise reader in "try with resourses" of my method read() I will get to different objects of BufReader for the same object when I use it in different theads. The second one I initialise it in class and get the same object for threads, but a great problem with exeptions and unpredictable behavior. I will write code for the second situation. Can you advice me how to solve. I am studying now, that's why I want advices.
class FileReaderClass {
private File file = new File("src\\exer1\\Transfers.txt");
private BufferedReader reader = null;
private FileReader fr = null;
StringBuilder sb = new StringBuilder();
void read() throws IOException {
try {
fr = new FileReader(file);
reader = new BufferedReader(fr);
String buftext;
while ((buftext = reader.readLine()) != null){
synchronized (reader) {
System.out.println(Thread.currentThread().getName());//for testing
sb.append(buftext).append("\n");
//System.out.println(buftext);
}
}
}
catch (IOException e) {
e.printStackTrace();
}
finally {
if (reader != null)
reader.close();
if (fr != null)
fr.close();
}
}
}
class Reader1 implements Runnable {
private FileReaderClass frc;
public Reader1(FileReaderClass frc) {
this.frc = frc;
}
#Override
public void run() {
try {
frc.read();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class Ex1 {
public static void main(String[] args) throws InterruptedException {
FileReaderClass frc = new FileReaderClass();
Thread t1 = new Thread(new Reader1(frc));
Thread t2 = new Thread(new Reader2(frc));
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("---------------------");
System.out.println(frc.sb);
}
}
You are sharing the same FileReaderClass instance (that is frc) in two threads. Your FileReaderClass is not thread safe, that causes the problems. There are two alternatives here:
Create another instance of FileReaderClass, so that both threads will use different instances.
Mark the method read as snychronized.
You are synchronizing on the wrong level. Your sync guard is useless, as there are actually two readers. That's because each time read is called, a reader will be created, which will be a separate instance. We can't even know for sure, which reader instance was the subject of syncing.
If you want to parallelize reading of your file you should use the same reader but use proper synchronization for each read by any thread, so that it stays consistent, the easiest way is to make the whole read method synchronized and instantiate your readers in your class constructor.
As you may want to read and write to your string builder in the correct order, the thread that reads a line must only release the lock after it writes to StringBuilder, making your whole method to be pretty much guarded by a lock. It will work but you won't gain any performance, and for small files it may cause too much overhead, but as an exercise it may suffice.
Your code as it is has the following problems
You are always creating new readers, thus every thread calling this method will start from the beginning.
As previously stated by another answear you are synchronizing at the wrong level, any thread may read and the thread scheduler can suspend it right after, causing your writes to StringBuilder object to be out of order (i think it is not intended).

Read Write File Through Multiple threads

I want to read and write in a same file through threads.
But the condition is when the first thread finishes reading only then the other thread should write.
The solution that I proposed was:
class XYX {
public void read(){
synchronised(fileObj){
//write the reading logic
}
}
public void write(){
synchronised(fileObj){
//write the writing logic
}
}
}
Please let me know your ideas
I want to retain this Ordering
Reading - Writing - Reading - Writing - Reading - so on
I would use a Lock shared between the threads.
Since only one thread would hold the lock at any given time, you would make sure that the writing thread would only write to the file when the reading thread had released the lock (on a finally block!)
I would use two semaphores one for read, one for write, with only one unit between them. The read method waits on the read semaphore, then reads, then signals the write semaphore. The writer waits on the write semaphore, then writes, then signals the read semaphore.
Definitely use something from java.util.concurrent. I'd suggest two CountDownLatches here. Writing happens in one thread before calling cdl1.countDown and cdl2.await; the reading thread does the opposite.
If a total ordering of read-then-write must be maintained then it's easiest to use a monitor:
class XYX {
private final Object fileObj = new Object();
private volatile boolean writerWaits = true;
public void read(){
// read from file
synchronized(fileObj){
writerWaits = false;
fileObj.notify(); // signal the writer to begin
}
}
public void write(){
synchronized(fileObj){
while(writerWaits)
fileObject.wait(); // wait for signal from reader
}
// write to file
}
}

Java: merging InputStreams

My goal is to create (or use existing) an InputStream implementation (say, MergeInputStream) that will try to read from a multiple InputStreams and return the first result. After that it will release lock and stop reading from all InputStreams until next mergeInputStream.read() call. I was quite surprised that I didn't found any such tool. The thing is: all of the source InputStreams are not quite finite (not a file, for example, but a System.in, socket or such), so I cannot use SequenceInputReader. I understand that this will probably require some multi-thread mechanism, but I have absolutely no idea how to do it. I tried to google it but with no result.
The problem of reading input from multiple sources and serializing them into one stream is preferably solved using SelectableChannel and Selector. This however requires that all sources are able to provide a selectable channel. This may or may not be the case.
If selectable channels are not available, you could choose to solve it with a single thread by letting the read-implementation do the following: For each input stream is, check if is.available() > 0, and if so return is.read(). Repeat this procedure until some input stream has data available.
This method however, has two major draw-backs:
Not all implementations of InputStream implements available() in a way such that it returns 0 if and only if read() will block. The result is, naturally, that data may not be read from this stream, even though is.read() would return a value. Whether or not this is to be considered as a bug is questionable, as the documentation merely states that it should return an "estimate" of the number of bytes available.
It uses a so called "busy-loop", which basically means that you'll either need to put a sleep in the loop (which results in a reading latency) or hog the CPU unnecessarily.
Your third option is to deal with the blocking reads by spawning one thread for each input stream. This however will require careful synchronization and possibly some overhead if you have a very high number of input streams to read from. The code below is a first attempt to solve it. I'm by no means certain that it is sufficiently synchronized, or that it manages the threads in the best possible way.
import java.io.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class MergedInputStream extends InputStream {
AtomicInteger openStreamCount;
BlockingQueue<Integer> buf = new ArrayBlockingQueue<Integer>(1);
InputStream[] sources;
public MergedInputStream(InputStream... sources) {
this.sources = sources;
openStreamCount = new AtomicInteger(sources.length);
for (int i = 0; i < sources.length; i++)
new ReadThread(i).start();
}
public void close() throws IOException {
String ex = "";
for (InputStream is : sources) {
try {
is.close();
} catch (IOException e) {
ex += e.getMessage() + " ";
}
}
if (ex.length() > 0)
throw new IOException(ex.substring(0, ex.length() - 1));
}
public int read() throws IOException {
if (openStreamCount.get() == 0)
return -1;
try {
return buf.take();
} catch (InterruptedException e) {
throw new IOException(e);
}
}
private class ReadThread extends Thread {
private final int src;
public ReadThread(int src) {
this.src = src;
}
public void run() {
try {
int data;
while ((data = sources[src].read()) != -1)
buf.put(data);
} catch (IOException ioex) {
} catch (InterruptedException e) {
}
openStreamCount.decrementAndGet();
}
}
}
I can think of three ways to do this:
Use non-blocking I/O (API documentation). This is the cleanest solution.
Multiple threads, one for each merged input stream. The threads would block on the read() method of the associated input stream, then notify the MergeInputStream object when data becomes available. The read() method in MergedInputStream would wait for this notification, then read data from the corresponding stream.
Single thread with a busy loop. Your MergeInputStream.read() methods would need to loop checking the available() method of every merged input stream. If no data is available, sleep a few ms. Repeat until data becomes available in one of the merged input streams.

Categories

Resources