Java Monitor 从两个方面来支持线程之间的同步,即:互斥执行与协作。 Java 使用对象锁 ( 使用 synchronized 获得对象锁 ) 保证工作在共享的数据集上的线程互斥执行 , 使用 notify/notifyAll/wait 方法来协同不同线程之间的工作。这些方法在 Object 类上被定义,会被所有的 Java 对象自动继承。
实质上,Java 的 Object 类本身就是监视者对象,Java 语言对于这样一个典型并发设计模式做了内建的支持。不过,在 Java 里,我们已经看不到了我们在 C++ 一节所讨论的区域锁与条件变量的概念。下图很好地描述了 Java Monitor 的工作机理。
线程如果获得监视锁成功,将成为该监视者对象的拥有者。在任一时刻内,监视者对象只属于一个活动线程 (Owner) 。拥有者线程可以调用 wait 方法自动释放监视锁,进入等待状态。
线程间的监视锁机制是遵循java所谓的Happens-before原则的,其实内容也都很好理解,A一定要发生在B前面。
happens-before完整规则:
(1)同一个线程中的每个Action都happens-before于出现在其后的任何一个Action。
(2)对一个监视器的解锁happens-before于每一个后续对同一个监视器的加锁。
(3)对volatile字段的写入操作happens-before于每一个后续的同一个字段的读操作。
(4)Thread.start()的调用会happens-before于启动线程里面的动作。
(5)Thread中的所有动作都happens-before于其他线程检查到此线程结束或者Thread.join()中返回或者Thread.isAlive()==false。
(6)一个线程A调用另一个另一个线程B的interrupt()都happens-before于线程A发现B被A中断(B抛出异常或者A检测到B的isInterrupted()或者interrupted())。
(7)一个对象构造函数的结束happens-before与该对象的finalizer的开始
(8)如果A动作happens-before于B动作,而B动作happens-before与C动作,那么A动作happens-before于C动作。
上面提到了volatile字段,
volatile相当于synchronized的弱实现,也就是说volatile实现了类似synchronized的语义,却又没有锁机制。它确保对volatile字段的更新以可预见的方式告知其他的线程。
volatile包含以下语义:
(1)Java 存储模型不会对valatile指令的操作进行重排序:这个保证对volatile变量的操作时按照指令的出现顺序执行的。
(2)volatile变量不会被缓存在寄存器中(只有拥有线程可见)或者其他对CPU不可见的地方,每次总是从主存中读取volatile变量的结果。也就是说对于volatile变量的修改,其它线程总是可见的,并且不是使用自己线程栈内部的变量。也就是在happens-before法则中,对一个valatile变量的写操作后,其后的任何读操作理解可见此写操作的结果。
尽管volatile变量的特性不错,但是volatile并不能保证线程安全的,也就是说volatile字段的操作不是原子性的,volatile变量只能保证可见性(一个线程修改后其它线程能够理解看到此变化后的结果),要想保证原子性,目前为止只能加锁!
volatile通常在下面的场景(停止线程的标志位):
- volatile boolean done = false;
- …
- while( ! done ){
- dosomething();
- }
还有一个定义,原子操作,具体描述,多个线程执行一个操作时,其中任何一个线程要么完全执行完此操作,要么没有执行此操作的任何步骤,那么这个操作就是原子的。
所以原子操作是不需要进行同步的(synchronized)。
不是一行代码就表示他是原子操作,比如C++这个自增就不是,下面是原子操作的例子,
Reads and writes are atomic for reference variables and for most primitive variables (all types except long and double).读写基本变量,除了long和double
Reads and writes are atomic for all variables declared volatile (including long and double variables).加上volatile字段的读写基本变量。
1,synchronized关键字
在实际编程中,我们有两种方式实现同步,分别是同步方法(synchronized methods)或同步块(synchronized block/synchronized statement)。
同步方法是在方法前加synchronized, 如果该方法是static的,则认为锁是相对于Class的,其他线程操作该类的任何对象时,遇到static同步方法或者方法内同步该Class时,需要等待;若该方法不是static的,则认为锁是相对于自身对象(this)的,其他线程操作此对象时,遇到同步方法(非static),需要等待。
注意点
1. 可以认为锁是属于引用类型的, 同步的操作需要获取锁之后才进行,否则一直等待。编程时需注意锁(synchronized)的对象
2. 线程在wait后会释放持有的锁。
3. 各线程同步时遵守先触发,先得锁原则(happens-before relationship)。
4. 构造函数无法被synchronized。
2,wait(),notify(),notifyAll()。
如果条件不满足,则等待。当条件满足时,等待该条件的线程将被唤醒。在Java中,这个机制的实现依赖于wait/notify。等待机制与锁机制是密切关联的。
- synchronized(obj) {
- while(!condition) {
- obj.wait();
- }
- obj.doSomething();
- }
当线程A获得了obj锁后,发现条件condition不满足,无法继续下一处理,于是线程A就wait()。
在另一线程B中,如果B更改了某些条件,使得线程A的condition条件满足了,就可以唤醒线程A:
- synchronized(obj) {
- condition = true;
- obj.notify();
- }
需要注意的点
1. wait, notify, notifyAll必须在synchronized代码内。即该线程持有了某引用的锁时,wait, notify, notifyAll才可以被执行,否则,会报IllegalMonitorStateException
- // 会抛IllegalMonitorStateException异常, 因为没持有this的锁。
- this.wait();
- synchronized(this) {
- // 正确写法
- this.wait();
- // 会抛IllegalMonitorStateException异常, 因为没有持有a对象的锁。
- this.a.wait();
- }
2. 执行wait()后会释放锁持有的锁,其他等待同步中的线程这时会持有该锁,并执行。
3. wait()执行后,线程状态会变为disabled, 想继续执行除非以下事件中的一个发生:
a)其他线程在此同步的引用上执行了notify()或者notifyAll(),注意是和wait相同引用上执行的notify()。
b)线程被其他线程中断,会报InterruptedException。
c)wait可以指定timeout, 当timeout时间过去时。
4. wait继续执行需要重新获得该引用的锁,若有其他线程占有着此锁,则仍然无法恢复。
5. notify是随机唤醒一个wait中的线程,notifyAll是把所有wait中的线程全部唤醒。notify的对象仍旧是其持有的锁的引用。
6. 如果没有线程wait, notify将会被忽略。
3,wait和sleep的区别
sleep与wait的不同点是:sleep并不释放锁,并且sleep的暂停和wait暂停是不一样的。obj.wait会使线程进入obj对象的等待集合中并等待唤醒。但是wait()和sleep()都可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException。
如果线程A希望立即结束线程B,则可以对线程B对应的Thread实例调用interrupt方法。如果此刻线程B正在wait/sleep/join,则线程B会立刻抛出InterruptedException,在catch() {} 中直接return即可安全地结束线程。
4,并发程序可能会出现的问题。
死锁,多个线程之间互相等待。著名 的哲学家拿叉子事件。
还有两个不是很长见的问题。饥饿和活锁,饥饿指的是要等一个锁很长很长时间,像当前占有锁的同步方法中执行了很复杂的操作,导致其他要等待太久。活锁就更有趣一些,像两个线程之间互相响应,谦让。像两个人相向而行,都想互相避开,但是A向左,B向右;A向右,B向左,还是碰到一起。
超时的等待和死锁对于用户来说就没有什么太大的区别了。
参考:http://wenjuema.iteye.com/blog/660705
http://blog.csdn.net/aming2006/article/details/4463979
http://www.blogjava.net/xylz/archive/2010/07/03/325168.html
相关推荐
**rtthread详解** RTThread(Real-Time Thread)是一款开源、小巧、高效且功能强大的实时操作系统(RTOS),适用于物联网(IoT)设备和嵌入式系统。它提供了丰富的组件和服务,如线程管理、内存管理、任务调度、...
3. **信号量与互斥锁**:为了实现线程间的同步和互斥,RT-Thread提供了信号量和互斥锁机制,确保资源的安全访问。 4. **内存管理**:内建的内存管理系统能有效分配和回收内存,支持静态和动态内存分配策略。 5. **...
RT-Thread提供了一套完整的任务管理、信号量、互斥锁、消息队列等机制,这些都需要在代码中正确地使用和调用。 在VC环境下,你可以编写应用程序代码,实现特定功能,然后利用IDE的调试工具进行断点设置、变量查看、...
### pthread_cond_wait详解 #### 一、pthread_cond_wait概述 `pthread_cond_wait` 是一个用于线程间同步的重要函数,通常配合互斥锁 `pthread_mutex_t` 使用,以实现线程间的条件等待与唤醒机制。当某个线程需要...
4. `wait()`, `notify()`和`notifyAll()`:用于线程间的协作,必须在同步块中调用。 五、多线程的优点与挑战 1. 优点:提高系统资源利用率,提高并发性能,增强用户体验。 2. 挑战:线程安全问题、死锁和活锁风险、...
- `java.util.concurrent`包提供了如`CountDownLatch`, `CyclicBarrier`, `Semaphore`等高级并发工具,帮助开发者更好地管理线程间的协作和同步。 在实际开发中,理解并熟练运用这些概念和机制,可以编写出高效、...
1. 线程池(Thread Pool)模式:通过重用一组固定数量的线程来执行任务,减少了线程创建和销毁所带来的开销,提高程序响应速度。 2. 工作队列(Work Queue)模式:在任务和执行线程之间设置一个队列,用于存放待...
例如,可以使用synchronized关键字实现互斥访问,避免数据竞争;使用wait(), notify(), notifyAll()方法进行线程间的协作;使用join()方法让一个线程等待另一个线程完成;使用Thread.sleep()方法让线程暂时休眠;...
本资源提供了详细的Java多线程设计模式详解,包括PDF文档和源代码,使得学习过程更加直观和实践性强。 首先,我们要理解什么是多线程。在单线程环境下,程序执行是顺序的,而在多线程环境中,多个任务可以同时执行...
生产者消费者模式是一种典型的线程同步问题,用于解决生产数据和消费数据的线程协作。在Java中,可以使用`BlockingQueue`(如`ArrayBlockingQueue`)来实现这种模式,生产者线程负责向队列中添加元素,消费者线程...
- **事件(Event)**:通过WaitOne()和Set()方法实现线程间的通信和协作。 - ** Barrier** 和 **CountdownEvent**:用于协调多个线程到达某个同步点。 4. **线程安全** - **锁(Lock)**:使用lock关键字确保临界区...
线程间的协作和设计模式也是多线程编程的重要部分。生产者消费者模型是一种常见的线程间协作模式,通过BlockingQueue实现,生产者线程向队列中添加元素,消费者线程从队列中取出元素。还有读者写者模式,允许多个...
"Java多线程机制详解" Java多线程机制是Java语言中的一种重要机制,能够提高程序的执行效率。...但是,多线程机制的实现需要考虑线程之间的同步和互斥问题、线程之间的通信和协作问题等,以避免资源的竞争和冲突。
### 并发服务器—多线程服务器详解 #### 一、引言 在现代软件开发中,特别是网络应用和服务的设计中,对并发处理能力的需求日益增长。为了满足高并发访问的需求,开发人员常采用多线程技术来构建高效、响应迅速的...
在这个例子中,`thread_function`是新线程要执行的函数,`pthread_create`用于创建线程,参数指定了线程属性(在这里是默认值),线程函数和它的参数。主程序继续执行,直到调用`pthread_exit`,此时所有子线程也将...
- **同步**:控制线程的执行顺序和协作,如生产者消费者模型、读者写者问题等,可以实现线程间的协调。 - **使用场景**: - 互斥常用于保护临界资源,如全局变量,避免并发修改导致数据错误。 - 同步则更关注...
线程的中断、互斥同步可以通过中断机制、wait/notify机制和`synchronized`关键字实现,线程间的协作可以使用Condition、CountDownLatch、CyclicBarrier等工具类。 在Java中,除了`synchronized`,还有更高级的锁...
同步对象是实现线程间协作和避免竞态条件的关键。 - **互斥锁属性**:互斥锁是一种常用的同步工具,用于保护临界区,防止多个线程同时访问共享资源。 - **初始化互斥锁属性对象**:在使用互斥锁前,需要先初始化...