回顾一下上次,我们讨论了lock/AutoResetEvent/ManualResetEvent以及Semaphore。这些用于线程同 步的结构叫做同步基元。同步基元从类型上可以分为锁定/通知/联锁三种。lock显然锁定方式,而且是独 占锁定,也就是在锁释放之前不能由其它线程获得。 Semaphore也是一种锁定,只不过不是独占锁,可以 指定多少个线程访问代码块。AutoResetEvent和ManualResetEvent当然就是通知方式了,前者在通行之后 自动重置,后者需要手动重置。我们还看到了即使使用同步机制不一定能确保线程按照我们规划的去执行 ,因为从根本上来说,操作系统的线程调度我们是没有办法预测的,除非使用阻塞或锁等待等方式,否则 我们很难去预测两个无关的线程究竟哪个先得到执行(即使设置了优先级),而且在使用这些同步机制的 时候我们也要考虑到性能问题,如果多线程程序做的不好的话很可能会比单线程执行效率还低,比如我们 开启了多个线程相互阻塞等待并没有任何的并行运算,比如在一个多线程环境汇总我们锁的范围很大,导 致多线程环境变为了一个单线程环境,有关性能问题以后再讨论,这次我们来看看其它的一些同步基元。
本文的例子基于上文定义的一些基本静态对象:
static int result = 0;
static object locker = new object();
static EventWaitHandle are = new AutoResetEvent(false);
static EventWaitHandle mre = new ManualResetEvent(false);
使用lock保护共享资源不被多个线程同时修改是常见的做法,其实lock本质上基于Monitor,而使用 Monitor本身可以带来更丰富的特性,比如可以设置超过某个等待时间段就不继续等待:
for (int i = 0; i < 10; i++)
{
new Thread(() =>
{
if (Monitor.TryEnter(locker, 2000))
{
Thread.Sleep(1000);
Console.WriteLine(DateTime.Now.ToString("mm:ss"));
Monitor.Exit(locker);
}
}).Start();
}
在这段代码中我们开启10个线程尝试申请locker独占锁,通过输出结果可以看出,由于我们设置了2秒 超时,程序只输出了三次:
计算(四)线程同步基础 下-java 多线程并行执行">
在第一个线程获取锁之后,一秒后释放,第二个线程获取,一秒后又释放,第三个线程最后获取到, 之后的线程都超过了2秒等待时间,TryEnter返回false,线程结束。