The book continues. Thread status, thread notification and wait, join, etc. This article continues to introduce the basic knowledge of concurrent programming.

sleep

When an executing Thread calls Thread’s sleep method, the calling Thread temporarily cedes execution rights for a specified period of time, does not participate in CPU scheduling, does not consume CPU, and does not release the monitor lock held by the Thread lock. When the specified time is up, the thread returns to the ready state, waits to allocate CPU resources again, and then executes again.

We sometimes see sleep(1), or even sleep(0), and it’s very strange, especially sleep(0), sleep for 0 seconds, does that make sense? Sleep (1), sleep(0) tells the operating system to immediately trigger a CPU race.

Let’s see what happens when the process that is sleeping is interrupted:

class MySleepTask implements Runnable{
    @Override
    public void run() {
        System.out.println("MyTask1");
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            System.out.println("Break");
            e.printStackTrace();
        }
        System.out.println("MyTask2"); } } public class Sleep { public static void main(String[] args) { MySleepTask mySleepTask=new MySleepTask(); Thread thread=new Thread(mySleepTask); thread.start(); thread.interrupt(); }}Copy the code

Running results:

Java MyTask1 interrupt. Lang. InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at java.lang.Thread.sleep(Thread.java:340) at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386) at com.codebear.MySleepTask.run(Sleep.java:10) at java.lang.Thread.run(Thread.java:748) MyTask2Copy the code

yield

We know that threads consume CPU resources in a timeslice mechanism. Under normal circumstances, a Thread must use up its allotted timeslice before the scheduler performs the next round of Thread scheduling. When the yield of Thread is performed, the scheduler tells the operating system “I don’t need the CPU anymore. You can now schedule the next round of threads “, but the operating system can ignore this hint, or the next round may still allocate the time slice to the same thread.

Let’s write an example to impress:

class MyYieldTask implements Runnable {
    @Override
    public void run() {
        for (int i = 10; i > 0; i--) {
            System.out.println("I am" + Thread.currentThread().getName() + "I've allocated time slices."); } } } public class MyYield { public static void main(String[] args) { Thread thread1 = new Thread(new MyYieldTask()); thread1.start(); Thread thread2 = new Thread(new MyYieldTask()); thread2.start(); }}Copy the code

Running results:

Of course, because of the nature of threads, the results may vary from run to run, but when we run it many times, we’ll find that most of the time, the two threads print fairly evenly, I run out of time slices, you run out of time slices, I run out of time slices.

When we call yield:

class MyYieldTask implements Runnable {
    @Override
    public void run() {
        for (int i = 10; i > 0; i--) {
            System.out.println("I am" + Thread.currentThread().getName() + "I've allocated time slices."); Thread.yield(); } } } public class MyYield { public static void main(String[] args) { Thread thread1 = new Thread(new MyYieldTask()); thread1.start(); Thread thread2 = new Thread(new MyYieldTask()); thread2.start(); }}Copy the code

Running results:

Of course, in normal cases, yield may never be used, but it is important to be aware of this method.

Sleep is different from yield

When a thread calls sleep, it blocks for the time specified by the current thread. During this time, the thread scheduler does not call the thread. When the specified time is over, the thread is in the ready state, waiting to allocate CPU resources. When a thread calls yield, it does not block the current thread, but simply gives up the time slice and returns to the “ready” state, waiting for CPU resources to be allocated.

A deadlock

A deadlock is a phenomenon in which multiple threads are waiting for each other during execution because they are competing for resources and cannot break the deadlock.

Four necessary conditions for deadlocks:

  • Mutually exclusive: A thread can exclusively use a resource it has acquired. That is, the resource can only be owned by one thread. If other threads want to own the resource, they have to wait until the thread that owns the resource releases the resource.
  • Request and hold: a thread has occupied a resource, but wants to occupy other resources, but the other resources have been occupied by another thread, so the current thread can only wait, but does not release its own resources.
  • Inalienable: Once a thread has acquired a resource, it cannot be possessed by other threads. It must release the resource after it has finished using it.
  • Loop wait: that is, T1 thread is waiting for resources occupied by T2 thread, T2 thread is waiting for resources occupied by T3 thread, and T3 thread is waiting for resources occupied by T1 thread.

To break the deadlock, only one of these four conditions needs to be broken, but the only two conditions that the programmer can intervene in are “request and hold” and “loop wait”. The other two conditions are characteristics of the lock, which the programmer cannot intervene in.

Smart you, must see that the so-called “deadlock” is caused by “pessimistic lock”, relative to the “deadlock”, there is a “live lock”, is caused by “optimistic lock”.

Daemon threads versus user threads

Threads in Java fall into two categories, user threads and daemon threads. When the JVM starts, the main function is called, which is the user thread, and some daemon threads, such as garbage collection threads, are also started inside the JVM. So what’s the difference between a daemon thread and a user thread? When the last user thread terminates, the JVM automatically exits, regardless of whether any daemon threads are currently running. How do you create a daemon thread?

public class Daemon {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
        });
        thread.setDaemon(true); thread.start(); }}Copy the code

Just set the thread daemon to true. To illustrate the difference between a user thread and a daemon thread:

public class Daemon {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            while (true) {}}); thread.start(); }}Copy the code

When we run it, we can find that the program never exits:

Let’s look at the daemon thread:

public class Daemon {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            while (true){}
        });
        thread.setDaemon(true); thread.start(); }}Copy the code

When we run it, we find that the program stops immediately:

Thread the interrupt

Thread interrupts are left behind because they are one of the most difficult aspects of concurrent programming fundamentals to understand, and also because they are not often used. Now let’s look at thread interrupts. Thread provides the stop method, which is used to stop the current Thread, but is marked expired and should be replaced by a Thread interrupt method.

interrupt

Interrupt the thread. While thread A is running (and not blocking), thread B can call thread A’s interrupt method to set thread A’s interrupt flag to true. Note that calling the interrupt method does not actually interrupt the thread. Thread A is still alive if the interrupt flag is set to true. If thread A is blocked, such as calling sleep, wait, or JOIN, thread A throws “InterruptedException” where these methods are called. Let’s try to prove that the interrupt method does not interrupt a running thread:

class InterruptTask implements Runnable {
    @Override
    public void run() {
        CopyOnWriteArrayList copyOnWriteArrayList = new CopyOnWriteArrayList();
        try {
            long start = System.currentTimeMillis();
            for (int i = 0; i < 150000; i++) {
                copyOnWriteArrayList.add(i);
            }
            System.out.println("It's over. The time is." + (System.currentTimeMillis() - start));
            System.out.println(Thread.currentThread().isInterrupted());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

public class InterruptTest {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(new InterruptTask());
        thread1.start();
        thread1.interrupt();
    }
}
Copy the code

Running results:

We're done. Time 7643true
Copy the code

In the child thread, we simulate a time-consuming operation by adding data to copyOnWriteArrayList through a loop. It is important to note that sleep is normally used to simulate time-consuming operations, but we cannot use sleep because calling sleep blocks the current thread, which is now running. As you can clearly see, the child thread is interrupted just after it starts, but it does not raise any exceptions and does not terminate the child thread. The child thread is still alive and well, except that it finally prints an “interrupt flag” of true.

If the interrupt method is not called, the interrupt flag is false:

class InterruptTask implements Runnable {
    @Override
    public void run() {
        CopyOnWriteArrayList copyOnWriteArrayList = new CopyOnWriteArrayList();
        try {
            long start = System.currentTimeMillis();
            for (int i = 0; i < 500; i++) {
                copyOnWriteArrayList.add(i);
            }
            System.out.println("It's over. The time is." + (System.currentTimeMillis() - start));
            System.out.println(Thread.currentThread().isInterrupted());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

public class InterruptTest {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(new InterruptTask());
        thread1.start();
    }
}
Copy the code

Running results:

We're done. Time 1false
Copy the code

Sleep, Wait, and Join methods will throw an exception if they are interrupted. This will not be demonstrated here, but it is important to note that when we catch InterruptedException, The interrupt flag is reset to false and we continue experimenting:

class InterruptTask implements Runnable {
    @Override
    public void run() {
        CopyOnWriteArrayList copyOnWriteArrayList = new CopyOnWriteArrayList();
        try {
            long start = System.currentTimeMillis();
            TimeUnit.SECONDS.sleep(3);
            System.out.println("It's over. The time is." + (System.currentTimeMillis() - start));
        } catch (Exception ex) {
            System.out.println(Thread.currentThread().isInterrupted());
            ex.printStackTrace();
        }
    }
}

public class InterruptTest {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(new InterruptTask());
        thread1.start();
        thread1.interrupt();
    }
}
Copy the code

Running results:

false
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at java.lang.Thread.sleep(Thread.java:340)
	at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
	at com.codebear.InterruptTask.run(InterruptTest.java:20)
	at java.lang.Thread.run(Thread.java:748)
Copy the code

You can clearly see that the interrupt flag has been reset to false.

One more question, if you think about it, is there a problem with this code?

Thread th = Thread.currentThread();
while(true) {
  if(th.isInterrupted()) {
    break; } try { Thread.sleep(100); }catch (InterruptedException e){ e.printStackTrace(); }}Copy the code

This question comes from geek time teacher Wang Baoling’s “Java Concurrent Programming Actual Combat”

The code is problematic because the “break flag” is reset when the exception is caught. If the thread is interrupted at exactly the time of the sleep and the “interrupt flag” is reset, then the next loop detects that the interrupt flag is false and the loop cannot exit.

isInterrupted

This method already appears above, which is to get the “interrupt flag” of the object thread.

interrupted

Gets the “interrupt flag” of the current Thread and resets the interrupt flag to false if the current Thread is interrupted. This method is static and is called directly from the Thread class.

Concurrent programming basis is over here, you can see content and considerable, although is a foundation, but each knowledge point, if you want to dig deeper, can be involved in “operating system”, so only in “operating system”, can really understand, now or just stay in the level of Java, alas.