The theoretical knowledge

The theoretical knowledge comes from Java documentation

  • Try to read the comments in the JDK source code for yourself
  • Translation is equivalent to worn shoes, smelly or fragrant?

digression

  • Concurrency cannot be separated from the support of hardware, first of all, the CPU has to have more cores
  • Threads are managed by the operating system
  • Thread and Runable are created and used by all of us
  • To buckle details

Thread priority is related

  • Each thread has a priority
  • One thread creates another, and the created thread defaults to the same priority as the creator thread
  • Daemon threads can be created first and only if a thread is a daemon
  • Even if you have a higher priority, the operating system doesn’t necessarily call you first

Precautions for Thread class

  • Each thread is identified by a name. More than one thread may have the same name. If you do not specify a name when you create a thread, a new name is generated for it.

  • Unless otherwise specified, passing a NULL argument to a constructor or method in this class will result in a NullPointerException being raised

  • When a Java virtual machine starts, there is usually only one non-daemon thread (usually calling a method named main of some specified class). The Java virtual machine continues to execute threads until either of the following occurs:
    • The Exit method of the Runtime class is called, and the security manager allows the exit operation.
    • All threads of the nonadherent thread are dead, either through a return statement from the run method or by throwing an exception that propagates outside the run method.

  • Thread cannot be started repeatedly, even if it is finished
  • The thread calling the start method and the thread executing the run method are executed simultaneously

  • If the thread is constructed using a separate Runnable object, the Runnable object’s run method is called; Otherwise, the run method in Thread is called. Otherwise, this method does nothing and returns.

wait()methods

  • The wait method is in the Object class
  • Causes the current thread to wait untilAnother threadCall this objectnotify()Method ornotifyAll()Methods. In other words, this method behaves the same as just doing the callwait(0)The behavior is exactly the same.

wait(long timeout)methods

  • Causes the current thread to wait until another thread calls this objectnotify()Method ornotifyAll()Method, or after a specified time.
  • The current thread must have the object’s Monitor.
  • This method causes the current thread (called T) to place itself in the wait set for the object and then abandon any and all synchronization declarations for the object. For thread scheduling purposes, thread T is disabled and dormant until one of the following four conditions occurs:
    • Some other threads call this objectnotify()Method, and thread T is just arbitrarily selected as the thread to wake up.
    • Some other threads call this objectnotifyAll()Methods.
    • Some other thread interrupts thread T.
    • The specified timeout has more or less passed. However, if timeout is 0, the specified timeout is ignored and the thread simply waits until notified.
  • After that, thread T is removed from the object’s wait set and thread scheduling is enabled again. It then competes with other threads for the right to synchronize on the object in the usual way. Once it gains control of the object, all its synchronization declarations on the object are reverted to what they were when the WAIT method was called. Thread T then returns from calling wait. Therefore, when the object and thread T return from the WAIT method, their synchronization state is exactly the same as when the wait method was called.
  • A thread inThere is nobenotify(), interrupt, or the specified time has passedAnd be awakenedSo calledFalse awaken. Although this rarely happens in practice, applications must guard against it by testing conditions that should cause the thread to wake up, and continue to wait if the condition is not met. In other words, waiting should always happen in a loop, like this:
      synchronized (obj) {
               while(<condition does not hold>) obj.wait(timeout); .// Perform action appropriate to condition
           }
Copy the code
  • If the current thread is interrupted by another thread before or while waiting, InterruptedException is thrown. As mentioned above, this exception is not thrown until the locked state of the object is restored.

Check,monitorIs the implementation (lock)lockCan be approximated bymonitorUnderstanding for the lock

  • The official documentation

notify()methods

  • Wakes up the object’smonitorA single thread waiting on. If multiple threads are waiting on this object, select one of themaWake up. The choice israndomAnd can be selected based on implementation. The thread waits for the object by calling one of the wait methodsmonitor.
  • The awakened thread will not be able to continue running until the current thread releases the lock on the object. The awakened thread will normally compete with other threads that want to synchronize on this object. For example, the awakened thread has no reliable advantage or disadvantage as the next thread to acquire the object lock.
  • This method can only be used by the objectmonitorOwner of the thread call.
  • A thread becomes the owner of the monitor object in one of three ways:
    • By executing the synchronization instance method of the object.
    public class Test{
       public synchronized void t(a){....}// Synchronize the instance method
       public static void main(String[] args)  {
           Test test = new Test();
           // When thread 1 executes test.t(), it acquires the lock on the object and cannot be accessed by any other thread
           Test test2 = new Test();
           // There are two instances where test2.t() can be executed by other threads while test.t() is executed by thread 1.
           // Because there are two real column objects, there are two locks}}Copy the code
    • By executing the body of a synchronized statement on an object.
    public class Test{
      public void t(a){
           synchronized(object){ ..... block// The body of a synchronized statement}}public static void main(String[] args)  {
          Test test = new Test();
          When thread 1 executes into the synchronized block in test.t(), it acquires the lock on the object, and no other thread can access it}}Copy the code
    • For objects of type Class, perform synchronization of that Class (synchronizedStatic methods.
    public class Test{
       public synchronized void t(a){....}// Synchronize the instance method
       public static synchronized void t2(a){....}// A statically synchronized method belonging to the Class object
       public static void main(String[] args)  {
           Test test = new Test();
           // Test is the instance object. Thread 1 acquires the lock on the object when it executes test.t(). No other thread can access the object
           // However, when thread 1 executes test.t(), any other thread can execute test.t2 ().
           // There are two locks, one for instance objects and one for Class objects. Static methods belong to classes}}Copy the code
  • Only one thread can own an object at a timemonitor.

In a word

1.waitandnotifyTo use on the same object, callwaitMethod to add an object to a wait set

2,objectthemonitorIs the key. callwaitMethod will be released latermonitorThen enter the waiting state

3,objectOf the classwaitMethods andThreadOf the classsleepMethod difference, the latter will not be releasedmonitor

4. In callwaitMethod, the thread must hold the property of the called objectmonitorWhen the callwaitMethod, the thread releases the lock on the object (monitor). In the callThreadOf the classsleepMethod, the thread does not release the lock on the object (monitor)

5, the thread isnotifyAfter waking up the lock will be fair to other threads competing with the common object

6. At some point, only one thread owns the lock on an object

7,notifyIs a random call from a wait set,notifyAllIs to wake up all of them, wake up will be fair with other threads of common contention object lock

Static: Class has its own lock

How do I get monitor for an object

With synchronized

  • When the object does not obtain monitor

  • Let the object get the Monitor
public class WaitTest {
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();
        synchronized (object){// Use synchronized to acquire monitorobject.wait(); }}}Copy the code
  • throughjavap -cDecompilation verifies that the object is obtainedmonitor

More talk, more code

Please listen to

Write a multithreaded program to achieve such a goal: 1. There is an object that has a member variable count of type int, whose initial value is 0. 2. Create two threads, one of which increments the object’s member variable counter by one and the other increments the object’s member variable by one. 3. Prints the value of the object’s member variable counter after each change. 4. The final output should be 1010101010101. . .

code

  • Try it with two threads
public class WaitTest {
    int count;
    public WaitTest(a) {
        count = 0;// initialize count =0;
    }
    public static void main(String[] args) {
        WaitTest test = new WaitTest();
        Runnable inCrease = () -> {/ / Lambda expressions
            for (int i = 0; i <100000 ; i++) {
                synchronized (test) {// let the thread acquire the object lock
                    try {//Thread.sleep((long) (Math.random() * 100));
                        if(test.count ! =0) {If count is not equal to 0, count is equal to 1
                            test.wait();// Add a wait set, no further statements will be executed, release the lock}}catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    test.count++;// If not added to the wait set, increment 1
                    System.out.print(test.count);/ / output
                    test.notify();// When the increment is complete, any thread in the waiting set will be notified to wake it up}}}; Runnable deCrease =() -> {/ / Lambda expressions
            for (int i = 0; i < 100000; i++) {
                synchronized (test) {// let the thread acquire the object lock
                    try {
                        //Thread.sleep((long) (Math.random() * 100));
                        if (test.count == 0) {// If count is 0, we don't need to subtract 1
                            test.wait();// Add a wait set, no further statements will be executed, release the lock}}catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    test.count--;// If it is not added to the wait set, it is reduced by 1
                    System.out.print(test.count);
                    test.notify();// Any thread in the waiting set will be notified to wake it up}}};new Thread(inCrease).start();
        newThread(deCrease).start(); }}Copy the code
  • Results the correct

  • Four threads try it

Why is 4 threads wrong?

  • Let’s add some moreprint
public class WaitTest {
    int count;

    public WaitTest(a) {
        count = 0;
    }
    public static void main(String[] args) {
        WaitTest test = new WaitTest();
        Runnable inCrease = () -> {
            for (int i = 0; i <100 ; i++) {
                synchronized (test) {// The thread acquires the object lock
                    Thread t = Thread.currentThread();
                    try {
                        //Thread.sleep((long) (Math.random() * 100));
                        if(test.count ! =0) {
                            System.out.println(t.getName()+"---wait.......");
                            test.wait();// Join the wait set, the following statement will not be executed, release the lock
                            Thread.sleep((long) (Math.random() * 100));
                            System.out.println("In"+t.getName()+"Wait after if"); }}catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("Before count++");
                    test.count++;
                    System.out.println(t.getName()+"-"+t.getState()+""+test.count);
                    //System.out.print(test.count);test.notify(); }}}; Runnable deCrease =() -> {for (int i = 0; i < 100; i++) {
                synchronized (test) {
                    Thread t = Thread.currentThread();
                    try {

                        if (test.count == 0) {
                            System.out.println(t.getName()+"---wait.......");
                            test.wait();
                            Thread.sleep((long) (Math.random() * 100));
                            System.out.println("In"+t.getName()+"Wait after if"); }}catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("Before the count -");
                    test.count--;
                    System.out.println(t.getName()+"-"+t.getState()+""+test.count);
                    //System.out.print(test.count);test.notify(); }}};// If you increase the for loop, it will be deadlocked
        new Thread(inCrease,"Thread-0").start();
        new Thread(inCrease,"Thread-1").start();
        new Thread(deCrease,"Thread-2").start();
        new Thread(deCrease,"Thread-3").start(); }}Copy the code

How to improve

  • Key point: when a thread is awakened, it does not use the if statement for judgment. When the thread is woken up, the problem is solved simply by making a judgment on count
  • willwait()In thewhileJust loop, about toifThe forwhile
public class WaitTest {
    int count;

    public WaitTest(a) {
        count = 0;
    }
    public static void main(String[] args) {
        WaitTest test = new WaitTest();
        Runnable inCrease = () -> {
            synchronized (test) {// The thread acquires the object lock
                for (int i = 0; i < 10000; i++) {
                    while(test.count ! =0) {
                        try {
                      // When a wait thread is awakened, a notifyAll() is required to prevent all threads from waiting.
                     // If other threads are waiting and the while condition is true, the thread will wait, causing all threads to wait,
                            test.notifyAll();
                            test.wait();
                            //Thread.sleep((long) (Math.random() * 100));
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    test.count++;
                    System.out.print(test.count);
                    test.notify();// Wake up other threads after execution}}}; Runnable deCrease = () -> {synchronized (test) {
                for (int i = 0; i < 10000; i++) {
                    while (test.count == 0) {
                        try {
                     // When a wait thread is awakened, a notifyAll() is required to prevent all threads from waiting.
                    // If other threads are waiting and the while condition is true, the thread will wait, causing all threads to wait,
                            test.notifyAll();
                            test.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    test.count--;
                    System.out.print(test.count);
                    test.notify();// Wake up other threads after execution}}};new Thread(inCrease, "Thread-0").start();
        new Thread(inCrease, "Thread-1").start();
        new Thread(deCrease, "Thread-2").start();
        new Thread(deCrease, "Thread-3").start(); }}Copy the code

conclusion

1, will bewaitWhen the thread wakes up, it will executewaitThe following statement, the preceding statement is not executed

A. wait B. wait C. wait D. waitwaitThe statement should be judged again, generallywhileVery pretty good

3, withwhileTo prevent all threads at all timeswait