Java concurrent programming-protective pause in synchronous mode

Protective pause in sync mode

That is Guarded Suspension, used in a thread to wait for the execution result of another thread

Point

  • There is a result that needs to be passed from one thread to another, so that they are associated with the same GuardedObject
  • If there are results from one thread to another, you can use message queues (see producer/consumer, asynchronous mode )
  • In the JDK, this mode is adopted for the realization of join and the realization of Future
  • Because we have to wait for the result of the other party, it is classified into synchronous mode
    Insert picture description here

Example 1, the main thread waits for the download result of the child thread

For example, the main thread waits for the result of the child thread download.

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.util.List;

import static cn.itcast.pattern.Downloader.*;

@Slf4j(topic = "c.TestGuardedObject")
public class TestGuardedObject {
    
    
    public static void main(String[] args) {
    
    
        GuardedObject guardedObject = new GuardedObject();
        new Thread(() -> {
    
    
            try {
    
    
                List<String> response = download();
                log.debug("download complete...");
                guardedObject.complete(response);
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }).start();

        log.debug("waiting...");
        Object response = guardedObject.get();
        log.debug("get response: [{}] lines", ((List<String>) response).size());

    }


}

class GuardedObject {
    
    

    private Object response;
    private final Object lock = new Object();

    public Object get() {
    
    
        synchronized (lock) {
    
    
            // 条件不满足则等待
            while (response == null) {
    
    
                try {
    
    
                    lock.wait();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
            return response;
        }
    }

    public void complete(Object response) {
    
    
        synchronized (lock) {
    
    
            // 条件满足,通知等待线程
            this.response = response;
            lock.notifyAll();
        }
    }
}

Example 2, Waiting with timeout

import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;
import java.util.List;

import static cn.itcast.n2.util.Sleeper.sleep;

@Slf4j(topic = "c.TestGuardedObjectV2")
public class TestGuardedObjectV2 {
    
    
    public static void main(String[] args) {
    
    
        GuardedObjectV2 v2 = new GuardedObjectV2();
        new Thread(() -> {
    
    
            sleep(1);
            v2.complete(null);
            sleep(1);
            v2.complete(Arrays.asList("a", "b", "c"));
        }).start();

        Object response = v2.get(2500);
        if (response != null) {
    
    
            log.debug("get response: [{}] lines", ((List<String>) response).size());
        } else {
    
    
            log.debug("can't get response");
        }
    }
}


/**
 * 添加超时处理
 */
@Slf4j(topic = "c.GuardedObjectV2")
class GuardedObjectV2 {
    
    

    private Object response;
    private final Object lock = new Object();

    public Object get(long millis) {
    
    
        synchronized (lock) {
    
    
            // 1) 记录最初时间
            long last = System.currentTimeMillis();
            // 2) 已经经历的时间
            long timePassed = 0;
            while (response == null) {
    
    
                // 4) 假设 millis 是 1000,结果在 400 时唤醒了,那么还有 600 要等
                long waitTime = millis - timePassed;
                log.debug("waitTime: {}", waitTime);
                if (waitTime <= 0) {
    
    
                    log.debug("break...");
                    break;
                }
                try {
    
    
                    lock.wait(waitTime);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
                // 3) 如果提前被唤醒,这时已经经历的时间假设为 400
                timePassed = System.currentTimeMillis() - last;
                log.debug("timePassed: {}, object is null {}", timePassed, response == null);
            }
            return response;
        }
    }

    public void complete(Object response) {
    
    
        synchronized (lock) {
    
    
            // 条件满足,通知等待线程
            this.response = response;
            log.debug("notify...");
            lock.notifyAll();
        }
    }
}

The principle of join in JDK

In JDK, the realization of join and the realization of Future adopt this mode

    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) {
    
    //参数是0的话,就无限等待
            while (isAlive()) {
    
    //判断线程是否存活
                wait(0);
            }
        } else {
    
    
            while (isAlive()) {
    
    //判断线程是否存活
                long delay = millis - now;
                if (delay <= 0) {
    
    
                    break;
                }
                wait(delay);
                // base不变,currentTime不断增加,now变大
                now = System.currentTimeMillis() - base;
            }
        }
    }

Protective suspension-extended

In the picture

  • Futures is like a mailbox on the first floor of a residential building (each mailbox has a room number),
  • T0, t2, and t4 on the left are like residents waiting for mail,
  • T1, t3, t5 on the right are like postman

If you need to use the GuardedObject object between multiple classes, it is not very convenient to pass as a parameter, so design an intermediate class for decoupling,
which can not only decouple [Result Waiter ] and [Result Producer], but also at the same time Support the management of multiple tasks.
Insert picture description here
achieve

Test class

import lombok.extern.slf4j.Slf4j;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;

@Slf4j(topic = "c.Test20")
public class Test20 {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        for (int i = 0; i < 3; i++) {
    
    
            new People().start();
        }
        Sleeper.sleep(1);
        for (Integer id : Mailboxes.getIds()) {
    
    
            new Postman(id, "内容" + id).start();
        }
    }
}

Residents and postmen who receive mail

@Slf4j(topic = "c.People")
class People extends Thread{
    
    
    @Override
    public void run() {
    
    
        // 收信
        GuardedObject guardedObject = Mailboxes.createGuardedObject();
        log.debug("开始收信 id:{}", guardedObject.getId());
        Object mail = guardedObject.get(5000);
        log.debug("收到信 id:{}, 内容:{}", guardedObject.getId(), mail);
    }
}

@Slf4j(topic = "c.Postman")
class Postman extends Thread {
    
    
    private int id;
    private String mail;

    public Postman(int id, String mail) {
    
    
        this.id = id;
        this.mail = mail;
    }

    @Override
    public void run() {
    
    
        GuardedObject guardedObject = Mailboxes.getGuardedObject(id);
        log.debug("送信 id:{}, 内容:{}", id, mail);
        guardedObject.complete(mail);
    }
}

mailbox
Insert picture description here

class Mailboxes {
    
    
    private static Map<Integer, GuardedObject> boxes = new Hashtable<>();

    private static int id = 1;
    // 产生唯一 id
    private static synchronized int generateId() {
    
    
        return id++;
    }

    public static GuardedObject getGuardedObject(int id) {
    
    
        return boxes.remove(id);
    }

    public static GuardedObject createGuardedObject() {
    
    
        GuardedObject go = new GuardedObject(generateId());
        boxes.put(go.getId(), go);
        return go;
    }

    public static Set<Integer> getIds() {
    
    
        return boxes.keySet();
    }
}

GuardedObject object

// 增加超时效果
class GuardedObject {
    
    

    // 标识 Guarded Object
    private int id;

    public GuardedObject(int id) {
    
    
        this.id = id;
    }

    public int getId() {
    
    
        return id;
    }

    // 结果
    private Object response;

    // 获取结果
    // timeout 表示要等待多久 2000
    public Object get(long timeout) {
    
    
        synchronized (this) {
    
    
            // 开始时间 15:00:00
            long begin = System.currentTimeMillis();
            // 经历的时间
            long passedTime = 0;
            while (response == null) {
    
    
                // 这一轮循环应该等待的时间
                long waitTime = timeout - passedTime;//---这个参数防止虚假唤醒
                // 经历的时间超过了最大等待时间时,退出循环
                if (timeout - passedTime <= 0) {
    
    
                    break;
                }
                try {
    
    
                    this.wait(waitTime); // 虚假唤醒 15:00:01
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
                // 求得经历时间
                passedTime = System.currentTimeMillis() - begin; // 15:00:02  1s
            }
            return response;
        }
    }

    // 产生结果
    public void complete(Object response) {
    
    
        synchronized (this) {
    
    
            // 给结果成员变量赋值
            this.response = response;
            this.notifyAll();
        }
    }
}

reference

"Java Multithreaded Design Pattern"

Guess you like

Origin blog.csdn.net/e891377/article/details/109152523