`

同一任务和对象锁的问题

阅读更多
   偶尔翻开java编程思想看看多线程的篇章,意焦突然卡在某个问题上不动了。这个问题看过多少次多少遍了,此刻才领会,顿时感觉自己好笨拙的思维。

    问题是这样的:一般来说,在多线程程序中,某个任务在持有某对象的锁后才能运行任务,其他任务只有在该任务释放同一对象锁后才能拥有对象锁,然后执行任务。于是,想到,同一个任务在持有同一个对象的锁后,在不释放锁的情况下,继续调用同一个对象的其他同步(synchronized)方法,该任务是否会再次持有该对象锁呢?

    答案是肯定的。同一个任务在调用同一个对象上的其他synchronized方法,可以再次获得该对象锁。

    多线程编程是一件很微妙的事情,这里考验人的不是大局而是细节,细节的细节。希望能唤醒自己愚笨的思维。

 package thread.thread1;

/**
 * 同一任务可以再次持有对象锁 create on 2010.08.04 08:27
 * 
 * @since jdk1.6
 * @author maozj
 * @version 1.0
 * 
 */
public class SynchronizedClassHolder {
	/**
	 * Test client
	 * @param args
	 */
	public static void main(String[] args) {
		new Thread() {
			public void run() {
				new SynchronizedClassHolder().f1();
			}
		}.start();
	}

	private static int count = 10;

	/**
	 * synchronized f1()
	 */
	public synchronized void f1() {
		if (--count > 0) {
			System.out.println("f1() calling f2() count " + count);
			f2();
		}
	}

	/**
	 * synchronized f2()
	 */
	public synchronized void f2() {
		if (--count > 0) {
			System.out.println("f2() calling f1() count " + count);
			f1();
		}
	}
}

输出:
f1() calling f2() count 9
f2() calling f1() count 8
f1() calling f2() count 7
f2() calling f1() count 6
f1() calling f2() count 5
f2() calling f1() count 4
f1() calling f2() count 3
f2() calling f1() count 2
f1() calling f2() count 1
分享到:
评论
37 楼 kind790 2010-08-06  
whoamiwangwb 写道
public class SynchronizedClassHolder {

	public static void main(String[] args) {
		new Thread("thread 1") {
			public void run() {
				new SynchronizedClassHolder().f1();
			}
		}.start();

		new Thread("thread 2") {
			public void run() {
				new SynchronizedClassHolder().f1();
			}
		}.start();
	}

	private static int count = 10;

	public synchronized void f1() {
		if (--count > 0) {
			System.out.println(Thread.currentThread().getName()
					+ "    f1() calling f2() count " + count);
			f2();
		}
	}

	public synchronized void f2() {
		if (--count > 0) {
			System.out.println(Thread.currentThread().getName()
					+ "    f2() calling f1() count " + count);
			f1();
		}
	}
}




显示的结果:


thread 1    f1() calling f2() count 9
thread 2    f1() calling f2() count 8
thread 2    f2() calling f1() count 6
thread 2    f1() calling f2() count 5
thread 2    f2() calling f1() count 4
thread 2    f1() calling f2() count 3
thread 2    f2() calling f1() count 2
thread 1    f2() calling f1() count 7
thread 2    f1() calling f2() count 1



这个怎么解释呢?
jvm 1.6.0.18


两回事情,请看看楼主的问题哦。呵呵
当多个线程使用同一个对象调用此对象的同步方法时才需要竞争(当前)对象锁,此时同步才有意义;现在的问题是两个线程使用各自的对象调用各自对象的同步方法了。
36 楼 wkoffee 2010-08-05  
whoamiwangwb 写道
public class SynchronizedClassHolder {

	public static void main(String[] args) {
		new Thread("thread 1") {
			public void run() {
				new SynchronizedClassHolder().f1();
			}
		}.start();

		new Thread("thread 2") {
			public void run() {
				new SynchronizedClassHolder().f1();
			}
		}.start();
	}

	private static int count = 10;

	public synchronized void f1() {
		if (--count > 0) {
			System.out.println(Thread.currentThread().getName()
					+ "    f1() calling f2() count " + count);
			f2();
		}
	}

	public synchronized void f2() {
		if (--count > 0) {
			System.out.println(Thread.currentThread().getName()
					+ "    f2() calling f1() count " + count);
			f1();
		}
	}
}




显示的结果:


thread 1    f1() calling f2() count 9
thread 2    f1() calling f2() count 8
thread 2    f2() calling f1() count 6
thread 2    f1() calling f2() count 5
thread 2    f2() calling f1() count 4
thread 2    f1() calling f2() count 3
thread 2    f2() calling f1() count 2
thread 1    f2() calling f1() count 7
thread 2    f1() calling f2() count 1



这个怎么解释呢?
jvm 1.6.0.18


你这个程序根本没有同步,两个线程都是只对自己的SynchronizedClassHolder加锁,synchronized根本没有意义
35 楼 piao_bo_yi 2010-08-05  
就是由编译器搞的互斥量或二元信号量,看JAVA里面的描述想理解这个话题,说心里话,很难。
34 楼 mercyblitz 2010-08-05  
whoamiwangwb 写道
是不是说 ,在从f1调用f2的时候,出现的释放+重新获取的动作呢?



没有,代码中没有wait这样的操作,不会出现释放锁的行为。
33 楼 yangjitang100 2010-08-05  
对象锁 分持有 和 非持有,持有状态有有不同情况,访问一个同步方法则指数加一,反之减一,所以当一个任务持有对象锁之后再次访问该对象的的同步方法则指数加一,不会死锁。
32 楼 whoamiwangwb 2010-08-05  
是不是说 ,在从f1调用f2的时候,出现的释放+重新获取的动作呢?
31 楼 whoamiwangwb 2010-08-05  
public class SynchronizedClassHolder {

	public static void main(String[] args) {
		new Thread("thread 1") {
			public void run() {
				new SynchronizedClassHolder().f1();
			}
		}.start();

		new Thread("thread 2") {
			public void run() {
				new SynchronizedClassHolder().f1();
			}
		}.start();
	}

	private static int count = 10;

	public synchronized void f1() {
		if (--count > 0) {
			System.out.println(Thread.currentThread().getName()
					+ "    f1() calling f2() count " + count);
			f2();
		}
	}

	public synchronized void f2() {
		if (--count > 0) {
			System.out.println(Thread.currentThread().getName()
					+ "    f2() calling f1() count " + count);
			f1();
		}
	}
}




显示的结果:


thread 1    f1() calling f2() count 9
thread 2    f1() calling f2() count 8
thread 2    f2() calling f1() count 6
thread 2    f1() calling f2() count 5
thread 2    f2() calling f1() count 4
thread 2    f1() calling f2() count 3
thread 2    f2() calling f1() count 2
thread 1    f2() calling f1() count 7
thread 2    f1() calling f2() count 1



这个怎么解释呢?
jvm 1.6.0.18
30 楼 coreymylife 2010-08-04  
感觉应该是这样的,当执行synchronized方法的时候,先判断该线程是否拥有锁,如果没有锁则获取锁。如果已经拥有了则执行。
29 楼 笑我痴狂 2010-08-04  
我不得不 说一句  

这里的f1调用f2是同一把锁吧   在加锁的f1中调用f2   这把锁根本就未曾释放过  知道运行完  也就是当count<0为止
28 楼 xuyan2680 2010-08-04  
mercyblitz 写道
hardPass 写道
mercyblitz 写道
hardPass 写道
对于单线程来说,synchronized锁是可重入的


重进入是针对多线程的,单线程synchronized还有什么意义?



说实话,我到现在为止,也没有明白,我们常说的 可重入锁 是什么意思?

这个 “重”是指 “多个线程”同时进入同1个锁?这个类似 readLock
还是指 单个线程 同时进入“多个”被“锁”住的代码块? 这个类似synchronized,如果硬要咬文嚼字,这里确实不应该说是“可重入”,
用其他词,好像也不够酷。


”可重入“很形象的,好比方法的递归。如果一个线程获得锁之后,我们常说的synchronized块下的代码块就是临界区,在这个临界区内部所有的方法调用都是同步的。这点不难明白,但是块中方法调用还有synchronized修饰的块或者方法,哪么线程不需要重复获得锁。简单地说,如果一个线程第一次进入了synchronized块,接下来的动作中,线程再次进入synchronized块不会重复获得锁。


正解!
27 楼 mercyblitz 2010-08-04  
lxs647 写道
多次获取同一对象的锁后,释放的时候也是要释放多次??还是一次释放掉所有的对象锁??



只有一个锁,这个锁是线程所有,并不是monitor对象。只会释放一次。
26 楼 lxs647 2010-08-04  
多次获取同一对象的锁后,释放的时候也是要释放多次??还是一次释放掉所有的对象锁??
25 楼 zy2419 2010-08-04  
没有再次持有吧,调用同步方法必须持有此对象锁,而此对象锁已经持有了.
24 楼 rain2005 2010-08-04  
可重入锁ReentrantLock是使用计数器变量,notify  wait实现的。
synchronized没有计数器的概念。
23 楼 hatedance 2010-08-04  
<div class="quote_title">michael.softtech 写道</div>
<div class="quote_div">
<p>我是这样理解的。 Java Monitors Are Reentrant 也就是当前线程如果已经有了一个Object a的锁,那么此线程想再次获取这个a的锁时,可以继续获取。然后在此线程对于该锁的计数上+1. 比如</p>
<p> </p>
<pre name="code" class="java">synchronized  m1(){
//加入此时对锁a的计数是N
m2();  //进入m2的方法体之后锁计数是N+1,离开m2后是N
}
synchronized m2(){}</pre>
<p> 这样就能保证锁的正确获取和释放了。</p>
<p> </p>
<p> 个人理解,仅供参考~</p>
</div>
<p>说的没错。我在《java concurrency in practice》一书里得到了验证:</p>
<p>
</p>
<div class="quote_title">
<span style="font-weight: normal;">《java concurrency in practice》:</span> 写道</div>
<div class="quote_div">2.3.2. Reentrancy<br>When a thread requests a lock that is already held by another thread, the requesting thread blocks. But <strong>because intrinsic locks are reentrant, if a thread tries to acquire a lock that it already holds, the request succeeds.</strong> Reentrancy means that locks are acquired on a <strong>per-thread</strong> rather than <strong>per-invocation</strong> basis. [7] Reentrancy is implemented by associating with each lock an acquisition count and an owning thread. When the count is zero, the lock is considered unheld. When a thread acquires a previously unheld lock, the JVM records the owner and sets the acquisition count to one. If that same thread acquires the lock again, the count is incremented, and when the owning thread exits the synchronized block, the count is decremented. When the count reaches zero, the lock is released.<br>
</div>
 
22 楼 mercyblitz 2010-08-04  
hardPass 写道
mercyblitz 写道
hardPass 写道
对于单线程来说,synchronized锁是可重入的


重进入是针对多线程的,单线程synchronized还有什么意义?



说实话,我到现在为止,也没有明白,我们常说的 可重入锁 是什么意思?

这个 “重”是指 “多个线程”同时进入同1个锁?这个类似 readLock
还是指 单个线程 同时进入“多个”被“锁”住的代码块? 这个类似synchronized,如果硬要咬文嚼字,这里确实不应该说是“可重入”,
用其他词,好像也不够酷。


”可重入“很形象的,好比方法的递归。如果一个线程获得锁之后,我们常说的synchronized块下的代码块就是临界区,在这个临界区内部所有的方法调用都是同步的。这点不难明白,但是块中方法调用还有synchronized修饰的块或者方法,哪么线程不需要重复获得锁。简单地说,如果一个线程第一次进入了synchronized块,接下来的动作中,线程再次进入synchronized块不会重复获得锁。
21 楼 mercyblitz 2010-08-04  
beneo 写道
看了半天原来对象锁是monitor,妈的翻译害死人



Java 1.5以后的Lock实现,确实可以带来一定帮助。较前面的synchronized而言,synchronized可以监视多个monitor对象,当monitor对象释放锁顺序不正确,会发生死锁。而Lock对象,monitor是其本身,相对而言,更容易检测。
20 楼 hardPass 2010-08-04  
mercyblitz 写道
hardPass 写道
对于单线程来说,synchronized锁是可重入的


重进入是针对多线程的,单线程synchronized还有什么意义?



说实话,我到现在为止,也没有明白,我们常说的 可重入锁 是什么意思?

这个 “重”是指 “多个线程”同时进入同1个锁?这个类似 readLock
还是指 单个线程 同时进入“多个”被“锁”住的代码块? 这个类似synchronized,如果硬要咬文嚼字,这里确实不应该说是“可重入”,
用其他词,好像也不够酷。








19 楼 mercyblitz 2010-08-04  
mercyblitz 写道
hardPass 写道
asialee 写道
hardPass 写道
maozj 写道
同一个任务在调用同一个对象上的其他synchronized方法,可以再次获得该对象锁。


是这样的吗?

我一直认为是,已经持有锁了,可以继续执行synchronized方法。
不是你说的“可以再次获得该对象锁”。


应该是采用的是引用计数的方法来实现的课重入。


计数?统计什么?记录下同一个线程 Monitor entered 了几次?
但是,这个计数器有什么用呢?又不是类似readlock的东西,可以多个线程同时持有锁。
而且这个synchronized根本不需要显示的声明释放锁。
所以我认为,即使是jvm底层实现,也没有必要进行计数。


或许吧,可能jvm底层实现,是有计数器,那只是可能,并且与开发人员没有关系,没有必要知道。


synchronized方法和块中,都是开辟了“临界区”,重复进入相同的“临界区”,确实需要计数。当进入时+1,退出是-1。



补充一点,如果synchronized定义的方法是对象方法,哪么锁就在加载this对象上。如果是类方法的话,锁就在该类之上。所谓重进入就是从第一个synchronized开始,不断地进入了synchronized块或者方法,尽管synchronized可以监视不同的monitor。而monitor是用于wait/notify通知机制。
18 楼 beneo 2010-08-04  
看了半天原来对象锁是monitor,妈的翻译害死人

相关推荐

    java多线程的条件对象和锁对象demo

    本示例"java多线程的条件对象和锁对象demo"着重探讨了如何利用锁对象和条件对象来精细控制线程的执行流程。 首先,我们需要了解Java中的锁对象。Java提供了多种类型的锁,其中最基础的是`synchronized`关键字,它...

    快速查找oracle锁对象

    在Oracle数据库管理中,"快速查找Oracle锁对象"是一个关键任务,特别是在处理并发事务和解决性能问题时。当多个用户或进程同时访问同一资源时,可能会出现锁冲突,导致某些事务等待,影响数据库的正常运行。了解如何...

    oracle由于对象被锁住无法编译处理

    总之,“由于对象被锁住无法编译处理”的问题虽然常见,但通过合理利用Oracle提供的工具和适当的应用程序设计,大多数情况下都是可以有效管理和解决的。在处理这类问题时,保持耐心,仔细分析,并采取适当的措施是...

    使用redis做任务队列分发子任务

    3. 广播与订阅:通过发布/订阅模式,可以实现任务的广播,让多个消费者同时处理同一任务,提高任务处理能力。 4. 弹性扩展:随着任务量的增长,可以轻松添加更多工作节点来消费队列中的任务。 5. 锁机制:Redis提供...

    易语言线程互斥对象解决

    线程互斥对象允许我们限制对共享资源的访问,防止多个线程同时访问同一资源,从而避免数据竞争和不一致状态。 1. **线程互斥对象(Mutex)**: 线程互斥对象是一种同步机制,当一个线程获得了Mutex的所有权后,...

    从0开始开发 基础库(配置文件读写、日志、多线程、多进程、锁、对象引用计数、内存池、免锁消息队列、免锁数据缓冲区、进.zip

    本压缩包“从0开始开发 基础库”涵盖了多个重要的编程概念和技术,包括配置文件读写、日志记录、多线程与多进程操作、同步机制如锁、对象引用计数、内存池以及两种优化数据结构——免锁消息队列和免锁数据缓冲区。...

    vc++中的线程锁(线程锁保持线程同步)

    线程锁就是用来解决这个问题的一种工具,它允许我们限制对特定资源的访问,确保在同一时间只有一个线程可以访问和修改该资源。 VC++中实现线程锁的方法多种多样,包括临界区(Critical Section)、互斥量(Mutex)...

    oracle锁表查询oracle锁表查询oracle锁表查询

    通过锁机制,Oracle能够确保数据的一致性和完整性,避免多用户操作时可能出现的数据冲突问题。 #### 二、锁表查询方法 ##### 1. 使用`V$LOCKED_OBJECT`视图进行锁表查询 在Oracle数据库中,可以通过查询`V$LOCKED...

    多线程同时查询同一数据库对比

    在这个场景下,我们将比较单线程和多线程查询同一数据库的性能差异。 首先,单线程是指程序按照顺序执行任务,一次只有一个任务在运行。在查询数据库时,如果使用单线程,那么每次只能进行一个查询操作,即使数据库...

    SQL server锁的机制

    SQL Server的并发控制策略基于行级锁和页级锁,允许在一定程度上的并行操作。然而,当写操作与读操作冲突时,锁机制会确保数据的一致性,可能会导致阻塞。例如,一个连接在修改数据时,其他连接会被阻止读取或修改...

    并发编程面试题汇总.docx并发编程是指在一个程序中同时执行多个独立的任务或操作的能力 在面试中,常常会问到与并发编程相关的问题

    - **字节码指令**:被`synchronized`修饰的代码块会在编译后生成`monitorenter`和`monitorexit`字节码指令,用于控制对象锁的获取和释放。 - **对象锁**:`Synchronized`通过对象锁来控制对共享资源的访问。 - **...

    RTT-Mini-mutex.rar

    - **非递归**:常规的互斥锁支持递归,即同一个任务可以多次获取同一把锁而不阻塞自己。但这个简易互斥锁不允许,如果一个任务已经持有了锁,再次尝试获取将导致错误。 3. **简易互斥锁的实现细节** - **数据结构...

    基于C++的任务管理器代码.zip

    多态则允许不同的对象对同一消息做出不同的响应,提供更强大的灵活性。 - **模板与泛型编程**:C++的模板可以创建泛型函数和类,使得代码更具通用性,适应不同数据类型的处理。 2. **进程管理** - **进程状态**:...

    一家三口共用同一账户银行卡,wait();notify();

    当一个线程调用wait()时,它会释放对象锁并进入等待状态,直到其他线程调用同一对象的notify()或notifyAll()方法唤醒它。notify()只会唤醒一个等待的线程,而notifyAll()会唤醒所有等待的线程。 3. **synchronized ...

    等待机制与锁机制wait notify

    需要注意的是,`wait()`、`notify()`和`notifyAll()`的调用者应当是持有对象锁的线程,否则会出现异常。此外,为了防止死锁和饥饿现象,合理地设计同步策略和唤醒逻辑至关重要。 总结来说,`wait`、`notify`和`...

    多线程使用同一数组测试

    例如,在VB.NET中,可以将数组作为锁对象,确保同一时间只有一个线程能够访问它。 ```vbnet SyncLock myArray ' 这里进行数组操作 End SyncLock ``` 2. **线程局部存储(Thread Local Storage)**:如果可能,...

    基于分布式锁或xxx-job实现分布式任务调度.zip

    分布式锁是确保在分布式环境中同一时刻只有一个实例执行特定任务的关键技术。它可以防止并发操作导致的数据不一致性和资源争抢。本项目可能使用了如ZooKeeper、Redis或Apache Curator等工具来实现分布式锁。这些工具...

    浅谈iOS中的锁的介绍及使用

    在示例中,`@synchronized(cjobj)` 创建了一个基于 `cjobj` 对象的锁,两个并发的异步任务会根据这个锁进行同步。如果两个任务使用了不同的锁标识(如将 `cjobj` 更改为 `self`),则不会产生阻塞效果,因为它们实际...

    Java 同步锁 wait notify 学习心得

    `notify()`方法随机唤醒正在等待该对象锁的线程之一。被唤醒的线程将有机会重新获取锁并继续执行。然而,`notify()`并不保证被唤醒的线程会立即执行,因为线程调度取决于操作系统的线程调度策略。 #### `notifyAll...

    [PHP] 基于redis的分布式锁防止高并发重复请求.docx

    * 可重入性:同一对象(如线程、类)可以重复、递归调用该锁而不发生死锁; * 可阻塞:在没有获得锁之前,只能阻塞等待直至获得锁; * 高可用:哪怕发生程序故障、机器损坏,锁仍然能够得到被获取、被释放; * 高...

Global site tag (gtag.js) - Google Analytics