Java notes: proficient in thread technology-basics-solving the problem of resource sharing (medium)-previous

Source: Internet
Author: User

In the previous article, I talked about how to create a thread in Java.CodeThe created threads are independent. That is to say, the created threads do not interfere with each other and perform their own operations independently, more importantly, the resources used by the threads created in the previous blog post are exclusive. No one will compete with them, but in the actual application of the thread, it is even more difficult to say that several threads will snatch the same resource, such as the ticket sales system of the train. It is much more troublesome to encounter such problems.

Due to the complexity of this problem, I divided the basic thread articles into two parts.ArticleTo release. Today is the previous article. Back to the topic. When n threads access a resource at the same time and n threads have the ability to modify and access the resource, the technology for resolving resource conflicts is too important, I remember that when I was studying the front-end optimization technology, the most frequently used term in my mind was high concurrency, and the website was able to ensure data accuracy under high concurrency, when I know that the Java thread scheduling mechanism is a random switching time slice, I feel this problem is much more complicated than I think.

To facilitate the elaboration of the topic, I want to write a monitoringProgram(Watcher), this monitoring program can check the content of the resources we call at any time, such as numbers. The Code is as follows:

View code

 Package Cn.com. sxia;

Public Class Alwayseven {

Private Int I;

Public Void Next (){
I ++;
I ++;
}

Public Int Getvalue (){
Return I;
}

Public Static Void Main (string [] ARGs ){
Final Alwayseven AE = New Alwayseven ();

New Thread ("wacther "){
Public Void Run (){
While ( True ){
Int Val = AE. getvalue ();
If (Val % 2 = 0 ){
System. Out. println (VAL );
System. Exit (0 );
}
}
}
}. Start ();

While ( True )
{
AE. Next ();
}
}

}

The program annotation is as follows:

The alwayseven class has an attribute I. The next method automatically adds 2 to the value of each execution of I, and getvalue returns the value of I. In the main function, we construct an alwayseven object AE. Note that final must be used before this variable. Otherwise, this variable cannot be accessed in the monitoring thread. Finally, we write an endless loop: call the next method.

When we execute this main function multiple times, we find that the printed results will be different. This shows a basic problem that occurs when a thread is used: We never know when the thread will run. It feels like we have created a pen and want to use it to write and write it. The pen disappears without any signs. This is really depressing, but this is a problem we often encounter when writing concurrent programs.

The preceding example shows that different threads use one resource together. The monitoring thread monitors the numerical changes of the I attribute in the AE object, in the main thread, the next method is continuously called to increase the I value. This is an instance competing for the same resource.

To better elaborate on the content I will elaborate on later, I would like to add the knowledge of some threads missing in the previous article:Background thread (Daemon ). Daemon is a thread that provides a common service in the background when the program is running, and this thread is not an indispensable part of the program. Therefore, when all non-Background threads end, the program will be terminated. Conversely, As long as any non-Background thread is still running, the program will not be terminated.. Let's look at the following code:

View code

 Package Cn.com. sxia;

Public Class Simpledaemon Extends Thread {

Public Simpledaemon (){
Setdaemon ( True );
Start ();
}

Public Void Run (){
While ( True ){
Try {
Sleep (100 );
} Catch (Interruptedexception e ){
E. printstacktrace ();
}
System. Out. println ( This );
}
}

Public Static Void Main (string [] ARGs ){
For ( Int I = 0; I <10; I ++ ){
New Simpledaemon ();
}
}

}

To make a thread a background thread, you must call the setdaemon () method before the thread starts. When we run this program, we find that no results are printed to the console. This is because there is no non-Background thread (except main, main is a non-Background thread) to keep the program running. Therefore, the program stops without printing any information.

Now, let's go back to the first instance code we mentioned. I want to write a test framework based on this code. This framework can simplify the testing of the thread examples we encounter. Our watcher thread is actually observing whether the Monitored object violates the constraints under certain circumstances. For the customer, the customer points to knowing whether the constraints we have defined have been violated, we also need to know the value of this violation of the constraints. If I have defined the following interface:

 
PackageCn.com. sxia;

Public InterfaceInvariantstate {

}

This interface is used to check whether a value violates our defined constraints. It has two implementation classes:

Success:

 
PackageCn.com. sxia;

Public ClassInvariantokImplementsInvariantstate {

}

Indicates failed:

PackageCn.com. sxia;

Public ClassInvariantfailureImplementsInvariantstate {
PublicObject value;

PublicInvariantfailure (object value)
{
This. Value = value;
}
}

The invariantfailure object will include an object that indicates information about the cause of failure. When the failure is monitored, we can print error information about failure.

Next we will define another interface. Any class that needs to test the constraints we define must implement this interface:

 
PackageCn.com. sxia;

Public InterfaceInvariant {
Invariantstate invariant ();
}

To prevent the program from having a problem with the underlying Java technical support on the platform (such as Windows, Linux, and multi-core systems of different versions), we define a time-out class, when the program fails to run normally within a certain period of time, the program will be automatically terminated. The Code is as follows:

Package Cn.com. sxia;

Import Java. util. timer;
Import Java. util. timertask;

Public Class Timeout Extends Timer {

Public Timeout ( Int Delay, Final String MSG ){
Super (True ); // Set true to indicate that the thread is a background thread (Daemon)
Schedule ( New Timertask (){

@ Override
Public Void Run (){
System. Out. println (MSG );
System. Exit (0 );
}
}, Delay );
}

}

in the code, we inherit the Timer class and call Super (true) in the constructor. This setting indicates that this thread will be created as a background program, as mentioned above, the background thread will not affect non-background programs. That is to say, when other threads exit the program, the created timeout object will not interfere with the running of other threads. The timer class is very useful. It is designed in Java to handle a large number of concurrent scheduling tasks. The following is the explanation of Timer In the JDK document:

Public ClassTimerExtendsObject
A tool used by a thread to schedule tasks to be executed in a background thread. You can schedule the task to be executed once, or perform the task repeatedly on a regular basis.
Corresponding to each timer object is a single background thread used to execute all timer tasks in sequence. The timer task should be completed quickly. If it takes too long to complete a timer task, it will "exclusively" the task execution thread of the timer. Therefore, this may delay the execution of subsequent tasks, and these tasks may be "heap together" and can be quickly and continuously executed only when the unfriendly tasks are finally completed.
After the final reference of the timer object is completed and all unprocessed tasks have been executed, the timer task execution thread will terminate normally (and become the garbage collection object ). However, this may take a long time. By default, the task execution thread does not run as a daemon, so it can prevent the application from terminating. If the caller wants to Quickly terminate the task execution thread of the timer, the caller should call the Cancel Method of the timer.
If the task execution thread that unexpectedly terminates the timer, for example, calls its stop method, all future attempts to schedule the task will cause illegalstateexception, it is like calling the Cancel Method of the timer.
This class is thread-safe: Multiple Threads can share a single timer object without external synchronization.
This class does not provide real-time guarantee: it uses Object. Wait (Long) Method to schedule the task.
Implementation notes: This class can be extended to a large number of tasks at the same time (there are no problems with thousands of tasks ). Internally, it uses a binary heap to represent its task queue. Therefore, the overhead of scheduled tasks is O (log n), where N is the number of tasks simultaneously scheduled.
Implementation Note: start the timer thread for all constructor methods.

Everything is ready. We have created a perfect class for monitoring. The Code is as follows:

 Package Cn.com. sxia;

Public Class Invariantwatcher Extends Thread {

Private Invariant invariant;

Public Invariantwatcher (invariant ){
This . Invariant = invariant;
Setdaemon ( True );
Start ();
}

Public Invariantwatcher (invariant, Final Int Timeout ){
This (Invariant );
New Timeout (timeout, "timeout ....");
}

Public Void Run (){
While ( True ){
Invariantstate state = invariant. Invariant ();
If (StateInstanceof Invariantfailure ){
System. Out. println ("invariant violated:" + (invariantfailure) State). value );
System. Exit (0 );
}
}
}

}

The invariantwatcher class is our defined monitoring class. In the invariantwatcher class, I define two constructors. The first constructor accepts a reference of the invariant object to be tested as a parameter, and then starts the thread. The second constructor calls the first constructor and creates a timeout to terminate all threads after a certain delay.

Note: we cannot throw an exception in the thread any more, because this will only terminate the thread and will not terminate the program, so I write system. Exit (0);

Next we will modify the code of our instance. The Code is as follows:

 Package Cn.com. sxia;

Public Class Evengenerator Implements Invariant {

Private Int I;

Public Void Next (){
I ++;
I ++;
}

Public Int Getvalue (){
Return I;
}

@ Override
Public Invariantstate invariant (){
Int Val = I;
If (Val % 2 = 0)
Return New Invariantok ();
Else
Return New Invariantfailure ( New INTEGER (VAL ));
}

Public Static Void Main (string [] ARGs ){
Evengenerator Gen = New Evengenerator ();
New Invariantwatcher (Gen );
While ( True ){
GEN. Next ();
}
}

}

The monitoring and testing framework we designed to learn about the Java thread of shared resources has been completed. Maybe some people may not understand why we need to design this architecture. It's okay, running the code in eclipse will feel a little bit better, and we will continue to look at it.

Starting from theory, what is the thread problem of resource sharing? We still use a pen as an example. There is a pen for both people to use it at the same time. The result is that the two people cannot argue over it. In the end, no one has used this pen, and everyone is suffering from it.

Therefore, we should avoid such a problem when using multithreading. To prevent such a problem, just add a lock to the thread when using resources. In this case, after the first thread to access the resource locks the resource, other threads can only wait for the first thread to unlock the lock to access the resource, when the lock is removed, another thread can lock the resource and access it.

Here I will introduce another important concept in the thread:Semaphores.

What is semaphores? This problem seems to be very complicated. My current understanding should be the simplest one. The following is my conclusion from the online documents:

 
Multiple Threads access a resource, such as database connection. Assume that several threads are running on the server to answer client requests. These threads need to connect to the same database, but only a certain number of database connections can be obtained at any time. How can you effectively allocate these fixed numbers of database connections to a large number of threads? One way to control access to a group of resources (except for simple on-ground locks) is to use the well-known counting semaphore ). Java multi-threaded semaphore count encapsulates the management of a group of available resources. Semaphores are implemented on the basis of simple locking, which is equivalent to a counter that can ensure thread security and initialize as the number of available resources. For example, we can initialize a semaphore to obtain the number of database connections. Once a thread obtains a Java multi-thread semaphore, the number of database connections can be reduced by one. When the thread consumes the resource and releases the resource, the counter will add one. When all resources controlled by semaphores are occupied, if a thread attempts to access this semaphores, the system will be blocked until available resources are released. The most common use of Java multi-threaded semaphores is to solve the "consumer-producer problem ". This problem may occur if another thread accesses the same shared variable when it is working. The consumer thread can only access data after the producer thread completes production. To solve this problem by using semaphores, you need to create a semaphores whose Initialization is zero so that the consumer thread can access this semaphores with blocking. The producer thread sends a signal (releasing resources) to the semaphore each time it completes unit work ).

We can simply understand semaphores. semaphores are the sign objects for communication between two threads. When the semaphores are 0, it indicates that the resources monitored by semaphores are available. If the semaphores are not zero, the resources monitored by semaphores are unavailable and threads are waiting. When the resources are available, the thread will increase the semaphore value, and then continue to execute and use this monitoring resource. The addition and reduction operations such as semaphore cannot be interrupted, which is very safe, therefore, semaphores ensure that two threads access the same resource at the same time without conflict.. The following is a simplified version of the semaphore concept:

 Package Cn.com. sxia;

Public Class Semaphore Implements Invariant {

Private Volatile Int Semaphore = 0;

Public Void Acquire (){
++ Semaphore;
}

Public Boolean Available (){
Return Semaphore = 0;
}

Public Void Release (){
-- Semaphore;
}

@ Override
Public Invariantstate invariant (){
Int Val = semaphore;
If (Val = 0 | val = 1 ){
Return New Invariantok ();
} Else {
Return New Invariantfailure ( New INTEGER (VAL ));
}
}

}

This Code contains three methods. Since the thread needs to check the availability when obtaining resources, we have to call this type of object so that the semaphore value is not 0 or 1 logically, the test code I wrote is as follows:

 Package Cn.com. sxia;

Public Class Semaphoretester Extends Thread {

Private Volatile Semaphore semaphore;

Public Semaphoretester (semaphore ){
This . Semaphore = semaphore;
Setdaemon ( True );
Start ();
}

Public Void Run (){
While ( True ){
If (Semaphore. Available ()){
Yield ();
Semaphore. Acquire ();
Yield ();
Semaphore. Release ();
Yield ();
}
}
}

Public Static Void Main (string [] ARGs) Throws Interruptedexception {
Semaphore semaphore = New Semaphore ();
New Semaphoretester (semaphore );
New Semaphoretester (semaphore );
New Invariantwatcher (semaphore). Join ();
}

}

We can see that the content in the run method ensures that the semaphore values are both 0 or 1, but when we run this main function, there will always be an error, for example:

 
Invariant violated:-1

If the program reports an error and exits, multiple threads accessing the same resource may cause data errors, which is the biggest risk of writing a multi-threaded program.

Well, I learned this today. I will finish my blog on sharing resources tomorrow.

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.