Java Thread implements read and write synchronization
WANG Hailong
This article gives an example that explains how to use Java Thread to process readers synchronize.
The source code in the article can be compiled. Java thread.testmain
This article can be read more than another article "Java Thread should pay attention to".
1. Reader-writer synchronization problem
Multiple readers can read the same buffer at the same time, but when the writer writes a buffer, there is a row of his nature, and other readers cannot read this buffer, and other writers cannot write this buffer. .
2. Code composition and ideas
This example includes four classes.
(1) Read and write resources RWResource, including the reader count, share resources, and all synchronization code.
(2) The reader RunnableReader. Implement the Runnable interface; read operation.
(3) Write RunnableWriter. Implement the Runnable interface; write operation.
(4) Test class TestMain. Generate and run multiple write threads and reading threads, display results.
This example is "cassette package" for shared resources, and the shared resource is included in a "box". And put all the synchronous code in the "box". The reader class and writer class do not perform synchronous processing, just apply for a resource, then read and write, after reading and writing, release resources.
The advantage of this method is that the code straight view of the "box" part of the "box" part, the compact control, reader and writer class do not have to care for synchronization issues. The disadvantage is that the shared resource "box" specifies the strict invocation order and call specifications, reader classes and writer classes must strictly abide by the call specification of the shared resource "box", otherwise the thread dead lock, or resource operation conflict.
However, even by the reader class and the writer class, thread synchronization is achieved, if not paying attention, the thread dead lock, or the resource operation conflict. This is the inherent problem of threads. :-)
The source code and description of these four classes are given below.
3. Read and write resources RWRESOURCE
Package thread;
Import java.util.list;
Import java.util.arraylist;
/ **
* Resource forreading and Writing
* /
Public clas rwresource {
/ **
* When readernumber == 0, There is no one reading or write.
* When ReaderNumber> 0, ReaderNumber Means Number of Readers.
* When ReaderNumber <0, IT Means That Some Writer IS Writing.
* /
PRIVATE INT ReaderNumber = 0;
/ **
* The Shared Resource for Writing or Reading
* /
PRIVATE LIST BUFFER = NULL;
Public rwresource () {
Buffer = new arraylist (512);
ReaderNumber = 0;
}
/ **
* Get buffer forreading.
* SHOULD BE CALLED BEFORE READ
* @Return the buffer
* /
Public synchronized list getBufferforreading () {
// if Some Writer is Writing, Wait Until No Writer IS WritingWhile (ReaderNumber <0) {
Try {
THIS.WAIT ();
} catch (interruptedexception e) {
E.PrintStackTrace ();
}
}
// when readernumber> = 0
ReaderNumber ;
Return buffer;
}
/ **
* SHOULD BE CALLED AFTER READING
* /
Public synchronized void finishreading () {
Readernumber--;
IF (readernumber == 0) {
THIS.NOTIFYALL (); // Notify Possible Waiting Writers
}
}
/ **
* Get buffer for Writing.
* SHOULD BE CALLED BEFORE WRITING.
* @Return the buffer
* /
Public synchronized list getBufferforWriting () {
// if Some Writer is Writing or Some Reader Is Reading, Wait Until No One is Writing or Reading
While (readernumber! = 0) {
Try {
THIS.WAIT ();
} catch (interruptedexception e) {
E.PrintStackTrace ();
}
}
// when readernumber == 0
ReaderNumber ---; // now readdernumber == -1.
Return buffer;
}
/ **
* SHOULD BE CALLED AFTER WRITING
* /
Public synchronized void finishwriting () {
ReaderNumber ; // readernumber = -1 1 = 0;
// readernumber Must Be 0 at this Point
THIS.NOTIFYALL (); // Notify Possible Waiting Writers Or Waiting Readers
}
}
The read and write resource class RWResource provides four Synchronized methods, divided into two groups, available for readers and writers.
Read the code above, you can see that the read and write resource class RWResource is used to control read and write access to the shared resource via the ReaderNumber count. When ReaderNumber is equal to 0, the resource is idle, you can read and write; when ReaderNumber is greater than 0, the resource is being read by some readers, other threads can be read, can not be written; when ReaderNumber is less than 0 (-1), explain resources Occupied by a writer, is written, other threads are not read, and you can't write.
The reader first calls getBufferforReading () to get the shared resource, if readerNumber is equal to 0, indicates that there is no writer to occupy resources, readers can get shared resources, at this time, ReaderNumber plus 1, indicating that the number of readers increases; after reading, you must call FinishReading () Release the resource, at this time, readERNUMBER is reduced, indicating that the number of readers is reduced. The writer first calls getBufferforWriting () gets the shared resource, if ReaderNumber is equal to 0, indicates that the resource is idle, the writer can get the shared resource, at this time, ReaderNumber minus 1, the value of ReaderNumber is -1, indicating that the resource is being written; After writing resources, the writer must call, you must call FinishWriting () release resources. At this point, ReaderNumber plus 1, the value of ReaderNumber becomes 0, returns to the idle state.
Also, please pay attention to the Wait () and NotifyAll () calls in the read and write resource class RWResource code.
The reader waits for the ReaderNumber less than 0, calling Wait (); writer waiting in the case of ReaderNumber greater than 0, calling Wait ().
At the time of releasing resources (FinishReading () or FinishWriting ()), if the value of ReaderNumber becomes 0, return to idle state, call notifyall (), notifying potential waiter - readers or writers.
4. Reader RunnableReader
Package thread;
Import java.util.list;
Import java.util.iterator;
Public Class RunnableReader Implements Runnable {
Private rwresource resource = null;
Public runnableReader () {
}
/ **
* Must Be Called Before Start Running
* @Param Theresource
* /
Public void setrwresource (rwresource theresource) {
Resource = theresource;
}
Public void run () {
While (true) {
// Get the reader's name
String readername = "[" thread.currentthread (). Getname () "]";
// first, get buffer forreading
List buffer = resource.getbufferforread ();
// reading
Iterator Iterator = Buffer.iterator (); item.hasnext ();) {
System.out.println (ReaderName Iterator.next ());
}
Int articles = buffer.size ();
Int thinkingTime = articles;
For (int i = 0; i // ThingKing Hard when reading} // Finish Reading Resource.finishreading (); // REST Try { Thread.sleep (articlenumber * 50); } catch (interruptedexception e) { E.PrintStackTrace (); } } } } The setrwresource () method in the above code is incorporated into the shared resource - read and write resources. This example uses the method of parameter transmission, sharing a read and write resource class between the reader and the writer. The run () method implements the Run () method of the runnable interface. First, get the name of the current reader (thread), then try to get read resources --Resource.getBufferforReading (), after obtaining resources, read all articles inside Buffer, reading while reading Ed (INT I = 0; I Note that during the above process, it must be strictly complied with such provisions, between Resource.getBufferforReading () and resource.finishreading (), read operations. 5. Wrantor RunnableWriter Package thread; Import java.util.list; Import java.util.iterator; Public Class RunnableWriter IMPLEments Runnable { Private rwresource resource = null; Private int articles = 0; Public runnablewriter () { Articlenumber = 0; } / ** * Must Be Called Before Start Running * @Param Theresource * / Public void setrwresource (rwresource theresource) { Resource = theresource; } Public void run () { While (true) { // Get the Writer's Name String Writername = "[" thread.currentthread (). Getname () "]"; // first, get buffer forreading List buffer = resource.getbufferforwriting (); INT nwritten = 3; // Write 4 Articles One Time For (int N = 0; n // Writing ArticleNumber ; String articles = "article" articles; Buffer.add (articlename); System.out.println (Writername Articles); INT thinkingTime = 10000; for (int i = 0; i // Thingking Hard when Writing } } // Finish Writing Resource.finishwriting (); // REST Try { Thread.sleep (500); } catch (interruptedexception e) { E.PrintStackTrace (); } } } } The setrwresource () method in the above code is incorporated into the shared resource - read and write resources. This example uses the method of parameter transmission, sharing a read and write resource class between the reader and the writer. The run () method implements the Run () method of the runnable interface. First, get the name of the current writer (thread), then try to get write resources --Resource.getBufferforWriting (), after obtaining resources, start writing 3 articles to Buffer, Write while thinking (note code in the code) INT i = 0; i Note that during the above process, it must be strictly complied with such provisions, between resource.getBufferforWriting () and resource.finishwriting (), write operations. 6. Test class TestMain Package thread; Public class testmain { Public static void main (String [] args) { // init generate shared resources RWResource resource = new rwresource (); / / Generate readers and set shared resources RunnableReader Reader = New RunnableReader (); Reader.setrwresource (resource); // Generate a write, set the shared resource. RunnableWriter Writer = New RunnableWriter (); Writer.setrwresource (resource); INT Writernumber = 5; // 5 writers Int ReaderNumber = 10; // 10 readers // Start Writers generates 5 write threads and gives each thread a name, Writer1, Writer2 ... For (int i = 0; i Thread thread = new thread (Writer, "Writer" (i 1)); Thread.start (); } // Give Writers Enough Time to Think and Write Articles Try { Thread.sleep (1000); } catch (interruptedexception e) { E.PrintStackTrace (); } // Start Readers generates 10 read threads and gives each thread a name, Reader1, Reader2 ... For (int i = 0; i Thread.start (); } } } The above test class TestMain code generates and runs multiple write threads and reading threads, resulting in the results possible: [Writer1] Article1 [Writer1] Article2 [Writer1] article3 ... [reader2] article1 [reader3] article1 [reader4] article1 [reader5] Article1 [reader6] article1 ... [reader1] article1 ... [Writer3] article67 [Writer3] article68 [Writer3] Article69 ... We can see that the number of the article written by Writer is different, and each Writer writes 3 articles each time, the process written will never be interrupted. Each reader reads all the articles each time, often has several readers to read the same article at the same time. 6. Two kinds of improvement ideas This example uses the "box" packaging method, put "lock" and resource (buffer) in the same "box" (rwresource), but "box" is incomplete to resource packaging, just simple The resource (buffer) is returned to the reader and writer, and does not package the resource (Buffer) access operation. In fact, it can be further encapsulated on the resource (buffer), for example, providing String [] ReaderBuffer () and WriteBuffer (String) for "box", in these two methods, according to the status of the readerNumber It is determined whether the read and write operation is legal, so that the overall code of the code will be better. The result is that the RWResource class's call specification and order must be more stringent, and must call the readerbuffer () method in resource.getbufferforreading () and resource.finishreaming (), must be in Resource.GetBufferforWriting () and resource.finishwriting () Call the WriteBuffer () method. Otherwise, these two methods will report an error. This will increase the complexity of the RWResource class. There are also some design factors that need to consider whether the READERBUFFER and WRITEBUFFER methods should be modified by SYNCHRONIZED? From the above example, there is a read operation between resource.getbufferforreading () and resource.finishreaming (); between resource.getbufferforwriting () and resource.finishwriting (), write. The code for reading and writing the operation section is not synchronized. So, between getBufferforReading () and finishreaming (), use synchronized to modify ReaderBuffer and WriteBuffer methods, it is more. However, from the perspective of the code's integrity, because ReaderBuffer and WriteBuffer methods need to read "lock", ReaderBuffer and WriteBuffer methods are better than the SYNCHRONIZED modifier. In view of these factors, this example has taken a compromise method. From the form, "lock" and "resource" are polymeric, in fact, the operation of both is separated, and is not related. "Box" is fully accessed by the resource after the "lock" state, the reader and writer get a resource. Another direction is to completely separate "resource" and "lock". Change the getBufferforReading method to StartReading, change the getBufferforWriting method to StartWriting, RWResource no longer allocate resources, only "lock" operation. Use rwresource as the RWLOCK class. The RunnableReader and RunnableWriter classes each add a setBuffer () method to share the buffer resource. In this way, RunnableReader and RunnableWriter classes have two separate methods: setBuffer () Sets shared resources, setrwlock () sets the read and write lock. The above ideas can be achieved for this example. Limited to the space, complete modification code cannot be given here. 7. supplement In this example, the read and write resource class RWResource has imposed the call sequence. Between Resource.getBufferforReading () and resource.finishreading (), read operations. Between resource.getbufferforwriting () and resource.finishwriting (), write operations. Requires Before performing some processing, you must perform a special operation, and you must also perform a special action after processing. This kind of artificial sequence undoubtedly increases the coupling of the code and reduces the independence of the code. It is very likely to be the root cause of thread deadlock and resource operation conflict. This has always made me uneasy, but I didn't find way to avoid it. After all, deadlocks or resource operation conflicts is the inherent problem of threads. Coincidentally, when I am uneasy, one of my friends provides information. SUN decided to introduce part of ConcURRENCY (concurrency) in JDK1.5 based on JCR. The following URL is an implementation of Util.Concurrent in the ConcURRENCY section. Very good information. It is helpful for handling multithreading. Http://gee.cs.oswego.edu/dl/classes/edu/oswego/cs/dl/util/concurrent/intro.html It provides a READWRITELOCK class, and the standard usage is as follows. Standard usage of readwritelock: Class x { Readwritelock rw; // ... Public void read () throws interruptedexception { rw.readlock (). acquire (); Try { // ... do the read } Finally { rw.readlock (). Release () } } Public void write () throws interruptedException { Rw.writelock (). acquire (); Try { // ... do the Write } Finally { rw.writelock (). Release () } } } We can see that readwritelock also requires the order of calls --aquire () and Release (). I enhance my confidence in my example. I also viewed the WriterPreferenceReadwritelock class, seeing a pair of methods, startread (), endread (); startwrite (), endwrite (). My mood is completely relaxed. Although my idea is rough, the general direction is correct. It is recommended that the examples of this paper are relatively simple, can be used as a introduction to the synchronization principle, then access the specialized code http://gee.cs.oswego.edu/dl/classess/concurrent /intro.html Enjoy it. :-)