对于稍微有点经验的.NET开发人员来说,倘若被问及如何保持线程同步,我想很多人都能说好好几种。在众多的线程同步的可选方式中,加锁无疑是最为常用的。如果仅仅是基于方法级别的线程同步,使用System.Runtime.CompilerServices.MethodImplAttribute无疑是最为简洁的一种方式。MethodImplAttribute可以用于instance method,也可以用于static method。当在某个方法上标注了MethodImplAttribute,并指定MethodImplOptions.Synchronized参数,可以确保在不同线程中运行的该方式以同步的方式运行。我们几天来讨论MethodImplAttribute(MethodImplOptions.Synchronized)和lock的关系。
一、提出结论
在进行讨论之前,我先提出下面3个结论:
- [MethodImplAttribute(MethodImplOptions.Synchronized)]仍然采用加锁的机制实现线程的同步。
- 如果[MethodImplAttribute(MethodImplOptions.Synchronized)]被应用到instance method,相当于对当前实例加锁。
- 如果[MethodImplAttribute(MethodImplOptions.Synchronized)]被应用到static method,相当于当前类型加锁
二、基于instance method的线程同步
为了验证我们上面提出的结论,我作了一个小小的例子。在一个console application中定义了一个class:SyncHelper,其中定义了一个方法Execute。打印出方法执行的时间,并休眠当前线程模拟一个耗时的操作:
1: class SyncHelper
<!--CRLF-->
2: {
<!--CRLF-->
3: public void Execute()
<!--CRLF-->
4: {
<!--CRLF-->
5: Console.WriteLine("Excute at {0}", DateTime.Now);
<!--CRLF-->
6: Thread.Sleep(5000);
<!--CRLF-->
7: }
<!--CRLF-->
8: }
<!--CRLF-->
在入口Main方法中,创建SyncHelper对象,通过一个System.Threading.Timer对象实现每隔1s调用该对象的Execute方法:
1: class Program
<!--CRLF-->
2: {
<!--CRLF-->
3: static void Main(string[] args)
<!--CRLF-->
4: {
<!--CRLF-->
5: SyncHelper helper = new SyncHelper();
<!--CRLF-->
6: Timer timer = new Timer(
<!--CRLF-->
7: delegate
<!--CRLF-->
8: {
<!--CRLF-->
9: helper.Execute();
<!--CRLF-->
10: }, null, 0, 1000);
<!--CRLF-->
11:
<!--CRLF-->
12: Console.Read();
<!--CRLF-->
13:
<!--CRLF-->
14: }
<!--CRLF-->
15: }
<!--CRLF-->
16:
<!--CRLF-->
由于Timer对象采用异步的方式进行调用,所以虽然Execute方法的执行时间是5s,但是该方法仍然是每隔1s被执行一次。这一点从最终执行的结果可以看出:
为了让同一个SyncHelper对象的Execute方法同步执行,我们在Execute方法上添加了如下一个MethodImplAttribute:
1: [MethodImpl(MethodImplOptions.Synchronized)]
<!--CRLF-->
2: public void Execute()
<!--CRLF-->
3: {
<!--CRLF-->
4: Console.WriteLine("Excute at {0}", DateTime.Now);
<!--CRLF-->
5: Thread.Sleep(5000);
<!--CRLF-->
6: }
<!--CRLF-->
从如下的输出结果我们可以看出Execute方法是以同步的方式执行的,因为两次执行的间隔正式Execute方法执行的时间:
在一开始我们提出的结论中,我们提到“如果[MethodImplAttribute(MethodImplOptions.Synchronized)]被应用到instance method,相当于对当前实例加锁”。说得直白一点:[MethodImplAttribute(MethodImplOptions.Synchronized)] = lock(this)。我们可以通过下面的实验验证这一点。为此,在SyncHelper中定义了一个方法LockMyself。在此方法中对自身加锁,并持续5s中,并答应加锁和解锁的时间。
1: public void LockMyself()
<!--CRLF-->
2: {
<!--CRLF-->
3: lock (this)
<!--CRLF-->
4: {
<!--CRLF-->
5: Console.WriteLine("Lock myself at {0}", DateTime.Now);
<!--CRLF-->
6: Thread.Sleep(5000);
<!--CRLF-->
7: Console.WriteLine("Unlock myself at {0}", DateTime.Now);
<!--CRLF-->
8: }
<!--CRLF-->
9: }
<!--CRLF-->
我们在Main()中以异步的方式(通过创建新的线程的方式)调用该方法:
1: static void Main(string[] args)
<!--CRLF-->
2: {
<!--CRLF-->
3: SyncHelper helper = new SyncHelper();
<!--CRLF-->
4:
<!--CRLF-->
5: Thread thread = new Thread(
<!--CRLF-->
6: delegate()
<!--CRLF-->
7: {
<!--CRLF-->
8:
<!--CRLF-->
9: helper.LockMyself();
<!--CRLF-->
10:
<!--CRLF-->
11: });
<!--CRLF-->
12: thread.Start();
<!--CRLF-->
13: Timer timer = new Timer(
<!--CRLF-->
14: delegate
<!--CRLF-->
15: {
<!--CRLF-->
16: helper.Execute();
<!--CRLF-->
17: }, null, 0, 1000);
<!--CRLF-->
18:
<!--CRLF-->
19: Console.Read();
<!--CRLF-->
20: }
<!--CRLF-->
结合我们的第二个结论想想最终的输出会是如何。由于LockMyself方法是在另一个线程中执行,我们可以简单讲该方法的执行和Execute的第一个次执行看作是同时的。但是MethodImplAttribute(MethodImplOptions.Synchronized)]果真是通过lock(this)的方式实现的话,Execute必须在等待LockMyself方法执行结束将对自身的锁释放后才能得以执行。也就是说LockMyself和第一次Execute方法的执行应该相差5s。而输出的结果证实了这点:
三、基于static method的线程同步
讨论完再instance method上添加MethodImplAttribute(MethodImplOptions.Synchronized)]的情况,我们相同的方式来讨论倘若一样的MethodImplAttribute被应用到static方法,又会使怎样的结果。
我们先将Execute方法上的MethodImplAttribute注释掉,并将其改为static方法:
1: //[MethodImpl(MethodImplOptions.Synchronized)]
<!--CRLF-->
2: public static void Execute()
<!--CRLF-->
3: {
<!--CRLF-->
4: Console.WriteLine("Excute at {0}", DateTime.Now);
<!--CRLF-->
5: Thread.Sleep(5000);
<!--CRLF-->
6: }
<!--CRLF-->
在Main方法中,通过Timer调用该static方法:
1: static void Main(string[] args)
<!--CRLF-->
2: {
<!--CRLF-->
3: Timer timer = new Timer(
<!--CRLF-->
4: delegate
<!--CRLF-->
5: {
<!--CRLF-->
6: SyncHelper.Execute();
<!--CRLF-->
7: }, null, 0, 1000);
<!--CRLF-->
8:
<!--CRLF-->
9: Console.Read();
<!--CRLF-->
10: }
<!--CRLF-->
毫无疑问,Execute方法将以1s的间隔异步地执行,最终的输出结果如下:
然后我们将对[MethodImpl(MethodImplOptions.Synchronized)]的注释取消:
1: [MethodImpl(MethodImplOptions.Synchronized)]
<!--CRLF-->
2: public static void Execute()
<!--CRLF-->
3: {
<!--CRLF-->
4: Console.WriteLine("Excute at {0}", DateTime.Now);
<!--CRLF-->
5: Thread.Sleep(5000);
<!--CRLF-->
6: }
<!--CRLF-->
最终的输出结果证实了Execute将会按照我们期望的那样以同步的方式执行,执行的间隔正是方法执行的时间:
我们回顾一下第三个结论:“如果[MethodImplAttribute(MethodImplOptions.Synchronized)]被应用到static method,相当于当前类型加锁”。为了验证这个结论,在SyncHelper中添加了一个新的static方法:LockType。该方法对SyncHelper tpye加锁,并持续5s中,在加锁和解锁是打印出当前时间:
1: public static void LockType()
<!--CRLF-->
2: {
<!--CRLF-->
3: lock (typeof(SyncHelper))
<!--CRLF-->
4: {
<!--CRLF-->
5: Console.WriteLine("Lock SyncHelper type at {0}", DateTime.Now);
<!--CRLF-->
6: Thread.Sleep(5000);
<!--CRLF-->
7: Console.WriteLine("Unlock SyncHelper type at {0}", DateTime.Now);
<!--CRLF-->
8: }
<!--CRLF-->
9: }
<!--CRLF-->
在Main中,像验证instance method一样,创建新的线程执行LockType方法:
1: static void Main(string[] args)
<!--CRLF-->
2: {
<!--CRLF-->
3: Thread thread = new Thread(
<!--CRLF-->
4: delegate()
<!--CRLF-->
5: {
<!--CRLF-->
6: SyncHelper.LockType();
<!--CRLF-->
border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12
分享到:
相关推荐
《深入Synchronized与java.util.concurrent.locks.Lock的区别详解》 Synchronized和java.util.concurrent.locks.Lock都是Java中用于实现线程同步的关键字和接口,它们的主要目标是保证多线程环境下的数据一致性与...
Java 中的 Lock 和 Synchronized 的区别 Java 语言中有很多相似关键字或相似意义的字,但 lock 和 synchronized 是两个最容易混淆的关键字。它们都是锁的意思,都是为了线程安全性、应用合理性和运行效率的。下面...
synchronized.pdf
简述synchronized和java.util.concurrent.locks.Lock的异同 ?(15分) 6.EJB规范规定EJB中禁止的操作有哪些?(15分) 最后还有一题考考你的眼力: public String toString(){ return this + "@" + this....
【B.Synchronized DMS-开源】是一个创新的项目,它代表了汽车行业管理系统的重大进步,特别是在经销商管理领域。作为首个开源的经销商管理系统,这个平台旨在为汽车经销商提供一个高度可定制且功能强大的解决方案,...
了解synchronized和lock的区别 synchronized是Java语言中的一个关键字,用于线程同步,主要用于解决多线程之间的竞争问题。它可以将某个方法或代码块锁定,使得只有一个线程可以执行该方法或代码块,其他线程只能...
通过对AQS的剖析,我们将揭示ReentrantLock如何实现其独特的特性和功能,以及它与synchronized关键字的异同。 ReentrantLock是Java.util.concurrent包下的一个类,它是基于AQS框架实现的线程同步机制。与...
lock.lock(); try { // 执行需要同步的代码 System.out.println("执行同步代码"); } finally { lock.unlock(); } } } ``` #### 六、Lock接口提供的synchronized不具备的主要特性 1. **可操作性**:Lock...
3. 锁住整个对象:当`synchronized`后面跟的是`this`或者类实例时,锁住的是整个对象,不允许其他线程同时访问对象的所有`synchronized`方法和代码块。 二、Lock接口 Lock接口提供了更灵活的锁控制,比`...
2. 锁机制可以通过 synchronized、ReentrantLock、Lock 等方式实现。 3. 锁机制可以分为悲观锁和乐观锁两种。 并发编程的应用 1. 并发编程广泛应用于服务器端编程、移动应用开发、游戏开发等领域。 2. 并发编程...
Java 编程 synchronized 与 Lock 的区别 synchronized 和 Lock 是 Java 编程中两种常用的同步机制,用于实现线程安全的访问。两者都可以实现同步访问,但是它们有着不同的设计理念和使用场景。 synchronized 的...
2. 每个对象只有一个锁(lock)与之相关联。 3. 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步掌握。 synchronized 关键字的作用域 synchronized 关键字的作用域有两种: 1. ...
# synchronized锁与lock锁的对比 Lock是显式锁,需要手动的开启和关闭,synchronized锁是隐式锁,只要出了作用域就会自动释放。Lock只有代码块锁,synchronized既有代码块锁还有方法锁。 使用Lock锁,JVM将花费较...
Java synchronized 学习 Java 中的 synchronized 关键字是用来实现线程同步的,它可以用来修饰方法、代码块和静态方法,以确保在多线程环境下数据的一致性。 一、进程和线程的区别 在计算机中,每个运行着的 xxxx...
"Synchronized与Lock"这个主题探讨了两种主要的同步机制:synchronized关键字和Lock接口(包括其实现类如ReentrantLock)。这两种机制都用于实现线程间的互斥访问,但它们在功能、灵活性和性能上有所差异。 首先,...
了解 JVM 锁机制中的 synchronized 和 Lock 实现原理 在 Java 中,锁机制是数据同步的关键,存在两种锁机制:synchronized 和 Lock。了解这两种锁机制的实现原理对于理解 Java 并发编程非常重要。 synchronized 锁...
Java 为程序加锁的方式主要有两种:synchronized 与 Lock。 1. synchronized 可以修饰的作用域如下: - 非静态方法(加的锁为对象锁); - 静态方法(加的锁为类锁); - 代码块(对象锁与类锁均可); 2. Lock ...
Java 并发编程 Synchronized 关键字实现原理 Synchronized 关键字是 Java 并发编程中最基本的同步机制,它可以保证线程安全,包括原子性、可见性和有序性。Synchronized 关键字可以修饰方法或代码块,使得在同一...
在多线程编程中,确保线程安全是至关重要的,特别是在Java中,有两种主要的同步机制:`synchronized`和`Lock`。本文将详细解释这两种机制以及它们的基础概念。 首先,我们需要理解什么是同步和临界资源。同步是指在...