线程间的同步与通信

本文介绍了Java中实现线程间通信的wait(), wait(long timeout), wait(long timeout, int nanos), notify(), notifyAll()五种方法,详细解析了锁池与等待池的概念,并探讨了notify()与notifyAll()的区别。通过一个实例展示了如何让三个线程轮流打印ABC,以此理解线程同步的重要性。" 78364421,1501275,Drupal8内容实体基类详解与设计模式分析,"['Drupal8源码分析', '内容实体', '编程设计模式']

线程间通信的方法

        在Java中,我们可以使用
                                            wait()
                                            wait(long timeout)
                                            wait(long timeout,int naos)
                                            notify()
                                            notifyAll()

        这5个方法来实现线程间的通信。

        其中wait()方法的作用是,阻塞当前线程(阻塞的原因常常是一些必要的条件还没有满足),一个线程在调用wait()方法后,会释放当前的锁对象,不再参与竞争,直到其他线程调用了这个锁的notifyAll()、或notify()方法,这个被阻塞的线程才会有可能继续执行

        注意,上面说的是有可能继续执行,这是为什么呢?这是因为,当那些被notifyAll()唤醒的线程,同时去抢一把锁时,只有一个线程可以成功抢到,此时成功抢到锁的线程才能继续执行,而那些没有抢到锁的线程,则继续是阻塞状态。

        notify()、notifyAll()方法的作用是,唤醒那些调用了wait()方法的线程。notify()是唤醒其中一个,notifyAll()是全部唤醒。

        上述的5个方法,都是定义在Object类中。

锁池与等待池

        锁池:假设线程A已经拥有了某个对象的monitor锁,而其他的线程调用这个对象的synchronized方法(或者synchronized代码块),由于这些线程在进入已经被对象的synchronized方法之前必须先获得该对象的monitor的拥有权,但是该对象的锁目前正在被线程A拥有,所以这些想要获取该对象monitor拥有权的线程就进入到了该对象的锁池中。
        等待池: 假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的monitor,进入到该对象的等待池中。

        我们通过一个很简单的伪代码再来看一看锁池:

class Handler extends Thread{
   
   
	Object o = new Object
	public void run(){
   
   
		sychronized(o){
   
   
			...
		}
	}
}

        假设现在有两个线程,分别为A、B。假设线程A先执行,那么线程A会执行到synchronized代码块时,首先获取对象o的monitor,此时,对象o的monitor的拥有者为线程A,然后线程A进入synchronized代码块执行。此时,线程B也要执行synchronized代码块里的内容,它发现对象o的monitor已经被占用了,此时,线程B就会进入到对象o的锁池中等待线程A释放掉对象o的monitor的使用权。

notify()、notifyAll()区别

        notify()、notifyAll()的区别在于:

       &

### 线程间通信同步的实现方式 线程间通信同步多线程编程中的核心问题,用于确保线程之间的数据访问顺序和资源一致性。线程同步的核心目标是控制多个线程对共享资源的访问,避免数据竞争和不一致的问题。线程间通信则侧重于线程之间的信息传递,确保线程能够协调执行顺序和状态变化。 #### 1. 线程同步的实现方式 线程同步可以通过多种机制实现,包括临界区、互斥对象(Mutex)、信号量(Semaphore)和事件对象(Event)。这些机制可以有效控制对共享资源的访问,并确保线程之间的执行顺序。 - **临界区**是一种轻量级的同步机制,适用于同一进程内的线程同步线程在访问共享资源前需要调用`EnterCriticalSection`函数进入临界区,访问完成后调用`LeaveCriticalSection`函数释放临界区。临界区会记录拥有它的线程句柄,因此具有“线程所有权”概念,允许线程重复进入临界区[^3]。 - **互斥对象(Mutex)**类似于临界区,但互斥量是内核对象,不仅可以在同一进程的线程间使用,还可以在不同进程之间共享。互斥对象也具有线程所有权的概念,因此主要用于线程间的互斥访问,而不是同步[^3]。 - **信号量(Semaphore)**是一种允许多个线程同时访问共享资源的同步机制。它通过计数器来控制线程的访问,当计数器大于零时,线程可以访问资源;当计数器为零时,线程需要等待。信号量属于内核对象,可以用于进程间的通信。 - **事件对象(Event)**用于线程间同步,通过触发事件来通知其他线程某个操作已经完成。事件对象分为手动置位事件和自动置位事件。线程可以通过`SetEvent`函数触发事件,通过`ResetEvent`函数将事件设为未触发状态。事件是内核对象,可以解决线程间同步问题,也能用于互斥访问。 #### 2. 线程间通信的实现方式 线程间通信通常依赖于同步机制来实现,例如通过共享内存和信号量或事件对象来传递状态信息。此外,还可以使用条件变量和管道等方式进行通信。 - **共享内存**是一种高效的线程间通信方式,多个线程可以直接读写同一块内存区域。为了确保数据一致性,通常需要配合同步机制(如互斥锁或信号量)来控制访问顺序[^1]。 - **条件变量**通常互斥锁一起使用,用于等待某个条件成立。线程可以在条件不满足时阻塞,直到其他线程通过条件变量发出通知。这种方式可以实现线程间的协调通信[^1]。 - **管道**是一种简单的线程间通信方式,通过创建一个管道,一个线程向管道写入数据,另一个线程从管道读取数据。管道可以是匿名管道(用于同一进程内的线程)或命名管道(用于不同进程之间的线程)。 #### 3. 示例代码:线程间同步通信 以下是一个使用互斥锁和条件变量实现线程间同步通信的示例: ```cpp #include <iostream> #include <thread> #include <mutex> #include <condition_variable> std::mutex mtx; std::condition_variable cv; bool ready = false; void worker() { std::unique_lock<std::mutex> lock(mtx); // 等待条件变量通知 cv.wait(lock, []{ return ready; }); std::cout << "Worker thread is processing data." << std::endl; } int main() { std::thread workerThread(worker); // 模拟一些操作 std::this_thread::sleep_for(std::chrono::seconds(1)); { std::lock_guard<std::mutex> lock(mtx); ready = true; // 修改共享状态 } cv.notify_one(); // 通知等待的线程 workerThread.join(); std::cout << "Main thread notifies worker thread." << std::endl; return 0; } ``` 在这个示例中,`std::condition_variable`用于实现线程间同步,主线程通过`cv.notify_one()`通知工作线程条件已经满足,工作线程则通过`cv.wait(lock, []{ return ready; })`等待条件成立。 ####
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值