Chapter 14 Multithreading

xiaoxiao2021-03-06  44

Chapter 14 Multithreading

With an object, a program can be divided into an independent area. We usually also need to convert a program into multiple independent subsidy.

Each sub-task like this is called a "thread". When writing programs, you can imagine each thread into independent operation and have your own dedicated CPU. Some basic mechanisms actually segmented the CPU for us. We usually don't have to care about these details, so multithreaded code writing is quite simple.

At this time, it is appreciated that some definitions are helpful for future learning. "Process" refers to a "self-contained" running program with its own address space. The Multi-Task operating system can run multiple processes (programs) at the same time, but is actually due to the role of the CPU mutual mechanism, allowing each process to get its own CPU time slice. However, since the rotation speed is very fast, all the programs seem to be just in "simultaneous" running. "Thread" is a single sequential control flow within the process. Therefore, a process may accommodate multiple simultaneous execution threads.

Multi-threaded applications are wide. However, in general, some parts of the program are associated with a specific event or resource, while do not want to suspend the execution of other parts of the program for it. In this way, you can consider creating a thread to associate it with that event or resource together and let it run independently of the main program. A good example is "quit" or "exit" button - we don't want to poll this button in each part of the program, but also hope that the button will respond in time (so that the program seems to be It is often polling it). In fact, one of the most important uses of multi-thread is to build a "reaction sensitive" user interface.

14.1 Reaction Sensitive User Interface

As our starting point, please think about a program that needs to perform some CPU intensive calculations. Since the CPU "wholeheartedly" is those computing services, it is very slow, there is almost no reaction to the user. Here, we use a synthetic Applet / Application to simply display a counter results:

752-753 page program

In this program, the AWT and the program code should be familiar, and Chapter 13 has a very detailed explanation of this. The GO () method is the treatment of the procedure of the whole, place the current count value into the TextField T, and then make the count value.

Some unlimited cycles in the Go () are called SLEEP (). Sleep () must be associated with the THREAD object, and it seems that each application has some threads to associate with it (in fact, Java itself is based on the thread, there must be some threads to write with us The application runs together). So whether we explicitly use threads, you can use thread.currentthread () to generate the current thread used by the program, and then call SLEEP () for that thread. Note that thread.currentthread () is a static method of the Thread class.

Note that Sleep () may "throw" out an InterruptException (interrupt violation) - although such a violation is considered to be a "malicious" means of aborting threads, and should be eradicated as much as possible. Remind everyone, violations are produced for abnormal conditions, not for normal control flow. Here, there is an interrupt of a "sleep" thread to support a future language characteristic.

Once the START button is pressed, Go () will be called. Study Go (), you might naturally (just like me) think it should support multithreading because it will enter the "sleep" state. That is, although the method itself "is asleep", the CPU should still be busy monitoring other buttons "Press" event. But there is a problem, that is, go () is never returned because it is designed to an infinite loop. This means that ActionPerformed () will not return at all. Since the first button is fed into actionPerformed (), the program cannot control any other events (if you think, you must "kill" in some way - the easiest way is in the console window. Press the Ctrl C button). The most basic problem here is that Go () needs to continue to perform its own operation, while it also needs to return so that ActionPerformed () can be completed, and the user interface can continue to respond to the user's operation. However, traditional methods such as object Go () cannot return control to other parts of the program while continuing. This seems to be something impossible, just like the CPU must be in two places, but threads can solve everything. "Thread Model" (and programming support in Java) is a programming specification that implements several operations in a single program. According to this mechanism, the CPU can allocate your own time for each thread. Each thread "feels" it seems to have the entire CPU, but the calculation time of the CPU is actually shared at all threads.

The thread mechanism has lowered some calculation efficiency, but regardless of the program's design, resource balance, or the convenience of user operation, it has obtained huge benefits. Comprehensive consideration, this mechanism is very valuable. Of course, if you have installed multiple CPUs, the operating system can determine which threads are allocated for different CPUs, and the overall running speed of the program will become faster (all of which require operating system and application support). Multi-threaded and multitasking is the most effective way to give full play to multi-processor system capabilities.

14.1.1 Inheriting from thread

In order to create a thread, the easiest way is to inherit from the Thread class. This class contains everything you need to create and run the thread. The most important method of Thread is Run (). However, in order to use Run (), it must be overloaded or covered to make it fully active. Therefore, Run () belongs to the code that will "concurrency" or "simultaneously" in the program.

The following example creates any number of threads and assigns a unique number (generated by a static variable) to each thread to track different threads. The run () method of Thread is overwritten here. Each time you pass a cycle, the count is reduced by 1 - the count is 0, complete the loop (once returns Run (), threads will abort the operation).

755 page

The Run () method is almost certainly containing some form of loop - they will continue until the thread is no longer needed. Therefore, we must specify specific conditions to interrupt and exit this cycle (or in the above example, simply returning from RUN ()). Run () is usually in the form of an infinite loop. That is to say, it will run forever by blocking the STOP () or Destroy () call to the thread, it will always run (until the program is completed).

In Main (), you can see the created and run a large number of threads. Thread contains a special method called start (), its role is to initialize threads and then call Run (). So the entire step includes calling the builder to build an object, then configure thread with start (), then call Run (). If you do not call START () - If appropriate, you can do it like the builder - thread will never start. Below is the output of the program runs a time (note each run):

Page 756

It can be noted that Sleep () is called everywhere in this example, however the output indicates that each thread has obtained the CPU execution time belonging to himself. It can be seen from it, although Sleep () rely on the existence of a thread, but is not related to the allowed or prohibited thread. It is just another different way.

It can also be seen that the thread is not running in the order when they create. In fact, the order in which the CPU handles an existing thread set is uncertain - unless we personally intervene, and use Thread's setPriority () method to adjust their priority.

MAIN When you create a Thread object, it does not capture the handle of any object. Ordinary objects are a "fair competition" for garbage collection, but thread is not the case. Each thread will "register" yourself, so there is a reference to it in some facts. In this way, the garbage collector had to "look" it ".

14.1.2 Multithreading for user interfaces

Now, we may use a thread to solve problems in Counter1.java. One of the techniques used is to place a "subtask" in a thread's Run () method - i.e., a loop located within Go (). Once the user presses the START button, the thread will start, but immediately end the thread creation. In this way, although the thread is still running, the main work of the program can continue (waiting and responding to the user interface event). Here is the specific code:

Page 757-759

Now, Counter2 has become a fairly straightforward program, its unique task is to set and manage the user interface. But if the user is now pressing the START button, it will not really call a method. It is not a thread that creates a class, but creates separatesubtask, then proceeds to the Counter2 event loop. Note that the handle of SeparateSubtask is saved so that we can switch the Runflag within SeparateSubtask when we press the onoff button. Then then the thread can be started (when it sees the flag), then it will be discontinued (can also set SeparateSubtask to an internal class to achieve this).

The SeparateSubtask class is a simple extension to Thread, which has a builder (where the Counter 2 handle is saved, and then running the thread by calling start ()) and a Run ()-essentially contains counter1.java Go ( The code in the inside. Since SeparateSubtask knows that he has accommodated a handle pointing to a Counter2, you can intervene when you need it, and access the TestField of Counter2 (text field).

Press the ONOFF button to get the correct response almost immediately. Of course, this response is actually not "immediately", it is different from the system that is driven by "interrupt". Only the thread has the execution time of the CPU and noted that the tag has changed, and the counter will stop.

1. Improve code with internal class

Let's talk about the topic of the question, please pay attention to the combination between the SeparateSubtask and Counter2 classes. SeparateSubtask combines with Counter2 "intimate" - it must hold a handle pointing to your own "Father" Counter2 object so that you can call and manipulate it. But two classes are not really merged into a single class (although in the next section, we will say that Java does provide merged methods), because they do their different things, and are different times Created. But in any case, they are still closely combined (more accurately, they should be called "United"), so make the program code look a bit awkward. In this case, an internal class can significantly improve the "readability" and execution efficiency of the code: 759-761

This SeparateSubtask name will not conflict with the SeparateSubtask in the previous example - even if they are in the same directory - because it has hidden as an internal class. Everyone can also see the internal class is set to Private properties, which means that its fields and methods can get the default access (except for Run (), it must be set to public because it is in the base class is public). In addition to Counter2i, any other aspect is accessible to the private class. And since the two classes are closely combined, it is easy to relax access restrictions between them. In SeparateSubtask, we can see that the invertflag () method has been deleted because Counter2i can now access Runflag directly.

In addition, Note SeparateSubtask's builder has been simplified - it is now the only used use is starting thread. The handle of the Counter2i object is still captured as before, but it is no longer to achieve this by manual delivery and reference external objects. The internal class mechanism at this time can automatically take it. In Run (), you can see that the access to T is directly, it seems that it is a field of SeparateSubtask. The T field in the parent class can now become Private, because SeparateSubtask can freely access it in the premise of any special licenses - and whether it will turn the fields as "private" attributes, in any case Some power outside the class is inadvertently changing them.

Regardless of whether, as long as it is noted that the combination of classes is close, consider using the internal class to improve the writing and maintenance of the code.

14.1.3 Merger with the primary class

In the above example, we see the thread class (main) of the thread and the program is separated. This is very reasonable, and it is easy to understand. However, there is still another way to use it. Although it is not very clear, it is generally more concise (this also explains why it is very popular). This form can be combined with the thread class by turning the main program class into a thread. Due to a GUI program, the main program class must inherit from Frame or Applet, so you must add additional functions with an interface. This interface is called Runnable, which contains the basic method consistent with Thread. In fact, Thread also implemented Runnable, which only pointed out that there is a Run () method.

For the merged program / thread, its usage is not very clear. When we start the program, a runnable object is created, but it will not start the thread itself. The startup of the thread must be explicit. Below this program demonstrates this, it will reproduce the functionality of Counter2:

Page 762-763 Page 1

Now Run () is within the class, but it is still in the "sleep" state after the init () end. If you press the start button, the thread will use how many 暧昧 的 的 的 的 方式 方式 (线 程 不 不 不): New Thread (Counter3.This);

If there is a runnable interface in the same way, it actually only means that it has a run () method, but there is no special thing related to it - it does not have any natural thread processing power, which inherited from Thread Category is different. So in order to generate threads from a runnable object, you must create a thread separately and deliver the Runnable object; you can use a special builder to use a runnable as your own parameter. Then you can call START () as follows:

SelfThread.start ();

Its role is to perform regular initialization operations and then call Run ().

One of the biggest advantages of runnable interface is that all things are from the same class. If you need to access something, just simply access it, you don't need to involve an independent object. But for this convenience, it is also a cost - only a single thread that can be run for that particular object (although multiple objects can be created, or create other objects in different classes).

Note that the runnable interface is not the culprit of this restriction. It is due to Runnable and our primary class, because each application can only primary class.

14.1.4 Make multiple threads

Now consider the problem that creates multiple different threads. We don't use the previous example to do this, so you must go back and use the multiple independent classes inherited from Thread. But this is a more conventional program, and it is more easy to understand, so although the previous example reveals the coded style we can often see, it is not recommended to do it in most cases, because it is just a little complex, and The flexibility is slightly lower.

The following example reproduces the previous encoding style with the counter and the switch button. But this time, all information (buttons and text fields) of a particular counter is within its own, from thread inherited. All fields in Ticker have private properties, which means that the specific implementation of Ticker can be arbitrarily modified according to the actual situation, including modifying the number and type of data components for obtaining and displaying information. After creating a Ticker object, the builder requests an AWT container (Container) handle - Acicker populates the container with its own visual components. In this way, once the visual component is changed, the code using Ticker does not need to be modified separately.

Page 764-766

Ticker includes not only its thread processing mechanism, but also provides tools for controlling and displaying threads. You can create any number of threads in your own will, do not have to create a windowing component in a clearly.

In Counter4, there is an array of Ticker objects called S. In order to achieve maximum flexibility, the length of this array is initialized with the web page with the block parameters. Below is a substantial look of the long parameters of the web page, which is embedded in the description of the applet:

Among them, Param, Name, and Value are keywords that all web pages. Name is a reference to parameters in the program, and Value can be any string (not just parsing into a number). We note that the determination of the length of the array S is done inside the init (), which is not provided as part of the embedded definition of S. In other words, the following code cannot be used as part of the class definition (should be located outside of any method):

Inst size = integer.parseint ("SIZE");

Ticker [] s = new ticker [size]

It can be compiled it, but it will get an empty pointer violation in the running period. However, if getParameter () is initialized into init (), you can work. The program framework will make the necessary startup work in order to collect some parameters before entering init ().

Further, the above code is simultaneously set to a block and an application (program). In the case of it is an application, the Size parameter can be extracted from the command line (otherwise a default value is provided).

After the length of the array is built, you can create a new Ticker object; as part of the Ticker Builder, the button and text field for each Ticker will be added to the block.

After pressing the START button, it will traverse the entire Ticker array and start () for each Ticker. Remember, start () will make the necessary thread initialization work and then call Run ().

The Togglel monitor simply switches the tag in the Ticker. Once the corresponding thread needs to modify this tag, it will make a corresponding reaction.

One advantage of this example is that it allows us to easily create a large collection consisting of a single sub-task and monitors their behavior. In this case, we will find that with the increase in sub-tasks, the numbers displayed by the machine may have a bigger difference, which is due to the way the thread provides services.

You can also try to experience the important role of Sleep (100) in Ticker.Run (). If you delete Sleep (), the situation will progress well before pressing a handover button. After pressing the button, the specific thread will have a failure Runflag, and Run () will deeply fall into an infinite loop - it is difficult to abort the exit during multi-task processing. Therefore, the response sensitivity of the program to the user will be greatly reduced.

14.1.5 daemon thread

The "daemon" thread is to provide a "General" service during the background of the program, but it does not belong to a basic part of the program. Therefore, once all non-DAEMON threads are completed, the program will stop running. Instead, if any non-Daemon thread is still running (for example, there is a thread that is running main ()), the runtime run will not abort.

By calling Isdaemon (), you can investigate a thread is not a daemon, and you can open or close a thread's daemon state with setDaemon (). If it is a daemon thread, then any thread it created will also automatically have a Daemon property.

The following example demonstrates the usage of the daemon thread:

Page 768-769

The daemon thread can set your own daemon tag to "true" and then generate a series of other threads and think they also have the daemon property. Subsequently, it enters an infinite loop where you call Yield () to give up control of other processes. In one version of this program, the infinite loop will make the int counter value, but the entire program will be in a pause state. After exchange for Yield (), it can make the program full "vitality", which does not cause the person to be stagnant or slowly. Once main () completes your own work, there is nothing to prevent the program from being running, because there is only a daemon thread that runs here. So you can see the result that is displayed after starting all Daemon threads, System.IN also has the corresponding settings, so that the program can wait a carriage return before interrupt. If such a setting is not made, you can only see some of the results of the DAEMON thread (try to change the readline () code to different lengths of SLEEP () calls, see what is the performance).

14.2 Sharing limited resources

You can imagine a single-threaded program into an isolated entity, which can traverse our problem space, and you can only do one thing. Since there is only one entity, I will never worry that there will be two entities at the same time trying to use the same resources, just like two people want to stop a parking space, and they want to pass a door, even simultaneously.

After entering the multi-threaded environment, they are never isolated. There may be two or more threads try to simultaneously the same limited resources. This potential resource conflict must be prevented, otherwise two threads may have access to a bank account at the same time, print to the same computer, and adjust the same value, etc.

14.2.1 Error Method for Resource Access

Now consider replacing the counter frequently seen in this chapter. In the following example, each thread contains two counters, which are value added and displayed in Run (). In addition, we used another thread of the Watcher class. Its role is to monitor counters and check if they remain equal. This surface is a meaningful action, because if you view the code, you will find that the counter is definitely the same. But the actual situation is not necessarily. Below is the first version of the program:

Page 770-773

As usual, each counter contains its own display components: two text fields and a label. Depending on their initial value, it can be known that the count is the same. These components add Container in the Twocounter builder. Since this thread is activated by a "Press button" operation of the user, START () may be called multiple times. But for a thread, multiple calls to thread.Start () are illegal (it will generate violations). In Started tags and overloaded START () methods, you can see preventive measures taken to this situation.

In Run (), the value added to count1 and count2 appears to keep them exactly the same as the display mode surface. SELEP () will then be called; if there is no call, the program will be wrong because it will cause the CPU to exchange tasks.

The synchtest () method seems to be meaningless actions, it checks if count1 is equal to count2; if it is not equal, set the label to "unsynched". But first, it calls a static member of class Sharing1 to increase the value and display an access counter, pointing out how many times this check has been successful (so the reason for this will become very obvious in this other versions)) .

The Watcher class is a thread that is called Synchtest () to all Twocounter objects in the active status. Together, it will traverse the arrays accommodated in Sharing1 objects. Watcher imagines become the shoulders of the Twocounter object constantly "peeks". Sharing1 contains an array of Twocounter objects, which is initialized via init (), and starts as threads after we press the "START" button. If you press the "Observe" button, you will create one or more viewers and investigate the no-fired Twocounter.

Note To let it run as a block in the browser, the web page needs to include the following lines:

774 page

You can change the width, height, and parameters yourself, and test it according to your own will. If Size and OBServers are changed, the behavior of the program will also change. We also noticed that it is designed as a separate application to run by accepting parameters from the command line (or using the default).

Below is the most "incredible". In Twocounter.Run (), infinite loops are only constantly repeating adjacent lines:

T1.SETTEXT (Integer.toString (Count1 ));

T2.SETTEXT (Integer.Tostring (count2 ));

(Like "sleep", but not important here). But when you run, you will find that count1 and count2 are "observed" (observed with Watcher) is not equal! This is caused by the essence of the thread - they can hang (pause) at any time. Therefore, there is sometimes the execution pause between the execution time of the above two lines. At the same time, the Watcher thread is just right, and it is just compared at this time, causing the counter that is unequal.

This example reveals a very basic problem when using threads. We follow the unknown from knowing a thread. Imagine yourself sitting in front of a table, put a fork on the table, preparing for the fork from your own last food. When the fork is to encounter food, the food suddenly disappears (because this thread has been hanged, and the other thread comes in "stealing". This is the problem we have to solve.

Sometimes, we don't mind whether a resource is being accessed when trying to use it (food in other trays). However, in order for multi-thread mechanisms to function normally, some measures need to prevent two threads from accessing the same resource - at least in key periods.

To prevent such conflicts, simply lock it when the thread is used to use a resource. After the first thread of the access resource will be plus the lock, other threads can no longer use that resource unless they are unlocked. If the front seat of the car is a limited resource, "this is mine!" The child will advocate it.

14.2.2 How to Share Resources in Java

A special resource-Object-Ava provides a built-in mechanism to prevent their conflicts. Since we usually set data elements to a PRIVATE (private) class, then only access to those memory, just to set a specific method to Synchronized (synchronous), it can effectively prevent conflicts. At any time, only one thread calls a SYNCHRONIZED method for a specific object (although the thread can call multiple object synchronization methods). The simple synchronized method is listed below:

Synchronized void f () {/ * ... * /}

Synchronized void g () {/ * ... * /} Each object contains a lock (also called "monitor"), which automatically becomes part of an object (there is no need to write any special code for this). When you call any Synchronized method, the object will be locked, and any other Synchronized method of that object cannot be called unless the first method has completed its own work and unlock it. In the above example, if f () is called for an object, the G () cannot be called for the same object, unless F () is complete and unlocked. Therefore, all SYNCHRONIZED methods for a particular object share a lock, and this lock can prevent multiple methods from writing to universal memory (such as multiple threads at the same time).

Each class also has its own lock (as part of the class Class object), so the Synchronized Static method can be locked in a class to prevent contact with STATIC data.

Note If you want to protect your other resources, you can enforce those resources via the Synchronized party at the same time as multiple threads.

1. Synchronization of the counter

After equipped with this new key, the solution we can take is more flexible: you can simply use the Synchronized keyword for the method in Twocounter. The following example is a revision of the preparation, where a new keyword is added:

Page 775-778

We noticed whether Run () or synchstest () is "synchronous". If you only synchronize one of the methods, then the other can freely ignore the lock of the object and can be called. So you must remember an important rule: For all methods accessing a key shared resource, you must set them to synchronized, otherwise it will not work properly.

Now I have encountered a new problem. Watcher2 will never see what is going on because the entire Run () method is set to "synchronize". And because you have to run Run () for each object, the lock can never be opened, and synchtest () will never be called. It can see this result because AccessCount has not changed at all.

In order to solve this problem, one of the ways we can take is to isolate some of the code in Run (). I want to use this part of the code that is isolated from the "critical area", and uses the Synchronized keyword in different ways to set a critical area. Java provides support for key areas via "synchronization block"; this time, we specifically use the synchronized keyword to synchronize the code in which the enclosed code is used. As follows:

Page 779

Before you can enter the synchronization block, you must get the lock on Synchobject. If there are other threads to get this lock, the block cannot be entered, and must wait to release the lock.

The Synchronized keyword can be removed from the entire run (), and the two key lines are enclosed with a synchronization block to complete the modification of the Sharing2 example. But what object should be used as a lock? That object has been marked by synchtest () - that is, the current object (this)! So the modified Run () method is like this:

779 page program

This is a unique modification that must be made to Sharing2.java, we will see that although the two counters will never be separated from sync (depending on how they are allowed to check them), but they will provide Watcher to Watcher in Run (). Access permission.

Of course, all synchronization depends on whether the programmer is diligent: Each part of the code to access the shared resource must be packaged into an appropriate synchronization block. 2. Synchronization efficiency

Because two methods are prepared for the same data, no matter how efficient is impressive. It seems that a better way is to set all the methods to automatic synchronization and completely eliminate the synchronized keyword (of course, an example containing synchronized run () shows that this is not very uncommon). However, it also reveals that it is not a "low-cost" program - the cost (entering and exiting method) paying by a method (entering and exiting method, the main body of the method) is at least 40 times, and according to our specific plan This cost may become higher. Therefore, if a method is known that a method will not cause a conflict, the most sensible approach is to revoke the synchronized keyword.

14.2.3 Review Java Beans

We have now understood synchronization, then can be changed from another angle to examine Java Beans. Whenever you create a bean, you must assume that it is running in a multi-threaded environment. this means:

(1) As long as it is feasible, all public methods of Beans should be synchronized. Of course, this also brings "synchronization" overhead during operation. If you particularly know this problem, you will remain "disagree" in a key area, but pay attention to this is usually not very easy to judge. The qualified method tends to be small size (as getcirclesize ()) and / or "micro" in the following example. That is, this method call is performed in such a small code to do so as to perform the object. If this method is set to "disagree", it may not have a significant impact on the execution speed of the program. All public methods of a bean may also be set to Synchronized, and only the Synchronized keyword is deleted only if it is necessary to guarantee it.

(2) If a multi-style event is given to a series of "listeners" interested in that event must be added or deleted when moving in the list.

The first point is easy to handle, but the second point needs to consider more things. Let us provide BangBean.java provided by the previous chapter as an example. In that example, we ignore the synchronized keyword (there is no introduction that is not introduced), and set the shape as a single pattern, thereby avoiding multithreaded problems. In the following modified version, we can work in a multi-threaded environment and use multi-style technology for the event:

Page 781-784

It is easy to add synchronized to the method. Note Note In AddactionListener () and RemoveActionListener (), the ActionListener is now added and moves from a vector, so you can use any of you according to your desire.

We noticed that the NotifyListeners () method is not set to "synchronize". The call to this method can be issued from multiple threads. In addition, the call to addActionListener () and RemoveActionListener () may be issued in the middle of the notifyListeners (). This will obviously cause problems because it denies Vector ActionListeners. To alleviate this problem, we "clone" in a synchronized clause, and the clon is negative. This can manipulate the vector without affecting the notifyListeners ().

The Paint () method is also not set to "synchronize". Compared to simply adding your own methods, you decide whether to synchronize overloaded methods. In this example, it seems to work properly regardless of whether Paint () is "synchronized". However, the questions must be considered include: (1) How does the method will modify the "key" variable inside the object? To determine if a variable is "critical", it must be known if it will be read or set up by other threads in the program (see, reading, or settings almost definitely through the "synchronization" method, so you can only They check). No changes will occur for the case of Paint ().

(2) Is the method be based on these "key" variables? If a "synchronization" method modifies a variable, and our method uses this variable, so generally willing to set your own method to "synchronize". Based on this premise, everyone can observe that CSIZE is modified by the "synchronization" method, so Paint () should be "synchronous". But here, we can ask: "If CSIZE has changed during the implementation of Paint (), what happens to happen?" If the discovery is not too bad, and it is just a temporary effect, then the most Keep the "disagree" state of Paint () to avoid additional overhead of synchronous method calls.

(3) The third clue to pay attention is that the Paint () base class version is "synchronized", here it is not synchronous. This is not a very strict parameter, just a "clue". For example, in the current situation, a field changed by the synchronization method (good CSIZE) has been synthesized into the Paint () formula, and may have changed. However, please note that Synchronized cannot inherit - that is, if a method is "synchronized" in the base class, it does not automatically enter the "synchronization" state in the derived class overload version.

Test code in TestBangbean2 has been modified on the basis of the previous chapter, which has joined an additional "audience" to demonstrate multi-modeling capabilities of Bangbean2.

14.3 blockage

A thread can have four states:

(1) New: Thread objects have been created, but they have not been launched, so they are not running.

(2) Runnable: Means that once the time slider is provided to a thread, the thread can start running immediately. Therefore, threads may be, or may not be running, but once condition license, there is nothing to stop its operation - it has neither "dead" and is not "blocked".

(3) Dead: After returning from your own Run () method, a thread has been "dead". You can also call the stop () to die, but it will generate a violation - a subclass belonging to Error (that is, we usually do not capture it). Remember that a violation of "throw" should be a special event instead of the normal program run. So don't recommend using STOP () (in Java 1.2 is resolutely opposed). There is also a destroy () method (which will never be implemented), avoid calling it as much as possible, because it is very arbitrary, and does not unlock the object at all.

(4) Block: The thread can run, but there is something that hinders it. If the thread is in a plug, the scheduling mechanism can simply skip it and do not assign any CPU time. No operation is not taken unless the thread enters the "run" status again.

14.3.1 Why is it more interesting in the foregoing four states, it is worthy of further discussion. Threads are blocked may be caused by the following five aspects:

(1) Call the SLEEP (number of milliseconds) so that the thread enters the "Sleep" state. This thread will not run in the specified time.

(2) Suspend the execution of the thread with suspend (). Unless the thread receives the resume () message, the "run" state is not returned.

(3) Suspend the execution of the thread with Wait (). Unless the thread receives the Nofify () or NotifyAll () message, it will not become "run" (yes, this looks very similar to the reason 2, but there is an obvious difference that we will reveal immediately).

(4) The thread is waiting for some IO (input and output) operations.

(5) The thread attempts to call another object's "synchronization" method, but that object is in the locked state, temporarily unwilling.

You can also call Yield () (a method of the Thread class) to automatically discard the CPU so that other threads can run. However, if the scheduling mechanism feels that our thread has enough time and jumps to another thread, the same thing will happen. That is, there is nothing to prevent the scheduling mechanism from restarting our thread. After the thread is blocked, there are some reasons to cause it to continue to run.

The following example shows all five ways to enter the plug. They all exist in a file named Blocking.java, but use the scattered pieces here (you can pay attention to "Continued" and "Continuing" logo before and after the segment. Use the tools introduced in Chapter 17. These pieces are connected together). First let's take a look at the basic framework:

Page 786-787

The Blockable class intends to be a basic class for all classes of this example. A blockable object contains a TextField named State that displays information about objects. The method used to display this information is called UPDATE (). We found that it produces a class name in getClass.getName (), not only print it; this is due to Update (0 does not know the accurate name of the class that you call it, because the class is derived from blockse. .

In block, the change indicator is an int i; the RUN () method of the derived class is a value added.

A thread of the Peeker class is launched for each BloAckable object. Peeker's task is to call the read () method, check the block associated with yourself, see if I has changed, and finally report the results of the check with its Status text field. Note that read () and update () are synchronized, and the lock can be freely released, this is very important.

Sleep

The first test of this program is made with SLEEP ():

Page 788-789

In Sleeper1, the entire Run () method is synchronized. We can see that Peeker associated with this object can run normally until we start threads, then Peeker will stop completely. This is a form of "blocked": because sleeper1.run () is synchronized, and once the thread is started, it is certainly in the Run () inside, and the method will never give up the object lock, causing the PEEKER thread to block the clogging.

Sleeper2 provides a solution by setting up asynchronous operations. Only the change () method is synchronized, so, although Run () is located inside the Sleep (), Peeker can still access the synchronization method you need - AD (). Here, we can see that Peeker will continue to run after starting the Sleeper2 thread. 2. Pause and restore

This example The next part introduces an overview of "suspend" or "suspend". The Thread class provides a method called SUSPEND (), temporarily stop thread; and a method called Resume () for starting recovery threads from the pause. Obviously, we can infer that resume () is called by a thread externally outside the thread. In this case, you need to use a independent class called a resume (recovery). Each class of the demonstration suspend / recovery process has a related recovery. As follows:

Page 789-790

SuspendResume1 also provides a synchronous RUN () method. Similarly, when we start this thread, you will find that Peeker that is associated with it enters the "blockage" state, waiting object lock is released, but never happens. As usual, this problem has been resolved in SuspendResume2, which does not synchronize the entire Run () method, but uses a separate synchronization Change () method.

For Java 1.2, everyone should pay attention to Suspend () and resume () have got strong opposition because suspend () contains an object lock, so it is easy to appear "dead lock" phenomenon. In other words, it is easy to see many of the locked objects waiting for each other. This will cause "solidification" of the entire application. Although they can see their traces in some old procedures, when you write your own procedure, you should avoid it. This chapter will tell the correct solution later.

3. Waiting and notice

Through the practice of the first two examples, we know that no matter how sleep () is still suspend (), it will not be locked when you are called. When you need to use an object lock, be sure to pay attention to this problem. On the other hand, the WAIT () method will unlock when called, which means that other synchronization methods can be invoked in the thread object during WAIT (). But in the two classes, we see that the run () method is "synchronous". During Wait (), Peeker still has full access to the synchronization method. This is because WAIT () is unlocked when the method hangs internal calls.

We can also see two forms of WAIT (). The first form uses a parameter in milliseconds, which has the same meaning as SLEEP (): Pause this section of the specified time. The difference is that in WAIT (), the object lock has been released, and Wait () is free to exit Wait (), because a Notify () can exercise the time passage.

The second form does not use any parameters, which means that Wait () will continue until notify () intervention. And after a period of time, it will not be aborted.

Wait () and Notify () are more particularly a place that these two methods belong to part of the base class Object, unlike Sleep (), Suspend (), and resume () belong to a part of THREAD. Although this surface is a bit strange - actually makes threaded tension becomes a part of a generic basis - but thinks about it, it will be relieved, because the object locks they operate are also part of each object. Therefore, we can place a wait () into any synchronization method, whether or not the process involving threads is prepared in that class. In fact, we can call WAIT () is in a synchronized method or code block inside. If WAIT () or Notify () is called within a non-synchronous method, although the program is still compiled, it will get an IllegalMonitorstateException (illegal monitor status violation), and how much is a bit inexplicable A message: "Current Thread Not Owner" (current thread is not owner ". Note that Sleep (), Suspend (), and resume () can be called within the unaptified method because they do not need to operate the lock. Can only To call WAIT () and notify (). Similarly, you can still compile the code that tries to use the error-locked code, but it will produce the same IllegalMonitorstateException violation. We can't use other people's object locks to fool the system. However, another object can be required to perform the corresponding operation, operate it its own lock. So a practice is to create a synchronization method to call NOTIFY () to your object, but in Notifier, we will see NOTIFY (): inside a synchronization method ():

792 page

Where Wn2 is an object of type WaitNotify2. Although it is not part of WaitNotify2, this method still obtains the lock of the WN2 object. At this time, it calls notify () to WN2 is legal and will not get IllegalMonitorStateException violation.

Page 792-793

If you have to wait for some other conditions (from thread external control), it is necessary to use Wait () in the thread. Wait () allows us to place the thread into the "sleep" state, and "actively" will wait for the conditions to change. And only when a NOTIFY () or NOTIFYALL () changes, the thread will be awakened and check if the condition has changed. Therefore, we believe that it provides a means of synchronizing between threads.

4. IO blocking

If a data stream must wait some IO activities, it will automatically enter the "blockage" state. In this section, in this example, there are two class collaborative universal readers and Writer objects (streams using Java 1.1). However, in the test model, a pipelined data stream will be set so that the two threads can secure data securely (this is the purpose of using the pipeline).

Sender places the data into Writer and "sleep" random time. However, Receiver itself does not include SLEEP (), Suspend () or wait () method. But when you do read (), if there is no data, it automatically enters the "clogging" state. As follows:

Page 793-794

These two classes also send the information into their own State field and modify the I value so that Peeker knows the thread is still running.

5. Test

Surprisingly, the main program (Applet) is very simple, this is the fact that most of the work has been placed in the Blockable framework. Probably, we have created an array made by the Blockable object. And because each object is a thread, after pressing the "START" button, they will take their own actions. There is another button and an actionPerformed () clause for aborting all Peeker objects. Since Java 1.2 "opposes" using Thread's STOP () method, it is possible to consider the use of this kind of compromise. In order to establish a connection between Sender and Receiver, we created a PiPedWriter and a PiPedReader. Note that PiPedReader in must be connected to PiPedWriterout via a builder parameter. After that, we can extract from in in Out - it seems that those things are transmitted through a "pipe". The IN and OUT objects are then passed to the Receiver and the Sender builder, respectively; the latter treat them as any type of Reader and Writer (that is, they are "tracered".).

The array of Blockable handle B is not initialized at first in the definition, because the pipelined data stream is not set before defining (the need to be the TRY block will become an obstacle):

Page 795-796

In init (), note that the loop will traverse the entire array and add a state and peeker.status text fields for the page.

After the first time you create a Blockable thread, each such thread will automatically create and launch your own Peeker. So we will see that all Peeker runs before the Blockable thread starts. This is very important because some Peeker will be blocked and stop running when the Blockable thread is started. Understand this, will help us to deepen the understanding of the concept of "blocked".

14.3.2 dead lock

Since the thread may enter the plug state, and because the object may have a "synchronous" method - unless the synchronous lock is released, the thread cannot access That object - so a thread is completely waiting for another object, and the other object is waiting An object is pushed in this class. This "waiting" chain is the most terrible situation is to enter the closed state - the last object is waiting for the first object! At this point, all threads will fall into an endless way to wait for each other, and everyone will move. We call this situation "dead lock". Although this situation is not often, once it encounters, the debugging of the program will become extremely difficult.

In terms of language itself, it has not been directly provided to prevent deadlocks, and we need to avoid them through cautious design. If anyone needs to debug a deadlock, he is available without any trick.

1. Java 1.2 pairs of stop (), suspend (), resume (), and destroy ()

In order to reduce the possibility of deadlocks, a contribution made by Java 1.2 is "opposed" using Thread (), Suspend (), resume (), and deStroy () method.

The reason for opposing using stop () is because it is not safe. It will release all locks acquired by the thread and if the object is in a non-connected state ("destroyed"), then other threads can check and modify them in that state. As a result, there was a subtle situation, and it is difficult to check the real problem. Therefore, you should try to avoid using stop (), you should use the method of blocking.java, tell the thread with a sign to trouble your execution by exiting your Run () method.

If a thread is blocked, for example, when it is waiting for input, then it is generally not like a sign as in blocking.java. But in these cases, we still should not use stop (), and should be used with the interrupt () method provided by thread to abort and exit the blockage of the plug. Page 797-798

Wait () inside Blocked.Run () will generate a clogged thread. When we press the button, the block of Blocked will be set to NULL, so that the garbage collector can clear it, then call the object's interrupt () method. If you press the button for the first time, we will see that the thread exits normally. But after the thread of "killing", it is just that the button is pressed.

Suspend () and resume () methods are naturally susceptible to dead locks. When you call suspend (), the target thread will stop, but still hold the lock before this. At this point, any other thread cannot access the locked resource unless the thread is restored to the thread. For any thread, if they want to resume the target thread, at the same time try to use any lock-locked resources, it will cause the embarrassing deadlock. So we should not use suspend () and resume (), but should be placed in your own Thread class, pointing out that the thread should act or hang. If the flag indicates that the thread should hang, use Wait () to enter the waiting state. If the flag indicates that the thread should be restored, use a Notify () to restart the thread. We can modify the front of Counter2.java to actually experience. Although the effects of the two versions are similar, everyone will notice that the organizational structure of the code has changed - for all "audiences" use anonymous internal classes, and Thread is an internal class. This makes the program's writing slightly, because it cancels some additional records in Counter2.java.

Page 799-801

Suspended flag in Suspendable is used to switch "hang" or "pause" state. To hang a thread, simply call FauxSuspend () to set the flag to TRUE (true). The detection of the logo state is in Run (). As mentioned earlier in this chapter, Wait () must be set to "Synchronized) to enable it to use the object lock. In FauxResume (), the SUSPENDED flag is set to false (fake) and calls notify () - Since this will wake Wait () in a "synchronization" clause, the fauxResume () method must also synchronize, so that it can Before calling notify (), the object lock (so, the object lock can be used by the Wait () to be called. If you are in accordance with the style displayed by this program, Wait () and Notify () can be avoided.

Thread's Destroy () method is not implemented at all; it is similar to SUSPEND () that cannot be recovered at all, so there is a deadlock as Suspend (). However, this method did not get clear "opposition", perhaps the version after Java (1.2 edition) is used for some special occasions that can withstand the danger of dead locks.

Everyone may be strange why they have to implement these methods that are now "opposed". This happens, probably because Sun mainly makes technical personnel to decide to change the language, rather than those market sales staff. Typically, technicians can understand the substance of language than engage in sales. After committing a mistake, you can also face them well. This means that Java can continue to make progress, even if this makes the Java programmer feel more inconvenient. As I personally, I would rather face these inconveniences, and I don't want to see the language stopping. 14.4 priority

Priority tells the debugging of how much is the importance of the thread. If there is a large number of threads being blocked, all waiting for operation, the debugger will first run the thread with the highest priority. However, this does not mean that the lower priority thread will not run (in other words, the deadlock is caused by the presence of priority). If the priority of the thread is low, it means that it is a chance that it is allowed to run.

You can use the getPriority () method to read a thread priority and change it with setPriority (). In the following sequence, everyone will find that the count speed is slow, because the threads they associate allocate lower priority:

802-805 page program

Ticker uses a good form in front of this chapter, but there is an additional TextField (text field) for displaying the priority of the thread; and two additional buttons for human becomes and reduced priority.

Also note yield () usage, which automatically returns control to the debug program (mechanism). If such a process is not performed, the multi-thread mechanism will still work, but we will find its running speed slow down (try to delete the call to Yield ()). Sleep () can also be called, but if it is done, the count frequency will change from the duration control of Sleep (), not the priority.

INIT () in Counter5 creates an array composed of 10 Ticker2; their buttons, and input fields (text fields) are placed in the form of the Ticker2 builder. Counter5 adds a new button to start everything, and to improve and reduce the maximum priority of the thread group. In addition, some tags are used to display a maximum and minimum priority of a thread that can be adopted; and a special text field for display the maximum priority of the thread group (in the next section, we will fully discuss the threading group The problem). Finally, the priority of the parent thread group is also displayed as a label.

When pressing the "UP" or "DOWN" button, you will first get the current priority of Ticker2, and then increase or decrease accordingly.

When running the program, we can notice a few things. First, the default priority of the thread group is 5. Even before starting the thread (or before creating threads, this requires appropriate modification to the code) to 5 or less, each thread will have a 5 default priority.

The simplest test is to get a counter that reduces its priority to 1, and it should be observed to be significantly slowed down. Now try again to improve the priority, can rise back to the priority of the line group, but can not be higher. The priority of the thread group is now reduced twice. The priority of the thread will not change, but if you try to improve or reduce it, you will find that this priority will automatically become priority of the thread group. In addition, the new thread still has a default priority, even if it is higher than the group's priority (in other words, do not expect to use group priority to prevent new threads from having higher priorities than existing).

Finally, try to increase the maximum priority of the group. It can be found that this is no effect. We can only reduce the maximum priority of the thread group, and we cannot increase it. 14.4.1 thread group

All threads are affiliated with a thread group. That can be a default thread group, or a group that is clearly specified when creating a thread. At the beginning of the creation, the thread is limited to a group and cannot be changed to a different group. There is at least one thread from the system thread group. If you create multiple threads without specifying a group, they automatically belong to the system thread group.

The thread group must also be subordinate to other thread groups. You must specify which thread group belonging to the new thread group in the builder. If you do not specify its belongings when you create a thread group, it will automatically become a subordinate of the system thread group. Therefore, all thread groups in an application will eventually use the system thread group as its own "father".

The reason why the "Thread Group" is to be proposed is difficult to find the reason from the literal. This has brought some confusion for our discussion. Generally speaking, we think that the thread group is used due to "safe" or "confidential". According to Arnold and Gosling: "Threads in the thread group can modify other threads in the group, including those deepest in the hierarchical structure. A thread cannot modify any thread located outside your own group or the subordinate group (Notes" 1). However, it is difficult to judge what the specific meaning of "modification" here is. The following example shows the priority of all threads located in a "leaf group" to modify all threads of the thread group tree, while also calling all threads within this "tree".

1: "The Java Programming Language" page 179. The book was edited by Arnold and Jams Gosling, and Addison-Wesley was published in 1996.

807-808 page program

In Main (), we created several ThreadGroup (thread groups), each in different "leafs": x No parameters, only its name (a string), so automatically enters "System" The thread group; Y is below X, and Z is located below y. Note that the initialization is based in the order sequence, so the code is legal.

There are two threads to create different thread groups. Among them, Testthread1 does not have a Run () method, but there is a f () for notifying the thread and printing something so that we know that it has been called. Testthread2 belongs to a subclass of Testthread1, and its Run () is very detailed, doing a lot. First, it gets the thread group where the current thread is located, and then use getParent () to move the two levels up in the inherited tree (this is reasonable because I want to move TestThread2 in the hierarchical structure). Subsequently, we call method ActiveCount (), query how many threads in all subclocking groups, thereby creating an array consisting of a handle to Thread. The enumerate () method will point to the handle of all of these threads into the array gall. Then travers in the entire array, call the f () method for each thread while modifying the priority. In this way, the thread in a "leaf" thread group modified the thread located in the parent thread group.

The debugging method list () Prints all the information related to a thread group and outputs them as a standard output. This is quite good when we investigate the behavior of the thread group. Below is the output of the program:

808 page program

List () not only prints the class name of ThreadGroup or Thread, but also prints the name of the thread group and its highest priority. For threads, their names are printed and connected to thread priority and the thread groups belong. Note List () shrink the threads and threads, indicating that they are the "sub" of the unmolished thread groups. Everyone can see f () is called by the Run () method of Testthread2, so it is obvious that all threads in the group are quite fragile. However, we can only access threads branching from their own System thread group, and maybe this is the so-called "safe" meaning. We cannot access other system thread trees.

1. Control of thread group

Throwing security issues do not talk, the most useful place in the thread group is control: Just use a single command to complete the operation of the entire thread group. The following example demonstrates this and explains the limitations of the priority within the thread group. Note Numbers in parentheses make a comparison output result:

809-810 page program

The following output results have been appropriately edited to use a page to be loaded (java.lang. Has been removed), and add the appropriate number, corresponding to the numbers in the brackets in the previous program list:

Page 811

All programs have at least one thread running, and main () taken is a Static (static) method called Thread, named currentthread (). From this thread, the thread group will be created, and List () will be called for the result. The output is as follows:

Page 812

We can see that the name of the main thread group is system, while the name of the main thread is Main, and it is from the System thread group.

The second practice shows that the highest priority of the SYSTEM group can be reduced, and the main thread can increase its priority:

Page 812

The third practice creates a new thread group called G1; it automatically slaves from the System thread group because it does not specify its property relationship. We placed a new thread inside G1, named A. Subsequently, we tried to set the maximum priority of this group to the highest level and set the priority of A to the highest level. The results are as follows:

Page 812

It can be seen that it is impossible to set the maximum priority of the thread group to a higher parent thread group.

The fourth exercise reduces the maximum priority of G1, then try to raise it to Thread.max_Priority. The results are as follows:

Page 812 Medium Program

It can also be seen that an attempt to improve the maximum priority is failed. We can only reduce the maximum priority of a thread group, and we cannot improve it. In addition, pay attention to the priority of thread A has not changed, and it is now higher than the maximum priority of the thread group. That is, the change in the maximum priority of the thread group does not affect the existing thread.

The fifth exercise tries to set a new line to the maximum priority. As follows:

812 page program

Therefore, the new thread cannot be changed to a higher level than the maximum thread group priority.

The default thread priority of this program is 6; if a new thread is created, it is its default priority and does not change unless the priority is specially processed. Exercise Six will reduce the maximum priority of the thread group to the default thread priority, see what happens in this case:

Page 813

Although the maximum maximum priority of the thread group is 3, but still use the default priority 6 to create a new thread. Therefore, the maximum priority of the thread group does not affect the default priority (in fact, there is no way to set the default priority of the new thread).

After changing the priority, try to reduce the level, the result is as follows:

Page 813

Therefore, it is only forced to comply with the maximum priority of the thread group only when trying to change the priority. We have made a similar trial in (8) and (9). Here, we created a new thread group called G2, which used it as a subgroup of G1, and changed its maximum priority. You can see that the priority of G2 cannot be higher than G1 anyway:

Page 813

Also note that when G2 is created, it will be automatically set to the maximum priority of the thread group of G1.

After all these experiments, the entire thread group and the thread system are printed, as shown below:

Page 813-814

Therefore, the rule of the thread group is limited, and the maximum priority of a subgroup can only be lower than or equal to its parent group's maximum priority.

The last part of this program demonstrates the method for the entire group thread. The program first traverses the entire thread tree and launches each thread that has not been started. For example, the SYSTEM group will then be suspended (pause), and finally abort (although Suspend () and STOP () are used to operate it seems interesting, but should pay attention to these methods in Java 1.2. "opposing). But while hanging the System group, also hangs the main thread, and the entire program will close. So never reach the step of letting thread abort. In fact, if you really stop the main thread, it will "throw" a ThreadDeath violation, so we usually don't do this. Since ThreadGroup is inherited from Object, which contains a wait () method, it can also call the Wait (second × 1000), and the program pauses the time of running any second. Of course, the object lock must be obtained in a synchronization block beforehand.

The ThreadGroup class also provides a suspend () and resume () method, so you can stop and start the entire thread group and all threads, and you can stop and start its subgroups, all of which are just one command (again reminding, Suspend () and resume () are "opposed" in Java 1.2).

From the surface, the thread group seems to be a bit not mind, but please note that we have little need to use them directly.

14.5 Review Runnable

Earlier in this chapter, I have suggested that you must think about it before you use a program or the main Frame as a Runnable implementation. If you use that way, you can only use one of the threads in your own program. This limits flexibility, once you need to use multiple threads that belong to that type, you will encounter unnecessary trouble.

Of course, if you must inherit from a class, and if you want to make the class, Runnable is a correct solution. The last example of this chapter analyzes this, making a RunnableCanvas class, which is used to depict different colors to yourself (canvas is the meaning of "canvas"). This application is designed to get parameter values ​​from the command line to determine how long the color grid is, and how long the color changes between color changes. By using these values, everyone can experience some interesting and possible feature of threads:

815-816 page program

ColorBoxes is a typical application (program) with a builder for setting up GUI. This builder uses a parameter of int GRID, with it to set GridLayout (grid layout), so that each Vi is a GRID unit. Subsequently, it adds an appropriate number of CBOX objects that use them to fill the grid and pass the PAUSE value for each. In Main (), we can see how to modify the default values ​​of the Pause and Grid (if you pass the command line parameters). CBOX is a place for formal work. It is inherited from Canvas and implements the runnable interface, so that each Canvas can also be a Thread. Remember when implementing runnable, there is no actually generating a Thread object, just a class with a Run () method. Therefore, we must create a Thread object clearly and passed the runnable object to the builder, then call start () (in the builder). In CBOX, the name of this thread is called T.

Please pay attention to the array colors, which enumerates (enumerates all the colors in the Color class). It is used to generate a random selection color in NewColor (). The current unit (grid) color is ccolor.

Paint () is quite simple - just set the color to ccolor, then fill the pan-CANVAS with that color.

In Run (), we see an infinite loop, which sets CColor as a random color, then call repaint () to display it. Subsequently, SLEEP () is executed, so that it "sleep" is specified by the command line.

Since this design is very flexible, and thread processing is closely combined with each CANVAS element, it can create any number of threads (but in practical applications, this is subject to the number of threads that can be dedicated to limit).

This program also provides us with an interesting evaluation benchmark because it reveals the dramatic differences between different JVM mechanisms in speed.

14.5.1 Excessive thread

Sometimes, we will find that ColorBoxes is almost stuck in a state. In my own machine, this situation happened after a 10 × 10 grid. Why is this so? Naturally, we have reason to suspect that AWT has done what it did. So there is an example of you can test the speculation, it produces less threads. The code has been reorganized so that a vector implements Runnable, and that VECTOR has accommodated a large number of colors and randomly selects some updates. Subsequently, we create a large number of these Vector objects, which is roughly on our choice of grid dimension. The result is that we get more threads with less compass. Therefore, if there is a speed acceleration, we can immediately know that because the number of threads in the precedent is too much. As follows:

817-819 page program

In ColorBoxes2, we created an array of CBoxVector and initialized it, allowing it to accommodate each CBoxVector grid. Every grid knows how long it is "sleep". Subsequently add equal amounts of CBOX2 objects for each CBoxVector, and each vector tells Go (), with it to launch your thread.

CBOX2 is similar to CBOX - you can draw yourself with a randomly selected color. But that is, CBOX2 can do all work. All processing involved in threads has been moved to CBoxVector.

CBoxVector can also have inherited thread and have a member object that is typed for Vector. The advantage of this is that the addElement () and Elementat () methods can get specific parameters and return value types, rather than only access to regular objects (their names can also become shorter). However, there is a need for less code on the design surface used here. In addition to this, it will automatically retain all other behaviors of a vector. Since Elementat () requires a large number of "closed" work, use many parentheses, so as the code main body is expanded, it is still possible to need a lot of code. As before, when we implement Runnable, there is not all the features provided with the Thread support, so you must create a new THREAD and pass yourself to its builder to formally "start" --Start () --something. Everyone can experience this in the CBoxVector builder and Go (). The Run () method simply selects a random element number in the vector and calls nextColor () to pick a new random color for that element.

When running this program, everyone will find it makes it faster, the response is more rapid (for example, when it is interrupted, it can stop faster). And as the grid size is growing, it does not often trapped in the "pause" state. Therefore, the process of thread has more new considerations: You must check yourself "too much thread" (no matter what program and running platform). If there is too much thread, you must try the use of the technique described above to "balance" the number of threads in the program. If you have a performance problem in a multi-threaded program, then there are many factors that need to be checked:

(1) Is there much enough call to Sleep, Yield () and / or WAIT ()?

(2) Is the call time of sleep () long enough?

(3) Is there much too much number of threads running?

(4) Try different platforms and JVM?

Some of the problems like this is to cause multi-threaded applications to become one of the reasons for "technical activities".

14.6 Summary

When you use multithreaded technology, and when to avoid it, this is an important topic we need to master. The main purpose of its sampish is to manage a large number of tasks. By mixing of multiple tasks, computer resources can be used more efficiently, or more convenient for users. The classic problem of resource equilibrium is how to use the CPU during the IO waiting period. As for user convenience, the most classic question is how to monitor and sensitively reactively and sensitively reactively in a long period of download.

The main disadvantages of multi-thread include:

(1) The running speed of the program will slow down when waiting for the shared resource.

(2) Additional CPU overhead for management requirements for threads.

(3) Complexity inclusive, such as using a separate thread to update the stupid idea of ​​each element in the array.

(4) Long waiting, wasteful resource competition and deadlock and other multi-threaded symptoms.

Another advantage of threads is that they replace the "severe" process scene switch (1000 instructions) with "Mild" execution switching (order of 100 instructions). Since all threads within a process share the same memory space, "Mild" scene switches only changes the execution of the program and the local variable. When the "severe" scene is switched, a process of change must be exchanged in full switched memory space.

Thread processing seems to have entered a new field that requires us to learn a new programming language - or learn at least a range of new language concepts. Since most microcomputer operating systems provide support for threads, the programming language or library has also expanded to threads. No matter what circumstances, the programming of the thread is:

(1) I'm trying to touch the mind and ask for the transformation of our traditional programming; (2) Other languages ​​seem similar to threads. So once you master the concept of threads, there will be no difficulties in other environments. Although the support for threads increases much more complexity in the Java language, please do not blame Java. After all, use threads to do a lot of beneficial things.

Multiple threads may share the same resource (such as memory in an object), which is the biggest trouble facing the thread. Multiple threads must not be tried to read and modify that resource at the same time. This requires skill to use the Synchronized keyword. It is a useful tool, but must truly master it, because if the fake is not operated, it is easy to have a deadlock.

In addition, pay attention to a very special issue when using threads. Due to the Java design, it allows us to create any number of threads as needed - at least theoretically (for example, assume that the limited element analysis of a project is created by millions of threads, which is not actually in Java. ). However, we generally control the upper limit of the number of threads you created. Because in some cases, a large number of threads will become a mess, so work will be almost paused. The critical point is not like a subject, but is 100 or less. In general, we only create a few critical threads and use them to solve a particular problem. At this time, the number of restrictions is not large. But in some of the conventional designs, this limitation really makes us feel the beam feet.

Everyone should pay attention to the thread processing is not very intuitive. Since the thread "scheduling" mechanism is used, the call to sleep () is inserted by inserting the Run (), and its own program can be run faster. This makes it very high in programming skills, especially when a longer latency seems to improve performance. Of course, this happens, is because the shorter delay may cause the "sleep" scheduling mechanism to be interrupted before the running thread is ready to enter the "sleep" state. This will force the scheduling mechanism to stop it and restart later so that it can do your own things and then go to sleep. Must think more about it, you can realize the true trouble of things.

One thing to miss this chapter is an animation example, which is the most popular application of the program. However, Java JDK support provides a set of solutions (and can play sound), you can go to java.sun.com to download. In addition, we have reason to believe that future versions of Java will provide better animation support - although the current Web has emerged with traditional non-Java, non-programmed animation. If you want to learn the working principle of Java animation, please refer to Core Java - Core Java, compiled by Cornell & Horstmann, Prentice-Hall published in 1997. If you want to understand the thread processing, please refer to "Concurrent Programming in Java - Java" compiled by Doug LEA, Addison-Wisley published in 1997; or "Java Threads - Java Thread", Oaks & Wong O'Reilly published in 1997.

14.7 practice

(1) Inheriting a class from Thread, and (overload) overrides the Run () method. In Run (), you print a message and then call SLEEP (). Duplicate three times, then return from Run (). Place a boot message in the builder and override Finalize (), print a shutdown message. Create a standalone thread class so that you call System.gc () and System.RunFinalization () in Run (), and print a message indicating that the call is successful. Create a few threads of these types, then run them, see what will happen. (2) Modify Counter2.java so that the thread becomes an internal class and does not need to be clearly saved one of the point to Counter2.

(3) Modify Sharing2.java, add a SYNCHRONIZED block to the Twocounter's Run () method, not synchronized the entire Run () method.

(4) Create two Thread subclasses, the first RUN () method is used to start startup, and capture the handle of the second Thread object and call Wait (). The second class Run () should call ModifyAll () for the first thread after a few seconds, so that the first thread can print out a message.

(5) In Counter5.java in Ticker2, delete yield () and explain the results. Replace Yield () with a sleeep (), explain the result.

(6) In Threadgroup1.java, the call to sys.suspend () will be replaced with a wait () call to the thread group, so that it is waiting for 2 seconds. To ensure that the correct result is obtained, the object lock of SYS must be obtained in a synchronization block.

(7) Modify Daemons.java to make main () have a sleep () instead of a readline (). Experiment different sleep times and see what happens.

(8) to Chapter 7 (Intermediate Part) Find the GreenhouseControls.java example, it should be constructed of three files. In Event.java, Event class is based on time monitoring. Modify this Event to make it a thread. The rest of the design is then modified to make them work with new, thread-based Event.

转载请注明原文地址:https://www.9cbs.com/read-59421.html

New Post(0)
CopyRight © 2020 All Rights Reserved
Processed: 0.039, SQL: 9