A brief introduction

The following is a brief introduction to joins, and the code for related examples is not referenced much.

In most cases, the main thread creates and starts the child thread, and if the child thread does a lot of time-consuming computation, the main thread usually ends before the child thread. If the main thread wants to wait for the child thread to finish, it uses join. Join waits for the thread object to be destroyed.

When the current thread uses the JOIN method, if the thread is interrupted, the current thread will be abnormal

Join uses wait internally, while synchronized uses the object monitor principle to perform synchronization.

Join vs. sleep

Thread.sleep(long) does not release the lock, but it does release CPU resources

The join method releases the lock

Second, join code running ahead of time

public class TestThread {
    public static void main(String[] args) {
        try {
            ThreadB b = new ThreadB();
            ThreadA a = new ThreadA(b);
            a.start();
            b.start();
            b.join(2000);
            System.out.println("main end " + System.currentTimeMillis());
        } catch(InterruptedException e) { e.printStackTrace(); }}}class RunFirst {
    public static void main(String[] args) {
        ThreadB b = new ThreadB();
        ThreadA a = new ThreadA(b);
        a.start();
        b.start();
        System.out.println("main end = "+ System.currentTimeMillis()); }}class ThreadA extends Thread {
    private ThreadB b;

    public ThreadA(ThreadB b) {
        this.b = b;
    }

    @Override
    public void run(a) {
        super.run();
        try {
            synchronized (b) {
                System.out.println("begin A thread name = " + Thread.currentThread().getName() + "" + System.currentTimeMillis());
                Thread.sleep(5000);
                System.out.println(" end A thread name = " + Thread.currentThread().getName() + ""+ System.currentTimeMillis()); }}catch(InterruptedException e) { e.printStackTrace(); }}}class ThreadB extends Thread {
    @Override
    synchronized public void run(a) {
        super.run();
        try {
            System.out.println("begin B thread name = " + Thread.currentThread().getName() + "" + System.currentTimeMillis());
            Thread.sleep(5000);
            System.out.println(" end B thread name = " + Thread.currentThread().getName() + "" + System.currentTimeMillis());

        } catch(InterruptedException e) { e.printStackTrace(); }}}Copy the code

After analyzing the code, I assumed that when running the main method of TestThread, the console would print the main method in the order of executing thread A’s run method, then B method, and finally the main method.

And the result of multiple runs is zero

begin A thread name = Thread-1 1587651053517
  end A thread name = Thread-1 1587651058517
main end 1587651058517
begin B thread name = Thread-0 1587651058517
  end B thread name = Thread-0 1587651063517
Copy the code

The main method of RunFirst is then executed multiple times, resulting in

main end = 1587651105291 begin A thread name = Thread-1 1587651105291 end A thread name = Thread-1 1587651110291 begin B  thread name = Thread-0 1587651110291 end B thread name = Thread-0 1587651115292Copy the code

Print results in the main method are usually executed first. The book concludes that the method join mostly runs first, grabbing the lock from ThreadB and quickly releasing it. (Doesn’t make much sense, but it seems to be the case??)

Now look at the source code of join.

public final synchronized void join(long millis)
throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;

    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (millis == 0) {
        while (isAlive()) {
            wait(0); }}else {
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break; } wait(delay); now = System.currentTimeMillis() - base; }}}Copy the code

Second analysis of the main method of TestThread based on books:

1. B. Join grabs the lock of B. At this point, while (isAlive) in the join determines whether thread B is allowed. The result of isAlive is true, and wait (delay) is executed. Release lock B at this point.

2. ThreadA grabs the lock, prints it, and releases the LOCK again.

3. The JOIN and threadB vie for the lock, and the join again vie for the lock

now = System.currentTimeMillis() - base;

long delay = millis – now; if (delay <= 0) { break; } stop the loop because delay<=0.

4. ThreadB grabs the lock and finishes printing.

So how do I get the result to follow my first thought?

Based on the source code, t1 is assumed to be the time of the first run to join, then T1 =base, t2 is assumed to be the time of the second run to join, then

t2=now(now = System.currentTimeMillis() – t1;) .

To execute the contents of ThreadB first, you need delay to be greater than 0. So the dealy = miles – now = miles – System. CurrentTimeMillis () + t1

You can change the value of System.currentTimemillis () by changing the time ThreadA runs, or you can increase the value of Miles.

Then change the ThreadA Run method to sleep 1000, and the result is as expected.

The above is my personal understanding, if there is any mistake, or improper description, I hope you can point out.

Reference: Java Multithreaded Programming Core Techniques