Java – handling interruptedexception (error in Android?) while waiting for an exit signal

I've seen the following code, and I want to know if it's really what I think:

synchronized(sObject) {
    mShouldExit = true;   
    sObject.notifyAll()    
    while (!mExited) {
      try {
           sObject.wait();
        } catch (InterruptedException ex) {
           Thread.currentThread().interrupt();
        }
     }
}

About context: another thread checks mshouldexit (in the sobject monitor) and exits in this case

This does not seem to me to be the right model If an interrupt occurs, it will set the interrupt state again, so when it returns to sobject When waiting (), another interruptedexception will appear, and so on Therefore, it will never go to the real wait state (sobject. Wait()), that is, it will never release the sobject monitor This may cause an infinite loop because another thread cannot set mexiting to true because it will never enter the monitor of sobject (so I think the interrupt () call is an error and it can't be used here.) Did I miss anything?

Please note that the code snippet is part of the official Android framework source code

Update: in fact, the situation is worse because the same mode is used in Android at the beginning of GL rendering GLSurfaceView. GLThread. Official source code of surfacecreated():

public void surfaceCreated() {
        synchronized(sGLThreadManager) {
            if (LOG_THREADS) {
                Log.i("GLThread","surfaceCreated tid=" + getId());
            }
            mHasSurface = true;
            sGLThreadManager.notifyAll();
            while((mWaitingForSurface) && (!mExited)) {
                try {
                    sGLThreadManager.wait();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }

You can reproduce the error in a similar way: make sure your UI thread is already in the interrupt state flag, then add your glsurfaceview and start GL rendering (through setrenderer (...), but on some devices, make sure your glsurfaceview has visibility Visible state, otherwise rendering will not start)

If you follow the above steps, your UI thread will eventually lead to an infinite loop, because the above code will continue to generate interruptedexception (due to wait()), so the GL thread will never be able to set mwaitingforsurface to false

According to my test, it seems that such an infinite loop will also lead to GC_ Current is an unordered sequence of garbage collection (or at least such messages in logcat) Interestingly, some people have an unknown and ambiguous problem in the early stackoverflow, which may be related to it: how to solve GC_ concurrent freed?

Maybe the interrupt flag of his UI thread is set to true. Is it possible for him to use glsurfaceview to refer to his map? It's just a hypothesis, a possible situation

Solution

Short version: this code is wrong and will cause an infinite loop (I still have questions, but may depend on the JVM Implementation) Setting the interrupt state is the right thing to do, but it should exit the loop and eventually use thread Isinterrupted() checks the same interrupt status

Long version for casual readers:

The problem is how to block the thread performing some work in response to the user's cancel button or due to some other application logic

Initially, Java supported a "stop" method, which stopped a thread in advance This method has proved to be unsafe because there is no (simple) way to clean up and free resources for stopped threads, avoid exposing partially modified objects, and so on

Therefore, Java evolved into a "cooperative" thread "interrupt" system The system is very simple: a thread is running, someone calls "interrupt" on it, a flag is set on the thread, and its thread is responsible for checking whether it is interrupted and executing accordingly

So, the correct thread The method implementation of run (or runnable.run, callable, etc.) should be as follows:

public void run() {
  while (!Thread.getCurrentThread().isInterrupted()) {
    // Do your work here
    // Eventually check isInterrupted again before long running computations
  }
  // clean up and return
}

As long as all the code your thread is executing is in your run method, this is good. You will never call things that are blocked for a long time, which is usually not the case, because if you generate a thread, it is because you have a lot to do

The simplest way to block is thread Sleep (millis), which is actually its only way: it blocks threads at a given time

Now, if your thread is in thread Sleep (600 million) internal interrupt arrival, without any other support, it will need many points to check isinterrupted

Even if your thread never quits For example, if your thread is calculating something and sending the result to a BlockingQueue with a limited size, you call queue Put (myresult), which will block until the consumer releases some space in the queue. If the consumer has been interrupted (or dead or any) at the same time, the space will never arrive, and the method will not return to check Isinterrupted will never execute and your thread is stuck

To avoid this, all (most) methods that interrupt the thread (should) throw interruptedexception The exception just tells you "I'm waiting for this, but the thread is interrupted at the same time. You should clean up and exit as soon as possible."

As with all exceptions, unless you know what to do, you should re throw and want to know on someone in the call stack

Interruptedexceptions are worse because when they are thrown, "interrupt status" is cleared This means that simply catching and ignoring them will result in threads that do not normally stop:

public void run() {
  while (!Thread.getCurrentThread().isInterrupted()) {
    try {
      Thread.sleep(1000);
    } catch (InterruptedException e) {
      // Nothing here
    }
  }
}

In this example, if an interrupt arrives in the sleep () method (this is 99.9999999999%), it will throw an interruptedexception, clear the interrupt flag, and then the loop will continue. Because the interrupt flag is false, the thread will not stop

That's why if you implement "while" correctly, use Isinterrupted, and you really need to capture interruptedexception, and you don't have any special things (such as cleaning, returning, etc.), at least you can set the interrupt flag again

The problem with the code you publish is that "while" only depends on mexited to decide when to stop, not isinterrupted

while (!mExited && !Thread.getCurrentThread().isInterrupted()) {

Or you can exit when interrupted:

} catch (InterruptedException e) {
  Thread.currentThread().interrupt();
  return; // supposing there is no cleanup or other stuff to be done
}

If you do not control threads, it is also important to set the isinterrupted flag to true For example, if you are in a runnable executed in a thread pool, or anywhere in any way, you do not own and control the thread (simply a servlet), you do not know whether the interrupt is for "you" (in the case of a servlet, the client closes the connection and the container attempts to prevent you from releasing the thread for other requests), Or if its goal is that the entire thread (or system) container is closing, stop everything

In this case (this is 99% of the code), if the interruptedexception cannot be thrown again (unfortunately checked), the only way to propagate the stack to the thread pool where the thread is interrupted is to set the return true flag before return

In this way, it will propagate to the stack and eventually generate more interruptedexceptions until the thread owner (whether the JVM itself, the executor or any other thread pool) can respond normally (reuse the thread and let it die, system. Exit (1)...)

Most of the content is introduced in Chapter 7 of Java concurrency practice. This is a very good book. I recommend it to any user interested in computer programming (not just Java), because these problems and solutions are similar in many other environments, and the explanation is well written

Why did sun decide to check the interruptedexception? When most documents suggest to ruthlessly override it, why did they decide to clear the interrupt flag when throwing an exception? When the correct way is to set it to true, it will remain open for debate most of the time

But if Wait releases the lock. Before checking the interrupt flag, it will open a small door from another thread to modify the mexited Boolean Unfortunately, the wait () method is native, so you should check the source of that particular JVM This will not change the fact that the code you publish is poorly coded

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>