3.6、wait notify join原理及应用,同步模式之保护性暂停,异步模式之生产者消费者

本文主要是介绍3.6、wait notify join原理及应用,同步模式之保护性暂停,异步模式之生产者消费者,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

小故事 - 为什么要wait

  • 由于条件不满足,小南不能继续进行计算
  • 但小南如果一直占用着锁,其他人就得一直阻塞,效率太低
  • 在这里插入图片描述
  • 于是老王单开了一键休息室(调用wait方法),让小南到休息室(WaitSet)等着去了,但这是锁释放开,其他人可以由老王随机安排进屋
  • 知道小M将烟送来,大叫一声【你的烟到了】(调用notify方法)
  • 在这里插入图片描述
  • 小南于是可以离开休息室,重新进入竞争锁的队列
  • 在这里插入图片描述

原理

在这里插入图片描述

  • Owner线程发现条件不满足,调用wait方法,即可进入WaitSet变为WAITING状态
  • BLOCKED和WAITING的线程都处于阻塞状态,不占用CPU时间片
  • BLOCKED线程会在Owner线程释放锁时唤醒
  • WAITING线程会在Owner线程调用notify或notifyAll时唤醒,单唤醒后并不意味着立即获得锁,仍需要进入EntryList重新竞争

join原理

调用者轮询检查线程alive状态

t1.join()

等价于下面的代码

synchronized(t1) {// 调用者线程进入t1的WaitSet等待,直到t1运行结束while(t1.isAlive()) {t1.wait(0);}
}

API介绍

  • obj.wait()让进入object监视器的线程到WaitSet等待
  • obj.notify()在object上正在WaitSet等待的线程中挑一个唤醒
  • obj.notifyAll()让object上正在WaitSet等待的线程全部唤醒

他们都是线程之间进行协作的手段,都属于Object对象的方法,必须获得此对象的锁,才能调用这几个方法

final static Object obj = new Object();public static void main(String[] args) throws InterruptedException {new Thread(() -> {synchronized (obj) {log.debug("执行...");try {obj.wait();} catch (InterruptedException e) {e.printStackTrace();}log.debug("其他代码...");}}, "t1").start();new Thread(() -> {synchronized (obj) {log.debug("执行...");try {obj.wait();} catch (InterruptedException e) {e.printStackTrace();}log.debug("其他代码...");}}, "t2").start();TimeUnit.SECONDS.sleep(2);log.debug("唤醒obj上的其他线程");
//        obj.notify();synchronized (obj) {obj.notify();
//            obj.notifyAll();}}

如果没有获得对象锁,则会报错

2022/03/06-03:56:00.386 [t1] c.Test1 - 执行...
2022/03/06-03:56:00.388 [t2] c.Test1 - 执行...
2022/03/06-03:56:02.386 [main] c.Test1 - 唤醒obj上的其他线程
Exception in thread "main" java.lang.IllegalMonitorStateExceptionat java.lang.Object.notify(Native Method)at waittest.Test1.main(Test1.java:43)

notify的一种结果

2022/03/06-03:57:15.787 [t1] c.Test1 - 执行...
2022/03/06-03:57:15.789 [t2] c.Test1 - 执行...
2022/03/06-03:57:17.789 [main] c.Test1 - 唤醒obj上的其他线程
2022/03/06-03:57:17.790 [t1] c.Test1 - 其他代码...

notifyAll的结果

2022/03/06-03:57:45.530 [t1] c.Test1 - 执行...
2022/03/06-03:57:45.532 [t2] c.Test1 - 执行...
2022/03/06-03:57:47.544 [main] c.Test1 - 唤醒obj上的其他线程
2022/03/06-03:57:47.544 [t2] c.Test1 - 其他代码...
2022/03/06-03:57:47.544 [t1] c.Test1 - 其他代码...

wait()方法会释放对象的锁,进入WaitSet等待区,从而让其他线程有机会获得对象的锁。无限制等待,直到notify为止
wait(long n)有时限的等待,到n毫秒后结束等待,或是被notify

wait notify的正确姿势

开始之前先看看

sleep(long n)wait(long n)区别

1)sleep是Thread方法,而wait是Object方法
2)sleep不需要强制和synchronized配合使用,但wait需要和synchronized一起用
3)sleep在睡眠时,不会释放对象锁的,但wait在等待的时候会释放对象锁
4)他们的状态都是TIMED_WAITING

step 1

思考下面的解决方案好不好,为什么?

final static Object room = new Object();static boolean hasCigarette = false;static boolean hasTakeout = false;public static void main(String[] args) throws InterruptedException {new Thread(() -> {synchronized (room) {log.debug("有没有烟?{}", hasCigarette);if (!hasCigarette) {log.debug("没有烟,歇会儿");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}log.debug("有没有烟?{}", hasCigarette);if (hasCigarette) {log.debug("可以干活了");}}}, "t1").start();for (int i = 0; i < 5; i++)new Thread(() -> {synchronized (room) {log.debug("可以干活哦...");}}, "其他人").start();Thread.sleep(1000);new Thread(() -> {hasCigarette = true;log.debug("烟来喽");}, "送烟的").start();}

输出

2022/03/06-13:27:54.211 [t1] c.Test2 - 有没有烟?false
2022/03/06-13:27:54.213 [t1] c.Test2 - 没有烟,歇会儿
2022/03/06-13:27:55.213 [送烟的] c.Test2 - 烟来喽
2022/03/06-13:27:56.217 [t1] c.Test2 - 有没有烟?true
2022/03/06-13:27:56.217 [t1] c.Test2 - 可以干活了
2022/03/06-13:27:56.217 [其他人] c.Test2 - 可以干活哦...
2022/03/06-13:27:56.217 [其他人] c.Test2 - 可以干活哦...
2022/03/06-13:27:56.217 [其他人] c.Test2 - 可以干活哦...
2022/03/06-13:27:56.217 [其他人] c.Test2 - 可以干活哦...
2022/03/06-13:27:56.217 [其他人] c.Test2 - 可以干活哦...
  • 其他干活的线程,要一直阻塞,效率太低
  • t1线程必须睡足2s后才能醒来,就算烟提前送到,也无法立刻醒来
  • 加了synchronized(room)后,就好比t1在里面反锁了门睡觉,烟根本没法送进门,main没加synchronized就好像main线程是翻窗户进来的
  • 解决办法,使用wait-notify机制

step 2

final static Object room = new Object();static boolean hasCigarette = false;static boolean hasTakeout = false;public static void main(String[] args) throws InterruptedException {new Thread(() -> {synchronized (room) {log.debug("有没有烟?{}", hasCigarette);if (!hasCigarette) {log.debug("没有烟,歇会儿");try {room.wait(2000);} catch (InterruptedException e) {e.printStackTrace();}}log.debug("有没有烟?{}", hasCigarette);if (hasCigarette) {log.debug("可以干活了");}}}, "t1").start();for (int i = 0; i < 5; i++)new Thread(() -> {synchronized (room) {log.debug("可以干活哦...");}}, "其他人").start();Thread.sleep(1000);new Thread(() -> {synchronized (room) {hasCigarette = true;log.debug("烟来喽");room.notify();}}, "送烟的").start();}

输出

2022/03/06-13:32:21.203 [t1] c.Test3 - 有没有烟?false
2022/03/06-13:32:21.203 [t1] c.Test3 - 没有烟,歇会儿
2022/03/06-13:32:21.203 [其他人] c.Test3 - 可以干活哦...
2022/03/06-13:32:21.203 [其他人] c.Test3 - 可以干活哦...
2022/03/06-13:32:21.203 [其他人] c.Test3 - 可以干活哦...
2022/03/06-13:32:21.203 [其他人] c.Test3 - 可以干活哦...
2022/03/06-13:32:21.203 [其他人] c.Test3 - 可以干活哦...
2022/03/06-13:32:22.202 [送烟的] c.Test3 - 烟来喽
2022/03/06-13:32:22.202 [t1] c.Test3 - 有没有烟?true
2022/03/06-13:32:22.202 [t1] c.Test3 - 可以干活了
  • 解决了其他线程干活的线程阻塞问题
  • 但如果有其他线程也在等待条件呢?

step 3

final static Object room = new Object();static boolean hasCigarette = false;static boolean hasTakeout = false;public static void main(String[] args) throws InterruptedException {new Thread(() -> {synchronized (room) {log.debug("有没有烟?{}", hasCigarette);if (!hasCigarette) {log.debug("没有烟,歇会儿");try {room.wait();} catch (InterruptedException e) {e.printStackTrace();}}log.debug("有没有烟?{}", hasCigarette);if (hasCigarette) {log.debug("可以干活了");} else {log.debug("干活失败");}}}, "t1").start();new Thread(() -> {synchronized (room) {log.debug("外卖来了吗?{}", hasCigarette);if (!hasTakeout) {log.debug("没外卖,歇会儿");try {room.wait();} catch (InterruptedException e) {e.printStackTrace();}}log.debug("外卖来了吗?{}", hasCigarette);if (hasTakeout) {log.debug("可以干活了");} else {log.debug("干活失败");}}}, "t2").start();Thread.sleep(1000);new Thread(() -> {synchronized (room) {hasTakeout = true;log.debug("外卖来喽");room.notify();}}, "送外卖的").start();}

输出

2022/03/06-13:37:13.816 [t1] c.Test4 - 有没有烟?false
2022/03/06-13:37:13.816 [t1] c.Test4 - 没有烟,歇会儿
2022/03/06-13:37:13.816 [t2] c.Test4 - 外卖来了吗?false
2022/03/06-13:37:13.816 [t2] c.Test4 - 没外卖,歇会儿
2022/03/06-13:37:14.823 [送外卖的] c.Test4 - 外卖来喽
2022/03/06-13:37:14.823 [t1] c.Test4 - 有没有烟?false
2022/03/06-13:37:14.823 [t1] c.Test4 - 干活失败
  • notify只能随机唤醒一个WaitSet中的线程,这时如果有其他线程也在等待,那么就可能唤醒不了正确的线程,称之为【虚假唤醒】
  • 解决方法,改为notifyAll

step 4

		new Thread(() -> {synchronized (room) {hasTakeout = true;log.debug("外卖来喽");room.notifyAll();}}, "送外卖的").start();

输出

2022/03/06-13:39:27.661 [t1] c.Test5 - 有没有烟?false
2022/03/06-13:39:27.661 [t1] c.Test5 - 没有烟,歇会儿
2022/03/06-13:39:27.661 [t2] c.Test5 - 外卖来了吗?false
2022/03/06-13:39:27.661 [t2] c.Test5 - 没外卖,歇会儿
2022/03/06-13:39:28.675 [送外卖的] c.Test5 - 外卖来喽
2022/03/06-13:39:28.675 [t2] c.Test5 - 外卖来了吗?false
2022/03/06-13:39:28.675 [t2] c.Test5 - 可以干活了
2022/03/06-13:39:28.675 [t1] c.Test5 - 有没有烟?false
2022/03/06-13:39:28.675 [t1] c.Test5 - 干活失败
  • 用notifyAll仅解决某个线程的唤醒问题,但使用if + wait 判断仅有一次机会,一单条件不成立,就没看有重新判断的机会了
  • 解决办法,用while + wait,当条件不成立,再次wait

step 5

将 if 改为 while

final static Object room = new Object();static boolean hasCigarette = false;static boolean hasTakeout = false;public static void main(String[] args) throws InterruptedException {new Thread(() -> {synchronized (room) {log.debug("有没有烟?{}", hasCigarette);while (!hasCigarette) {log.debug("没有烟,歇会儿");try {room.wait();} catch (InterruptedException e) {e.printStackTrace();}}log.debug("可以干活了");}}, "t1").start();new Thread(() -> {synchronized (room) {log.debug("外卖来了吗?{}", hasCigarette);while (!hasTakeout) {log.debug("没外卖,歇会儿");try {room.wait();} catch (InterruptedException e) {e.printStackTrace();}}log.debug("可以干活了");}}, "t2").start();Thread.sleep(1000);new Thread(() -> {synchronized (room) {hasTakeout = true;log.debug("外卖来喽");room.notifyAll();}}, "送外卖的").start();}

输出

2022/03/06-13:43:45.737 [t1] c.Test6 - 有没有烟?false
2022/03/06-13:43:45.737 [t1] c.Test6 - 没有烟,歇会儿
2022/03/06-13:43:45.743 [t2] c.Test6 - 外卖来了吗?false
2022/03/06-13:43:45.743 [t2] c.Test6 - 没外卖,歇会儿
2022/03/06-13:43:46.751 [送外卖的] c.Test6 - 外卖来喽
2022/03/06-13:43:46.751 [t2] c.Test6 - 可以干活了
2022/03/06-13:43:46.751 [t1] c.Test6 - 没有烟,歇会儿

同步模式之保护性暂停

1、定义

即Guarded Suspension,用在一个线程等待另一个线程的执行结果

要点

  • 有一个结果需要从一个线程传递给另一个线程,让他们关联同一个GuardedObject
  • 如果有结果不断从一个线程到另一个线程,那么可以使用消息队列(生产者/消费者)
  • JDK中,join的实现、Future的实现,采用的就是此模式
  • 因为要等待另一方的结果,因此归类到同步模式

在这里插入图片描述

2、实现

@Slf4j(topic = "c.GuardedSuspension")
public class GuardedSuspension {public static void main(String[] args) {GuardedObject guardedObject = new GuardedObject();new Thread(()->{try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}Object response = new Object();log.debug("new response : {}", response);guardedObject.complete(response);}).start();log.debug("wait response");Object response = guardedObject.get();log.debug("get response : {}", response);}}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();}}}

输出

2022/03/06-14:00:11.709 [main] c.TPTInturrept - wait response
2022/03/06-14:00:13.724 [Thread-0] c.TPTInturrept - new response : java.lang.Object@77f37d84
2022/03/06-14:00:13.724 [main] c.TPTInturrept - get response : java.lang.Object@77f37d84

3、带超时版的GuardedObject

@Slf4j(topic = "c.GuardedObjectV2")
class GuardedObjectV2 {private Object response;private final Object lock = new Object();public Object get(long millis) {synchronized (lock) {// 开始时间long begin = System.currentTimeMillis();// 已经过时间long timePassed = 0;while (response == null) {// 还需要等待时间long waitTime = millis - timePassed;if (waitTime <= 0) {log.debug("超时了...");break;}try {lock.wait(waitTime);} catch (InterruptedException e) {e.printStackTrace();}timePassed = System.currentTimeMillis() - begin;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();}}}

测试,没有超时

    public static void main(String[] args) {GuardedObjectV2 guardedObjectV2 = new GuardedObjectV2();new Thread(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}guardedObjectV2.complete(null);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}guardedObjectV2.complete(new Object());}).start();log.debug("wait response");Object response = guardedObjectV2.get(2500);log.debug("get response : {}", response);}

输出

2022/03/06-14:32:05.620 [main] c.GuardedSuspension - wait response
2022/03/06-14:32:06.630 [Thread-0] c.GuardedObjectV2 - notify...
2022/03/06-14:32:06.630 [main] c.GuardedObjectV2 - timePassed:1010, object is null? true 
2022/03/06-14:32:07.634 [Thread-0] c.GuardedObjectV2 - notify...
2022/03/06-14:32:07.634 [main] c.GuardedObjectV2 - timePassed:2014, object is null? false 
2022/03/06-14:32:07.634 [main] c.GuardedSuspension - get response : java.lang.Object@3f0ee7cb

测试,超时

Object response = guardedObjectV2.get(1500);

输出

2022/03/06-14:34:23.324 [main] c.GuardedSuspension - wait response
2022/03/06-14:34:24.330 [Thread-0] c.GuardedObjectV2 - notify...
2022/03/06-14:34:24.330 [main] c.GuardedObjectV2 - timePassed:995, object is null? true 
2022/03/06-14:34:24.839 [main] c.GuardedObjectV2 - timePassed:1504, object is null? true 
2022/03/06-14:34:24.839 [main] c.GuardedObjectV2 - 超时了...
2022/03/06-14:34:24.839 [main] c.GuardedSuspension - get response : null
2022/03/06-14:34:25.333 [Thread-0] c.GuardedObjectV2 - notify...

4、多任务版GuardedObject

途中Futures就好比居民楼一层的信箱(每个信箱有房间编号),左侧的t0,t2,t4就好比等待邮件的居民,右侧的t1,t3,t4就好比邮递员

如果需要在多个类之间使用GuardedObject对象,作为参数传递不是很方便,因此设计一个用来解耦的中间类,这样不仅能够解耦【结果等待者】和【结果消费者】,还能同时支持多个任务管理

在这里插入图片描述
新增id来表示GuardedObject


@Slf4j(topic = "c.GuardedObjectV3")
class GuardedObjectV3 {private int id;public GuardedObjectV3(int id) {this.id = id;}public int getId() {return id;}private Object response;public Object get(long millis) {synchronized (this) {// 开始时间long begin = System.currentTimeMillis();// 已经过时间long timePassed = 0;while (response == null) {// 还需要等待时间long waitTime = millis - timePassed;if (waitTime <= 0) {log.debug("超时了...");break;}try {this.wait(waitTime);} catch (InterruptedException e) {e.printStackTrace();}timePassed = System.currentTimeMillis() - begin;log.debug("timePassed:{}, object is null? {} ", timePassed, response == null);}return response;}}public void complete(Object response) {synchronized (this) {this.response = response;log.debug("notify...");this.notifyAll();}}}

中间解耦类


class Mailboxs {private static final Map<Integer, GuardedObjectV3> boxes = new Hashtable<>();private static int id = 0;private static synchronized int generateId() {return ++id;}public static GuardedObjectV3 getGuardedObject(int id) {return boxes.remove(id);}public static GuardedObjectV3 createGuardedObject() {GuardedObjectV3 guardedObjectV3 = new GuardedObjectV3(generateId());boxes.put(guardedObjectV3.getId(), guardedObjectV3);return guardedObjectV3;}public static Set<Integer> getIds() {return boxes.keySet();}}

业务相关类及接口


@Slf4j(topic = "c.PeopleAction")
class PeopleAction implements Runnable {@Overridepublic void run() {// 收信GuardedObjectV3 guardedObjectV3 = Mailboxs.createGuardedObject();log.debug("开售收信,id:{}", guardedObjectV3.getId());Object mail = guardedObjectV3.get(5000);log.debug("收到信了,id:{},内容:{}", guardedObjectV3.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;}@Overridepublic void run() {// 收信GuardedObjectV3 guardedObjectV3 = Mailboxs.getGuardedObject(id);log.debug("开售送信,id:{},内容:{}", guardedObjectV3.getId(), mail);guardedObjectV3.complete(mail);}
}

测试

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

输出

2022/03/06-15:02:11.244 [Thread-0] c.PeopleAction - 开售收信,id:1
2022/03/06-15:02:11.244 [Thread-1] c.PeopleAction - 开售收信,id:2
2022/03/06-15:02:11.244 [Thread-2] c.PeopleAction - 开售收信,id:3
2022/03/06-15:02:12.241 [Thread-4] c.Postman - 开售送信,id:2,内容:内容2
2022/03/06-15:02:12.241 [Thread-3] c.Postman - 开售送信,id:3,内容:内容3
2022/03/06-15:02:12.241 [Thread-4] c.GuardedObjectV3 - notify...
2022/03/06-15:02:12.241 [Thread-3] c.GuardedObjectV3 - notify...
2022/03/06-15:02:12.241 [Thread-5] c.Postman - 开售送信,id:1,内容:内容1
2022/03/06-15:02:12.241 [Thread-5] c.GuardedObjectV3 - notify...
2022/03/06-15:02:12.241 [Thread-2] c.GuardedObjectV3 - timePassed:997, object is null? false 
2022/03/06-15:02:12.241 [Thread-1] c.GuardedObjectV3 - timePassed:997, object is null? false 
2022/03/06-15:02:12.241 [Thread-0] c.GuardedObjectV3 - timePassed:997, object is null? false 
2022/03/06-15:02:12.241 [Thread-1] c.PeopleAction - 收到信了,id:2,内容:内容2
2022/03/06-15:02:12.241 [Thread-0] c.PeopleAction - 收到信了,id:1,内容:内容1
2022/03/06-15:02:12.241 [Thread-2] c.PeopleAction - 收到信了,id:3,内容:内容3

异步模式之生产者/消费者

1、定义

要点

  • 与前面的保护性暂停中的GuardedObject不同,不需要产生结果和消费结果的线程一一对应
  • 消费队列可以用来平衡生产和消费的线程资源
  • 生产者仅负责产生结果数据,不关心数据如何处理,而消费者专心处理数据结果
  • 消息队列是有容量限制的,满时不再加入数据,空时不再消耗数据
  • JDK中各种阻塞队列,采用的就是这种模式

在这里插入图片描述

2、实现


class Message {private int id;private Object Object;public Message(int id, java.lang.Object object) {this.id = id;Object = object;}public int getId() {return id;}public java.lang.Object getObject() {return Object;}
}@Slf4j(topic = "c.MessageQueue")
class MessageQueue {private int capacity;private final Deque<Message> queue;public MessageQueue(int capacity) {this.capacity = capacity;queue = new LinkedList<>();}public void put(Message message) {synchronized (queue) {while (queue.size() == capacity) {log.debug("队列已满,wait");try {queue.wait();} catch (InterruptedException e) {e.printStackTrace();}}log.debug("生产消息,id:{},内容:{}", message.getId(), message.getObject());queue.addLast(message);queue.notifyAll();}}public Message take() {synchronized (queue) {while (queue.isEmpty()) {log.debug("没有可用的消息,wait");try {queue.wait();} catch (InterruptedException e) {e.printStackTrace();}}Message message = queue.removeFirst();log.debug("消费消息,id:{},内容:{}", message.getId(), message.getObject());queue.notifyAll();return message;}}
}

测试

public static void main(String[] args) {MessageQueue messageQueue = new MessageQueue(2);for (int i = 0; i < 4; i++) {int finalI = i;new Thread(() -> {messageQueue.put(new Message(finalI, "内容" + finalI));}, "生产者" + i).start();}new Thread(() -> {while (true) {Message message = messageQueue.take();}}, "消费者").start();}

输出

2022/03/06-15:56:31.921 [消费者] c.MessageQueue - 没有可用的消息,wait
2022/03/06-15:56:31.921 [生产者3] c.MessageQueue - 生产消息,id:3,内容:内容3
2022/03/06-15:56:31.930 [消费者] c.MessageQueue - 消费消息,id:3,内容:内容3
2022/03/06-15:56:31.930 [消费者] c.MessageQueue - 没有可用的消息,wait
2022/03/06-15:56:31.930 [生产者1] c.MessageQueue - 生产消息,id:1,内容:内容1
2022/03/06-15:56:31.930 [生产者0] c.MessageQueue - 生产消息,id:0,内容:内容0
2022/03/06-15:56:31.930 [生产者2] c.MessageQueue - 队列已满,wait
2022/03/06-15:56:31.930 [消费者] c.MessageQueue - 消费消息,id:1,内容:内容1
2022/03/06-15:56:31.930 [消费者] c.MessageQueue - 消费消息,id:0,内容:内容0
2022/03/06-15:56:31.930 [消费者] c.MessageQueue - 没有可用的消息,wait
2022/03/06-15:56:31.930 [生产者2] c.MessageQueue - 生产消息,id:2,内容:内容2
2022/03/06-15:56:31.930 [消费者] c.MessageQueue - 消费消息,id:2,内容:内容2
2022/03/06-15:56:31.930 [消费者] c.MessageQueue - 没有可用的消息,wait

这篇关于3.6、wait notify join原理及应用,同步模式之保护性暂停,异步模式之生产者消费者的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1053324

相关文章

流媒体平台/视频监控/安防视频汇聚EasyCVR播放暂停后视频画面黑屏是什么原因?

视频智能分析/视频监控/安防监控综合管理系统EasyCVR视频汇聚融合平台,是TSINGSEE青犀视频垂直深耕音视频流媒体技术、AI智能技术领域的杰出成果。该平台以其强大的视频处理、汇聚与融合能力,在构建全栈视频监控系统中展现出了独特的优势。视频监控管理系统EasyCVR平台内置了强大的视频解码、转码、压缩等技术,能够处理多种视频流格式,并以多种格式(RTMP、RTSP、HTTP-FLV、WebS

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

服务器集群同步时间手记

1.时间服务器配置(必须root用户) (1)检查ntp是否安装 [root@node1 桌面]# rpm -qa|grep ntpntp-4.2.6p5-10.el6.centos.x86_64fontpackages-filesystem-1.41-1.1.el6.noarchntpdate-4.2.6p5-10.el6.centos.x86_64 (2)修改ntp配置文件 [r

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

zoj3820(树的直径的应用)

题意:在一颗树上找两个点,使得所有点到选择与其更近的一个点的距离的最大值最小。 思路:如果是选择一个点的话,那么点就是直径的中点。现在考虑两个点的情况,先求树的直径,再把直径最中间的边去掉,再求剩下的两个子树中直径的中点。 代码如下: #include <stdio.h>#include <string.h>#include <algorithm>#include <map>#

hdu4407(容斥原理)

题意:给一串数字1,2,......n,两个操作:1、修改第k个数字,2、查询区间[l,r]中与n互质的数之和。 解题思路:咱一看,像线段树,但是如果用线段树做,那么每个区间一定要记录所有的素因子,这样会超内存。然后我就做不来了。后来看了题解,原来是用容斥原理来做的。还记得这道题目吗?求区间[1,r]中与p互质的数的个数,如果不会的话就先去做那题吧。现在这题是求区间[l,r]中与n互质的数的和