多线程设计模式笔记

知识点

一、wait-notify 正确使用姿势

  • 两个方法必须在 synchronized 代码块中调用
  • 正确使用方式:
  thread1:  
        synchronized (lock){
            while (条件不成立){
                lock.wait();
            }
            //条件成立执行代码
        }
        
  thread2:
      synchronized (lock){
           lock.notifyAll();
        }

二、ReentrantLock正确使用姿势

reentrantLock.lock();
        try {
            //业务代码
        }finally {
            reentrantLock.unlock();
        }

三 、synchronized -> ReentrantLock 代码转换

转换过程:

1、synchronized 锁对象继承 ReentrantLock
2、synchronized 代码替换为 ReentrantLock 代码
3、wait-notify 替换为 await-signal

synchronized :

package LockSync;

import lombok.extern.slf4j.Slf4j;

public class Test {

    public static void main(String[] args) {
        Message message = new Message();
        new Thread(() -> {
            message.getName();
        }, "t1").start();

        new Thread(() -> {
            message.setName("张三");
        }, "t2").start();

    }

}

@Slf4j
class Message {

    MyLock lock = new MyLock();
    String name;

    //获取姓名
    String getName() {
        synchronized (lock) {
            while (name == null) {
                try {
                    log.info("获取姓名阻塞");
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.info("获取姓名成功:{}",name);
            return name;
        }

    }

    //设置姓名
    void setName(String name) {
        synchronized (lock) {
            log.info("设置姓名:{}",name);
            this.name = name;
            lock.notifyAll();
        }
    }
}

class MyLock {
}

ReentrantLock:

package LockSync;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class Test {

    public static void main(String[] args) {
        Message message = new Message();
        new Thread(() -> {
            message.getName();
        }, "t1").start();

        new Thread(() -> {
            message.setName("张三");
        }, "t2").start();

    }

}

@Slf4j
class Message {

    MyLock lock = new MyLock();
    Condition condition = lock.newCondition();
    String name;

    //获取姓名
    String getName() {

        lock.lock();
        try {
            while (name == null) {
                try {
                    log.info("获取姓名阻塞");
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.info("获取姓名成功:{}", name);
            return name;
        } finally {
            lock.unlock();
        }
    }

    //设置姓名
    void setName(String name) {
        lock.lock();
        try {
            log.info("设置姓名:{}", name);
            this.name = name;
            condition.signalAll();
        } finally {
           lock.unlock();
        }
    }
}

class MyLock extends ReentrantLock {
}


四、证明 synchronized 可见性

代码

package Thread7;

public class Test {

    public static void main(String[] args) {

        Message message = new Message();
        new Thread(() -> {
            message.start();
        }, "t1").start();

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            message.setFlag();
        }, "t2").start();


    }
}

class Message {
    private Boolean flag = false;

    private Object lock = new Object();

    public void start() {
        while (!flag) {
            synchronized (lock) {

            }
        }
    }

    public void setFlag() {
        flag = true;
    }

}

结论

synchronized 锁 this、class对象、某一个变量,都会导致该类所有共享变量可见,每次获取锁都重新从主内存读取最新的共享变量。

六、synchronized 有序性

synchronized的有序性实际上是指线程间操作synchronized块的有序,对于synchronized块中的代码,其并不能保证不会发生指令重排现象。(DCL问题)

五、volatile知识点

1、读写屏障

  • 写屏障:对volatile变量写指令后加入写屏障。
  • 读屏障:对volatile变量读指令前加入读屏障。

2、可见性

  • 写屏障保证在该屏障之前,对共享变量(包括非volatile变量)的改动都同步到主存。
  • 读屏障保证在该屏障之后,对共享变量(包括非volatile变量)的读取,加载的都是主存最新数据。

3、有序性

  • 写屏障保证指令重排,保证写屏障之前的代码(字节码指令)不会排到写屏障之后。
  • 读屏障保证指令重排,保证读屏障之后的代码(字节码指令)不会排到读屏障之前。
  • 注意:有序性保证指令重排序,创建对象的指令包括:1、分配内存空间 2、初始化对象 3、将内存空间的地址赋值给对应的引用 (DCL问题)

4、总结

volatile变量在所有变量中尽量最后写、最先读。

七、CAS

特征:

无锁并发,没有阻塞,不会引起上下文切换,不会早成CPU用户态和内核态之间的转换。

注意:

CAS必须借助volatile才能实现交换。

使用:

一般借助while(true)循环重试

八、不可变类

思路:

可变类 -> 线程安全问题 -> 不可变类 -> 使用保护性拷贝 -> 拷贝对象太多 -> 享元模式

应用:

Boolean、Integer、Byte、Short、Long、Character
String
BigDecimal、BigInteger

注意:

单个方法是线程安全的,多个方法之间的组合不是线程安全的。

九、线程池笔记

1、线程池里有两个集合,一个是线程集合HashSet,另一个是阻塞队列BlockingQueue
2、阻塞队列使用 ReentrantLock 锁,使用两个Condition 分别存储队列空和队列满时的等待线程
3、线程池执行任务时,若线程数低于核心线程数,创建Worker对象,传入任务,调用start方法,并将对象存入works组合;否则存入阻塞队列。
4、Worker继承Thread后重写run方法,采用 while (task != null || (task = getTask()) != null) 循环调用传入的或阻塞队列里的任务。
5、allowCoreThreadTimeOut == false,表示保存核心线程数不被销毁,实现方式:
getTask()方法中当前线程数小于等于核心线程数时,调用workQueue.take()方法,该方法会调用 ReentrantLock.await() 进行阻塞,所以 getTask() 不会返回null,当前Worker不会销毁。

在这里插入图片描述

实现方式

package thread8;

import lombok.extern.slf4j.Slf4j;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 自定义线程池
 */
@Slf4j
public class ThreadPoolTest {


    public static void main(String[] args) {

        ThreadPool pool = new ThreadPool(2, 4, 6);

        for (int i = 0; i < 20; i++) {
            int finalI = i;
            pool.excute(() -> {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.info("任务" + finalI);
            });
        }

    }

}


/**
 * 线程池
 */
@Slf4j
class ThreadPool {

    //工作线程集合
    private HashSet<Worker> workers;

    //阻塞队列
    private TaskPool taskPool;

    //核心线程数
    private int coreSize;

    //最大线程数
    private int maxCoreSize;

    private int capcity;

    ThreadPool(int coreSize, int maxCoreSize, int capcity) {
        this.coreSize = coreSize;
        this.maxCoreSize = maxCoreSize;
        this.workers = new HashSet<>();
        this.capcity = capcity;
        this.taskPool = new TaskPool(capcity);
    }

    //执行线程
    public void excute(Runnable runable) {
        //线程集合未满
        synchronized (workers) {
            if (workers.size() < coreSize || (taskPool.getSize() == capcity && workers.size() < maxCoreSize)) {
                Worker worker = new Worker(runable);
                worker.start();
                log.info("创建线程");
                workers.add(worker);
            } else {
                taskPool.add(runable);
            }
        }

    }



    /**
     * 工作线程
     */
    class Worker extends Thread {
        private Runnable runnable;

        Worker(Runnable runnable) {
            this.runnable = runnable;
        }

        @Override
        public void run() {
            //执行传入进来的Runable和阻塞队列中的Runable
            while (runnable != null || (runnable = taskPool.get()) != null) {
                try {
                    runnable.run();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    runnable = null;
                }
            }
            //任务执行完之后删除线程(可以根据核心线程池决定是否保存线程)
            workers.remove(this);
        }
    }


}

/**
 * 阻塞队列
 */
@Slf4j
class TaskPool {

    private LinkedList<Runnable> tasks = new LinkedList();

    ReentrantLock lock = new ReentrantLock();
    Condition emptyWait = lock.newCondition();
    Condition fullWait = lock.newCondition();

    private int capcity;

    TaskPool(int capcity) {
        this.capcity = capcity;
    }

    public Integer getSize() {

        int size = 0;
        size = tasks.size();
        return size;
    }

    //获取线程
    Runnable get() {

        lock.lock();
        try {
            while (tasks.isEmpty()) { //阻塞队列为空,进入阻塞等待
                log.info("阻塞队列为空");
                try {
                    emptyWait.await(); //此处可以添加拒绝策略
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //阻塞队列不为空,返回数据,唤醒阻塞队列
            Runnable runnable = tasks.removeFirst();
            log.info("出队列");
            fullWait.signalAll();

            return runnable;
        } finally {
            lock.unlock();
        }

    }

    //添加线程
    void add(Runnable runnable) {
        lock.lock();
        try {
            while (tasks.size() == capcity) { //队列已满,插入等待
                log.info("队列已满");
                fullWait.await();
            }
            //队列未满,插入数据,唤醒阻塞队列
            tasks.add(runnable);
            log.info("进队列");
            emptyWait.signalAll();
        } catch (InterruptedException e) {

        } finally {
            lock.unlock();
        }
    }

}


十、线程池大小设置

1、CPU密集型

线程数 = 核数+1

2、IO密集型

线程数 = 核数 * 期望CPU利用率 * 总时间(CPU计算时间+等待时间)/ CPU计算时间
例如:4核CPU计算时间50%,等待时间50%,期望CPU利用率 100%:
4 * 100% * 100% / 50% = 8

多线程设计模式

一、保护性暂停

  • 作用 :一个线程等待另一个线程的执行结果
  • 应用:JDK的 join、future 实现原理

1、生产消费不解耦

package thread;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class GuardedObjectTest {
    public static void main(String[] args) {
        final GuardedObject guardedObject = new GuardedObject();
        new Thread(() -> {
             log.info("等待结果");
            guardedObject.get(5000);
        }, "thread1").start();

        new Thread(() -> {
            //执行业务代码
            log.info("获取结果");
            Object response = new Object();
            guardedObject.complete(response);
        }, "thread1").start();
    }
}

class GuardedObject {
    //结果
    private Object response;

    //获取结果
    //timeout 等待时间
    public Object get(long timeout) {

        //开始等待时间
        long begin = System.currentTimeMillis();

        //已等待时间
        long passedTime = 0;
        synchronized (this) {
            while (response == null) {
                //开始等待前判断是否需要继续等待
                if(passedTime >= timeout){
                    break;
                }

                try {
                    this.wait(timeout - passedTime); //被虚假唤醒后继续等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //结束等待后计算等待时间
                passedTime = System.currentTimeMillis() - begin;
            }
            return response;
        }
    }

    //产生结果
    public void complete(Object response) {
        synchronized (this) {
            this.response = response;
            this.notifyAll();
        }
    }

}

2、生产消费解耦

第一种方式只有一个目标对象,需要生产一个消费一个。
给目标对象增加ID编码,采用享元模式缓存目标对象,解耦生产者与消费者
与生产-消费模式有区别,这里要求生产与消费一一对应。

package thread2;

import lombok.extern.slf4j.Slf4j;

import java.util.Hashtable;
import java.util.Map;
import java.util.Set;

public class Test {

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 3; i++) {
            new Consumer().start();
        }
        Thread.sleep(2000);
        for (Integer id : Future.getIds()) {
            new Producer(id, "内容" + id).start();
        }
    }
}

/**
 * 消费者
 */
@Slf4j
class Consumer extends Thread {
    @Override
    public void run() {
        GuardedObject guardedObject = Future.creatureFuture();
        log.info("希望获取记录{}", guardedObject.getId());
        Object response = guardedObject.get(5000);
        log.info("获取记录{}", response);
    }
}


/**
 * 生产者
 */
@Slf4j
class Producer extends Thread {
    private int id;
    private Object response;

    Producer(int id, Object response) {
        this.id = id;
        this.response = response;
    }

    @Override
    public void run() {
        GuardedObject guardedObject = Future.getGuardedObject(id);
        log.info("设置返回值{}", response);
        guardedObject.complete(response);
    }
}

/**
 * 工具类
 */
class Future {
    private static Map<Integer, GuardedObject> map = new Hashtable<>();

    private static int id = 0;

    //生成主键ID
    private static synchronized int generateId() {
        return id++;
    }

    //获取当前存在的所有的ID
    public static Set<Integer> getIds() {
        return map.keySet();
    }

    //创建目标对象
    public static GuardedObject creatureFuture() {
        GuardedObject myObject = new GuardedObject(generateId());
        map.put(myObject.getId(), myObject);
        return myObject;
    }

    //获取目标对象
    public static GuardedObject getGuardedObject(Integer id) {
        return map.remove(id);
    }

}


/**
 * 目标类
 */

class GuardedObject {
    //结果
    private Object response;

    //增加ID
    private Integer id;

    GuardedObject(Integer id) {
        this.id = id;
    }


    //获取结果
    //timeout 等待时间
    public Object get(long timeout) {

        //开始等待时间
        long begin = System.currentTimeMillis();

        //已等待时间
        long passedTime = 0;
        synchronized (this) {
            while (response == null) {
                //开始等待前判断是否需要继续等待
                if (passedTime >= timeout) {
                    break;
                }

                try {
                    this.wait(timeout - passedTime); //被虚假唤醒后继续等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //结束等待后计算等待时间
                passedTime = System.currentTimeMillis() - begin;
            }
            return response;
        }
    }

    //产生结果
    public void complete(Object response) {
        synchronized (this) {
            this.response = response;
            this.notifyAll();
        }
    }

    public Integer getId() {
        return id;
    }
}

返回结果:
在这里插入图片描述

二、生产消费者模式

  • 作用:平衡生产者消费者线程资源,不需要线程一一对应
  • 应用:JDK阻塞队列
  • 注意:调用wait、notifyAll方法的对象必须是 synchronized 锁的对象

1、标准实现

package thread3;

import lombok.extern.slf4j.Slf4j;

import java.util.LinkedList;

public class Test {
    public static void main(String[] args) {
        MessageQueue queue = new MessageQueue(2);
        new Thread(()->{
            while (true){
                try {
                    Thread.sleep(1000);
                    queue.getMessage();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"消费者").start();

        for (int i = 0;i< 3;i++){
            int finalI = i;
            new Thread(()->{
                queue.putMessage(new Message(finalI, "消息体"+finalI));
            },"生产者"+finalI).start();
        }


    }
}

/**
 * 消息队列
 */
@Slf4j
class MessageQueue {
    private static LinkedList<Message> list = new LinkedList();
    private static Integer capcity;


    MessageQueue(Integer capcity) {
        this.capcity = capcity;
    }

    /**
     * 获取消息
     */

    public  Message getMessage() {
        synchronized (list) {
            while (list.isEmpty()) {
                try {
                    log.info("队列为空");
                    list.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            Message message = list.removeFirst();
            log.info("获取消息{}",message);
            list.notifyAll();
            return message;
        }
    }

    /**
     * 插入消息
     */

    public void putMessage(Message message){
        synchronized (list){
            while (list.size() >= capcity){
                try {
                    log.info("队列已满");
                    list.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.info("插入消息{}",message);
            list.add(message);
            list.notifyAll();
        }
    }
}

/**
 * 消息组件
 */
final class Message {
    private Integer id;
    private String name;

    Message(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        return "id:"+id+",name:"+name;
    }
}

三、固定运行顺序

1、wait、notify方式

package Thread4;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class Test {

    private static final Object lock = new Object();
    private static Boolean flsg = false;

    public static void main(String[] args) {

        new Thread(() -> {
            synchronized (lock) {
                while (!flsg) {
                    try {
                        log.info("t2未执行,等待");
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.info("t1执行");
            }
        }, "t1").start();

        new Thread(() -> {
            synchronized (lock) {
                log.info("t2执行");
                flsg = true;
                lock.notify();
            }
        }, "t2").start();
    }
}

2、park、unpark方式:

package Thread4;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.LockSupport;

@Slf4j
public class Test2 {

    public static void main(String[] args) {

        Thread thread = new Thread(() -> {
            LockSupport.park();
            log.info("t1执行");
        }, "t1");
        thread.start();

        new Thread(() -> {
            log.info("t2执行");
            LockSupport.unpark(thread);
        }, "t2").start();
    }
}

四、交替输出

1、wait-notify

package Thread5;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class Test {

    private static Integer flag = Integer.valueOf(1);
    private static final Object LOCK = new Object();

    public static void main(String[] args) {

        Message message = new Message(5);

        new Thread(() -> {
            message.print(1,2,"a");
        }, "t1").start();


        new Thread(() -> {
            message.print(2,3,"b");
        }, "t2").start();

        new Thread(() -> {
            message.print(3,1,"c");
        }, "t3").start();
    }
}


@Slf4j
class Message {
    private  Integer flag = 1;
    private Integer loopNumber;

    Message(Integer loopNumber) {
        this.loopNumber = loopNumber;
    }

    public void print(Integer waitFlag,Integer nextFlag,String str) {
        for (int i = 0; i < loopNumber; i++) {
            synchronized (this) {
                while (flag != waitFlag) {
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.info(str);
                flag = nextFlag;
                this.notifyAll();
            }
        }
    }

}

2、await-signal(一个Condition)

package Thread5;

import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 交替输出
 */
@Slf4j
public class Test2 {

    public static void main(String[] args) {

        Message2 message2= new Message2(5,new ReentrantLock());

        new Thread(() -> {
            message2.print(1, 2, "a");
        }, "t1").start();


        new Thread(() -> {
            message2.print(2, 3, "b");
        }, "t2").start();

        new Thread(() -> {
            message2.print(3, 1, "c");
        }, "t3").start();
    }
}


@Slf4j
class Message2 {

    private Integer flag = 1;
    private Integer loopNumber;
    private Condition condition;
    private ReentrantLock reentrantLock;

    Message2(Integer loopNumber, ReentrantLock reentrantLock) {
        this.loopNumber = loopNumber;
        this.reentrantLock = reentrantLock;
        this.condition = reentrantLock.newCondition();
    }

    public void print(Integer waitFlag, Integer nextFlag, String str) {
        for (int i = 0; i < loopNumber; i++) {
            reentrantLock.lock();
            try {
                while (flag != waitFlag) {
                    try {
                        condition.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.info(str);
                flag = nextFlag;
                condition.signalAll();
            }finally {
                reentrantLock.unlock();
            }
        }
    }

}

3、await-signal(多个Condition)

package Thread5;

import lombok.extern.slf4j.Slf4j;
import org.omg.PortableServer.THREAD_POLICY_ID;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 交替输出
 */
@Slf4j
public class Test3 {

    public static void main(String[] args) {

        Message3 message = new Message3(5);
        Condition a = message.newCondition();
        Condition b = message.newCondition();
        Condition c = message.newCondition();


        new Thread(() -> {
            message.print(a, b, "a");
        }, "t1").start();


        new Thread(() -> {
            message.print(b, c, "b");
        }, "t2").start();

        new Thread(() -> {
            message.print(c, a, "c");
        }, "t3").start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        message.lock();
        try{
            a.signal();
        }finally {
            message.unlock();
        }
    }
}


@Slf4j
class Message3 extends ReentrantLock {
    private Integer loopNumber;

    Message3(Integer loopNumber) {
        this.loopNumber = loopNumber;
    }

    public void print(Condition thisCondition, Condition nextCondition, String str) {
        for (int i = 0; i < loopNumber; i++) {
            lock();
            try {

                thisCondition.await();
                log.info(str);
                nextCondition.signal();

            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                unlock();
            }
        }
    }
}

4、park-unpark:

package Thread5;

import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.LockSupport;

/**
 * 交替输出
 */
@Slf4j
public class Test4 {
    static Thread a;
    static Thread b;
    static Thread c;

    public static void main(String[] args) {

        Message4 message = new Message4(5);


        a = new Thread(() -> {
            message.print(b, "a");
        }, "t1");


        b = new Thread(() -> {
            message.print(c, "b");
        }, "t2");


        c = new Thread(() -> {
            message.print(a, "c");
        }, "t3");

        a.start();
        b.start();
        c.start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        LockSupport.unpark(a);
    }
}


@Slf4j
class Message4 {
    private Integer loopNumber;

    Message4(Integer loopNumber) {
        this.loopNumber = loopNumber;
    }

    public void print(Thread nextThread, String str) {
        for (int i = 0; i < loopNumber; i++) {
            LockSupport.park();
            log.info(str);
            LockSupport.unpark(nextThread);
        }
    }

}

五、两阶段终止

1、interrupt打断

package thread6;

import lombok.extern.slf4j.Slf4j;

/**
 * 两阶段终止
 */
@Slf4j
public class Test {
    public static void main(String[] args) {
        Message message = new Message();

        new Thread(() -> {
            log.info("t1开始线程");
            message.start();
        }, "t1").start();

        try {
            Thread.sleep(3500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            log.info("t2打断线程");
            message.stop();
        }, "t2").start();

    }
}

@Slf4j
class Message {
    private Thread thread;

    public void start() {

        thread = new Thread(() -> {
            while (true) {
                if (Thread.currentThread().isInterrupted()) {
                    log.info("循环被打断,退出循环");
                    break;
                } else {
                    try {
                        Thread.sleep(2000);
                        log.info("正常执行");
                    } catch (InterruptedException e) {
                        log.info("睡眠被打断");
                        Thread.currentThread().interrupt();
                    }
                }
            }
        }, "monitor");
        thread.start();
    }

    public void stop() {
        thread.interrupt();
    }
}

2、volatile版本

也需要调用 interrupt 打断 sleep

package thread6;

import lombok.extern.slf4j.Slf4j;

/**
 * 两阶段终止
 */
@Slf4j
public class Test {
    public static void main(String[] args) {
        Message message = new Message();


        new Thread(() -> {
            log.info("t1开始线程");
            message.start();
        }, "t1").start();

        try {
            Thread.sleep(3500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            log.info("t2打断线程");
            message.stop();
        }, "t2").start();

    }
}

@Slf4j
class Message {
    private Thread thread;
    private volatile Boolean stop = false;
    public void start() {

        thread = new Thread(() -> {
            while (true) {
                if (stop) {
                    log.info("循环被打断,退出循环");
                    break;
                } else {
                    try {
                        Thread.sleep(2000);
                        log.info("正常执行");
                    } catch (InterruptedException e) {
                        log.info("睡眠被打断");
                    }
                }
            }
        }, "monitor");

        thread.start();

    }

    public void stop() {
        stop = true;
        thread.interrupt();
    }


}

六、犹豫模式

1、定义

一个线程发现另一个线程已经做过某件事,所以本线程不再重复操作

2、代码(基于两阶段终止)

private volatile Boolean starting = false;

    public void start() {

        synchronized (this) {
            if (starting) {
                return;
            }
            starting = true;
        }
        //创建新线程
      }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值