- 921. Minimum Add to Make Parentheses Valid 使括号有效的最少添加
- 915. Partition Array into Disjoint Intervals 分割数组
- 932. Beautiful Array 漂亮数组
- 940. Distinct Subsequences II 不同的子序列 II
概念:A线程在运行时需要某个地址中的值,但是该地址还没有值,所以A等待。当B线程往该地址处写入了值后,B线程通知A线程,于是A线程继续执行。上面这样的一个过程就是等待/通知机制。
实现:
Object类中的wait()方法,可以使执行当前代码的线程等待,暂停执行。直到接到通知或被中断为止。注意:wait()方法只能在同步代码块中由锁对象调用,且调用wait()方法后,当前线程会释放锁。
Object类的notify()方法可以唤醒处于等待的线程,该方法也必须在同步代码块中由锁对象调用。如果有多个等待的线程,notify()只能唤醒其中一个,具体唤醒哪一个是不知道的。被notify()的线程需要重新去竞争锁才能被执行。
没有使用锁对象就调用wait()/notify()方法会产生异常:IllegalMonitorStateException。
wait()代码实例:
package wait;
public class Test01 {
public static void main(String[] args) throws InterruptedException {
String test = "abc";
String another = "def";
System.out.println("同步代码块前的代码");
synchronized (test) {
try {
System.out.println("wait前的代码");
// another.wait(); 只有被锁住的对象才能调用wait()方法
test.wait();
System.out.println("wait后的代码");
} catch (IllegalMonitorStateException e) {
e.printStackTrace();
}
}
System.out.println("同步代码块后的代码");
}
}
wait()/notify()代码示例:
package wait;
/**
* 需要通过notify唤醒线程
*/
public class Test02 {
public static void main(String[] args) {
String str = "wa";
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (str) {
System.out.println("线程1开始等待");
try {
str.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程1被唤醒并执行结束了");
}
}
}, "Thread1");
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (str) {
System.out.println("线程2唤醒线程1");
str.notify();
}
}
}, "Thread2");
thread1.start();
thread2.start();
}
}
执行了notify()的线程并不会立即释放锁,而是执行完同步代码块的所有代码后才会释放锁
interrupt()方法会中断wait():
当线程调用wait()处于等待状态时,调用线程对象的interrupt()方法会中断线程的等待状态,产生InterruptedException异常。
package wait;
import java.util.concurrent.TimeUnit;
/**
* interrupt()会中断线程的wait状态
*/
public class Test04 {
public static void main(String[] args) throws InterruptedException {
SubThread subThread = new SubThread();
subThread.start();
TimeUnit.SECONDS.sleep(1);
subThread.interrupt();
}
private static final Object lock = new Object();
static class SubThread extends Thread {
@Override
public void run() {
synchronized (lock) {
System.out.println("subThread wait");
try {
lock.wait();
} catch (InterruptedException e) {
System.out.println("wait等待被中断了");
}
System.out.println("subThread end wait");
}
}
}
}
notify()和notifyAll()的区别:
notify()只能唤醒一个线程,如果有多个线程处于等待状态,那么只能会有一个会被唤醒。notifyAll()可以唤醒所有等待的线程。
wait(long)的使用:
如果在指定时间内没有被唤醒,那么线程会自动唤醒。
package wait;
public class Test06 {
public static void main(String[] args) {
SubThread subThread = new SubThread();
subThread.start();
}
static final Object lock = new Object();
static class SubThread extends Thread{
@Override
public void run() {
synchronized (lock) {
try {
System.out.println("开始等待");
lock.wait(5000);
System.out.println("等待结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
在Java中,负责生产数据的是生产者,负责使用数据的是消费者。没有数据时,消费者等待;数据满时,生产者等待。
package producerdata;
public class ValueOP {
private String value = "";
//定义方法修改value字段的值
public void setValue() throws InterruptedException {
//如果value不是空串
synchronized (this) {
while(!value.equalsIgnoreCase("")) {
this.wait();
}
//是空串
value = System.currentTimeMillis() + "";
System.out.println("setValue设置的值是:" + value);
this.notify();
}
}
//定义方法读取字段值
public String getValue() throws InterruptedException {
synchronized (this) {
while(value.equalsIgnoreCase("")) {
this.wait();
}
//不是空串,读取值
System.out.println("value的值是:" + value);
value = "";
this.notify();
}
return value;
}
}
假设只有一个生产者和消费者:那么生产者和消费者将按顺序执行。
如果有多个生产者和消费者,那么可能出现假死现象:
1、 一个消费者唤醒了一个生产者,但是在这个生产者拿到锁之前,另一个消费者抢先拿到了锁;
2、 三个生产者全部等待,某个消费者唤醒的不是生产者,而是另一个消费者;
解决上述假死现象的方法是:将notify()改成notifyAll(),保证消费者唤醒了生产者,生产者唤醒了消费者。
操作栈:
package producerstack;
import java.util.List;
import java.util.ArrayList;
public class MyStack {
private List<Integer> list = new ArrayList<>();
private static final int MAX_SIZE = 2;
//定义方法模拟入栈
public synchronized void push(int value) throws InterruptedException {
//当栈中的数据已满,等待
while(list.size() >= MAX_SIZE) {
System.out.println(Thread.currentThread().getName() + " begin wait...");
this.wait();
}
list.add(value);
this.notifyAll();
System.out.println(Thread.currentThread().getName() + "添加了数据:" + value);
}
//定义方法模拟出栈
public synchronized void pop() throws InterruptedException {
//当栈中的数据为空,等待
while(list.size() == 0) {
System.out.println(Thread.currentThread().getName() + " begin wait...");
this.wait();
}
this.notifyAll();
System.out.println(Thread.currentThread().getName() + "拿到了数据:" + list.remove(0));
}
}
Java.io包的PipeStream管道流用于在线程之间传递数据,一个线程通过管道输出数据,另一个线程从管道中输入数据。相关类包括PipedInputStream、PipedOutputStream、PipedReader和PipedWriter。
package pipeStream;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.nio.charset.StandardCharsets;
/**
* 使用PipedInputStream和PipedOutputStream在线程间传递字节流
*/
public class Test {
public static void main(String[] args) throws IOException {
//定义管道字节流
PipedInputStream in = new PipedInputStream();
PipedOutputStream out = new PipedOutputStream();
//建立管道之间的关系
in.connect(out);
//创建两个线程,分别往管道里写数据,和读数据
new Thread(new Runnable() {
@Override
public void run() {
try {
writeData(out);
} catch (IOException e) {
e.printStackTrace();
}
}
}, "Thread1").start();
new Thread(new Runnable() {
@Override
public void run() {
try {
readData(in);
} catch (IOException e) {
e.printStackTrace();
}
}
}, "Thread2").start();
}
//向管道流中写入数据
public static void writeData(PipedOutputStream out) throws IOException {
//分别把0~100的数据写入管道
try {
for(int i = 0; i <= 10000; i++) {
out.write(("" + i).getBytes(StandardCharsets.UTF_8)); //把字节数组写入到输出管道流中
}
} catch (IOException e) {
e.printStackTrace();
} finally {
out.close();
}
}
//从管道流中读取数据
public static void readData(PipedInputStream in) throws IOException {
int count = 0;
byte[] bytes = new byte[1024];
int len = 0;
try {
while((len = in.read(bytes)) != -1) {
System.out.println(new String(bytes, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
in.close();
}
}
}
除了控制资源的访问外,还可以通过增加资源来保证线程安全。ThreadLocal主要解决为每个线程绑定自己的值的问题。
在以下这个代码示例中我们发现,如果多个线程共用一个SimpleDateFormat对象的话,实际运行下来会出现问题。但是通过ThreadLocal,我们为每个线程分别创建了一个SimpleDateFormat,就不会出现什么问题了。
package threadlocal;
import sun.java2d.pipe.SpanShapeRenderer;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 在多线程环境中,将字符串转换为日期对象。发现多个线程使用同一个SimpleDateFormat对象产生了线程安全问题,有的线程报了错误
*/
public class Test2 {
//定义SimpleDataFoemat对象,可以将字符串转换为日期
//发现多个线程使用同一个SimpleDateFormat对象,会产生线程安全问题
// private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
//使用ThreadLocal,为每个线程创建一个SimpleDateFormat
static ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<>();
//定义Runnable接口的实现类
static class ParseDate implements Runnable {
private int i = 0;
public ParseDate(int i) {
this.i = i;
}
@Override
public void run() {
String text = "2068年11月22日 08:22:" + i % 60; //构建日期字符串
try {
if(threadLocal.get() == null) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
threadLocal.set(sdf);
}
SimpleDateFormat sdf = threadLocal.get();
Date date = sdf.parse(text);
System.out.println(i + "--" + date);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
//创建100个线程
for(int i = 0; i < 100; i++) {
new Thread(new ParseDate(i)).start();
}
}
}
ThreadLocal指定初始值:
定义ThreadLocal的子类,在子类中重写initialValue()方法指定初始值
package threadlocal;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
*
* ThreadLocal初始值
*/
public class Test3 {
static class SubThreadLocal extends ThreadLocal<Date> {
@Override
protected Date initialValue() {
return new Date();
}
}
//定义ThreadLocal对象
static ThreadLocal<Date> threadLocal = new SubThreadLocal();
//定义线程类
static class SubThread extends Thread {
@Override
public void run() {
for(int i = 0; i < 10; i++) {
//第一次调用threadLocal的get方法,会返回null
System.out.println("----" + Thread.currentThread().getName() + " value = " + threadLocal.get());
if(threadLocal.get() == null) {
System.out.println("-------");
threadLocal.set(new Date());
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
SubThread t1 = new SubThread();
t1.start();
TimeUnit.SECONDS.sleep(1);
SubThread t2 = new SubThread();
t2.start();
}
}
大多数语言都支持双向进程通信。例如,在 Python 中,我可以(草率地)执行以下操作: >>> from subprocess import * >>> p = Popen('nslookup',
致力于使用 C++ 在 arduino 和 PC (Win 7) 之间进行通信。使用 WriteFile 和 ReadFile 创建通信或简单地发送或接收数据没有问题。但是当我想以某种方式“协调”沟通
我们正在开发一个基于微服务的应用程序。它们将使用 Helm Package Manager 部署到 kubernetes,并且它们都存储了自己的存储库和 helm chart。以下是我们微服务的名称。
我正在开发一个大型 MVVM 应用程序。我为此使用了 MVVM 轻量级工具包。该应用程序就像一个带有后退和前进按钮的网络浏览器。主视图是一个用户控件。我在主视图用户控件中放置了后退和前进按钮。主视图又
我在 java 和 freepascal(lazarus) 应用程序之间的通信有问题。我使用套接字。它们正确连接。一切都很顺利,直到我想从一个应用程序向另一个应用程序发送一些东西。在java而不是“a
我已经使用客户端套接字和服务器套接字使用C#编写了群聊。 当我使用VS 2017在自己的PC中运行程序(服务器和客户端)时,客户端和服务器之间的通信工作正常。 当我在笔记本电脑中运行客户端程序,并在自
Kubernetes 中两个不同 Pod 之间的通信是如何发生的? 就我而言,我有两个 Pod:前端和后端,它们都有不同的容器。 我希望我的前端 pod 与后端 pod 通信,但我不想使用后端 pod
我正在尝试在浏览器中嵌入的 flash 实例与在 C# WinForms 应用程序中运行的 flash 实例之间进行通信...我收到一个编译错误,内容为: 1119 Access of possibl
鉴于网络上缺乏信息,请问一个问题:我要在 Android 中创建一个应用程序,使用一个数据库应用程序 rails 。为此,我需要一个手动 session 。所以如果有人准备好了示例/教程显示通信 an
我正在编写一个应用程序,它将通过 MySQL 数据库对用户进行身份验证。我已经用 Java (android) 编写了它,但现在正在移植到 Windows 手机。 PHP 文件使用 $get 然后回显
是否可以通过互联网在两个不同设备上的两个不同应用程序之间建立通信。我想从设备 A 上的应用程序点击一个设备 B 上的应用程序,然后从设备 B 上的应用程序获取数据到设备 A 上的应用程序。如果可能,如
这是脚本: 它被放置在其他网站上。 com 并显示一个 iframe。如果有人点击 iframe 中的某个内容,脚本应该将一个 div 写入 othersite 。 com. 所以我的问题是如何做到
你好我是 php 的新手,我用 c++ 编写了整个代码并想在 php 中使用这段代码。所以我为我的代码制作了 dll 以使用它。但是我不能在 php 中使用这个 dll,可以谁能给我完整的代码来使用
我确定之前已经有人问过(并回答过)此类问题,所以如果是这样,请将我链接到之前的讨论... 在 C++ 中,假设我有一个 ClassA 类型的对象,其中包含一个 ClassB 类型的私有(private
我正在尝试使用 ATmega32 进行串行通信。首先,我使用 RS232,使用 USB-to-RS232 建立使用串行终端的接收和传输(在我的例子中是 tera 术语)。无论我从串行终端 Atmega
我找不到适用于 Ruby 的 SSL 实现。 我的部分项目需要服务器和客户端之间的安全通信链接,我希望为此使用 SSL 以创建安全 session 。 谢谢 最佳答案 如果你使用 Ruby 1.9.x
我正在尝试在客户端/服务器之间进行 SSL 通信。 到目前为止,我已经从 keystore 创建了 java.security.cert.X509Certificate。接下来我应该怎么做才能使这次沟
我在与 Windows 上的 USB 设备 通信时遇到问题。我不能使用 libusb 或 WinUSB,因为我有一个特定的驱动程序(Silabs USB 到 UART,这是一个 USB 到串口的桥接器
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
我发现 xcom 实际上是将数据写入数据库并从其他任务中提取数据。我的数据集很大,将其腌制并写入数据库会导致一些不必要的延迟。有没有办法在不使用 xcom 的情况下在同一 Airflow Dag 中的
我是一名优秀的程序员,十分优秀!