Lock与synchronized是不一样的

原创 2015年11月17日 23:17:18

很多编码者都会说,Lock类和synchronized关键字用在代码块的并发性和内存上时语义 是一样的,都是保持代码块同时只有一个线程具有执行权。这样的说法只对了一半,我们以一个任务提交给多个线程运行为例,来看看使用显式锁(Lock类)和内部锁(synchronized 关键字)有什么不同。首先定义一个任务:

class Task{
	public void doSomething(){
		try {
			//每个线程等待2秒钟,注意将此时的线程转为WAITING状态
			Thread.sleep(2000);
		} catch (Exception e) {
			// TODO: handle exception
		}
		StringBuffer sb=new StringBuffer();
		sb.append("线程名称是:"+Thread.currentThread().getName());
		sb.append(",执行时间是:"+Calendar.getInstance().get(13)+"s");
		System.out.println(sb);
		
	}
}

该类模拟了一个执行时间比较长的计算,注意这里使用的是模拟方式,在使用sleep 方法时线程的状态会从运行状态转变为等待状态。该任务要具备多线程能力时必须实现Rmmable接口,我们分别建立两种不同的锁实现机制,首先看

//显式销实现 //一个线程一个锁  锁不共享  不会互斥
class TaskWithLock extends Task implements Runnable{
	//声明显式锁
	final Lock lock=new ReentrantLock();//一个线程一个锁  锁不共享  不会互斥
	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			//开始锁定
			lock.lock();
			doSomething();
		} catch (Exception e) {
			// TODO: handle exception
		}finally{
			//释放销
			lock.unlock();
		}
	}
	
}


这里有一点需要说明的是,显式锁的锁定和释放必须在一个try……finally块中,这是 为了确保即使出现运行期异禽也能正常释放锁,保证其他线程能够顺利执行。

内部锁的处理也非常简单,代码如下:

//内部<span style="font-size:18px;">锁</span>任务
class TaskWithSync extends Task implements Runnable{

	@Override
	public void run() {
		// TODO Auto-generated method stub
		//内部锁
		synchronized ("A") {//跟随类 类的  所有对象共享
			doSomething();
		}
	}
	
}


这两个任务看着非常相似,应该能够产生相似的结果吧?我们建立一个模拟场景,保证同时有三个线程在运行,代码如下:

public static void runTasks(Class <? extends Runnable> clz) throws Exception{
	ExecutorService es=Executors.newCachedThreadPool();
	System.out.println("***开始执行"+clz.getSimpleName()+"任务***");
	//启动三个线程
	for(int i=0;i<3;i++){
		es.submit(clz.newInstance());
	}
	//等待足够长的时间,然后关闭执行器
	TimeUnit.SECONDS.sleep(10);
	System.out.println("-----"+clz.getSimpleName()+"任务执行完毕------\n");
	es.shutdown();
}
	
	public static void main(String[] args) throws Exception {
		//运行显式销任务
		runTasks(TaskWithLock.class);
		  //运行内部销任务
		runTasks(TaskWithSync.class);
		
}


按照一般的理解,Lock和synchronized的处理方式是相同的,输出应该没有差别,但是 很遗憾的是,输出差别其实很大。输出如下:

***** 开始执行TaskWithLock 任务 ******

线程名称:pool-1-thread-l,执行时间:33 s 线程名称:pool-1-thread-2,执行时间:33 a 线程名称:pool-1-thread-3,执行时间:33 s

----- TaskWithLock任务执行完毕-

***** 开始执行 TaskWithSync任务 ******

线程名称pool-2-thread-l,执行时间:43 s 线程名称:pool-2-thread-3,执行时间:45 s 线程名称:pool-2-thread-2,执行时间:47 s

----- TaskWithSync任务执行完毕-

注意看运行的时间戰,显式锁是同时运行的,很显然在pool-1-thread-l线程执行到 sleep时,其他两个线程也会运行到这里,一起等待,然后一起输出,这还具有线程互斥的槪念吗?

而内部锁的输出则是我们的预期结果:pool-2-thread-l线程在运行时其他线程处于等待 状态,pool-2-thread-l执行完毕后,JVM从等待线程池中随机获得一个线程pol-2-thread-3 执行,最后再执行pool-2-thread-2,这正是我们希望的。

现在问题来了: Lock锁为什么不出现互斥情况呢?

这是因为对于同步资源来说(示例中是代码块),显式锁是对象级别的锁,而内部锁是类级别的锁,也就是说Lock锁是跟随对象的,synchronized锁是跟随类的,更简单地说把 Lock定义为多线程类的私有属性是起不到资源互斥作用的,除非是把Lock定义为所有线程 的共享变量。都说代码是最好的解释语言,我们来看一个Lock锁资源的代码:

	public static void main(String[] args) throws Exception {	
		
		//多个线程共享锁
		final Lock lock=new ReentrantLock();
		//启动三个线程
		for(int i=0;i<3;i++){
			new Thread(new Runnable() {
				
				@Override
				public void run() {
					// TODO Auto-generated method stub
					try {
						lock.lock();
						Thread.sleep(2000);
						System.out.println(Thread.currentThread().getName());
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}finally{
						lock.unlock();
					}
				}
			}).start();
		}
	}


读者可以执行一下,会发现线程名称Thread-0、Thread-1、Thread-2会逐渐输出,也就 是一个线程在执行时,其他线程就处于等待状态。注意,这里三个线程运行的实例对象是同一个类(都是Client$l类的实例)。

那除了这一点不同之外,显式锁和内部锁还有什么不同呢?还有以下4点不同:

(1) Lock支持更细粒度的锁控制

假设读写锁分离,写操作时不允许有读写操作存在,而读操作时读写可以并发执行,这一点内部锁就很难实现。显式锁的示例代码如下:

class Foo{

   //可重入的读写锁

    private  final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

   //读锁

   privatefinal Lock r = rwl. readLock ();

   //写锁

   private final Lock w = rwl.writeLockO;

   //多操作,可并发执行

   publicvoid read() {

     try {

         r.lock() ;

        Thread.sleep(1000);

        System.out.println("read....)

   }catch (InterruptedException e) {

        e.printStackTrace();

   } finally {

       r.unlock();
   }
}
 //写操作,同时只允许一个写操作

  public void write{Object _obj) { 
       try {

          w.lock();

          Thread.sleep{1000);

          System, out .printIn (“Writing   ");

      } catch(InterruptedException e) {
           e.printStackTrace();

      } finally {

         w. unlock();

      }

   }

}


可以编写一个Runnable的实现类,把Foo类作为资源进行调用(注意多线程是共享这 个资源的),然后就会发现这样的现象:读写锁允许同时有多个读操作但只允许有一个写操作,也就是当有一个写线程在执行时,所有的读线程和写线程都会阻塞,直到写线程释放锁 资源为止,而读锁则可以有多个线程同时执行。

(2)  Lock是无阻塞锁,synchronized是阻塞锁

当线程A持有锁时,线程B也期望获得锁,此时,如果程序中使用的是显式锁,则B 线程为等待状态(在通常的描述中,也认为此线程被阻塞了),若使用的是内部锁则为阻塞状态。

(3)  Lock可实现公平锁,synchronized只能是非公平锁

什么叫非公平锁呢?当一个线程A持有锁,而线程B、C处于阻塞(或等待)状态时, 若线程A释放锁,JVM将从线程B、C中随机选择一个线程持有锁并使其获得执行权,这叫 做非公平锁(因为它抛弃了先来后到的顺序);若JVM选择了等待时间最长的一个线程持有 锁,则为公平锁(保证每个线程的等待时间均衡)。需要注意的是,即使是公平锁,JVM也 无法准确做到“公平”,在程序中不能以此作为精确计算。

显式锁默认是非公平锁,但可以在构造函数中加入参数true来声明出公平锁,而 synchronized实现的是非公平锁,它不能实现公平锁。

(4) Lock是代码级的,synchronized是JVM级的

Lock是通过编码实现的,synchronized是在运行期由JVM解释的,相对来说

synchronized的优化可能性更髙,毕竟是在最核心部分支持的,Lock的优化则需要用户自行

考虑。

显式锁和内部锁的功能各不相同,在性能上也稍有差别,但随着JDK的不断推进,相 对来说,显式锁使用起来更加便利和强大,在实际开发中选择哪种类型的锁就需要根据实际情况考虑了:灵活、强大则选择Lock,快捷、安全则选择synchronized。

注意两种不同的锁机制,根据不同的情况来选择。

Java多线程中 synchronized和Lock的区别

在上一节中, 我们已经了解了Java多线程编程中常用的关键字synchronized,以及与之相关的对象锁机制。这一节中,让我们一起来认识JDK 5中新引入的并发框架中的锁机制。 我想很多购买...
  • lingzhm
  • lingzhm
  • 2015年04月08日 21:28
  • 10804

java并发之Lock与synchronized的区别

1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;   2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致...
  • gongpulin
  • gongpulin
  • 2016年04月21日 19:22
  • 3266

java并发编程---synchronized和lock两种锁的比较

性能比较     在JDK1.5中,synchronized是性能低效的。因为这是一个重量级操作,它对性能最大的影响是阻塞的是实现,挂起线程和恢复线程的操作都需要转入内核态中完成,这些操作给系统的并...
  • u012470138
  • u012470138
  • 2016年12月06日 11:27
  • 2657

“建议127:Lock与synchronized是不一样的问题”实际验证

最近重新翻看 “编写高质量代码:改善Java程序的151个建议” 一书的时候看到“建议127”的文章中作者提供的测试用例存在一些值得商榷的地方。...
  • RoadE
  • RoadE
  • 2014年10月23日 13:11
  • 655

Java多线程简析——Synchronized(同步锁)、Lock以及线程池

Java多线程 Java中,可运行的程序都是有一个或多个进程组成。进程则是由多个线程组成的。 最简单的一个进程,会包括mian线程以及GC线程。 线程的状态 线程状态由以下一张网上图片来说明: 在...
  • yangzhaomuma
  • yangzhaomuma
  • 2016年04月25日 00:56
  • 16065

Java并发编程:Lock类和synchronized关键字区别

以下是本文目录大纲:   一.synchronized的缺陷   二.java.util.concurrent.locks包下常用的类   三.锁的相关概念介绍   若有不正之处请多多谅解,并...
  • Hi_TYSONZHANG
  • Hi_TYSONZHANG
  • 2017年11月22日 17:06
  • 70

隐式锁 Synchronized 与显示锁 Lock的用法和简单对比

SynchronizedSynchronized是Java的关键字,当它用来修饰一个方法或一个代码块时,能够保证在同一时刻最多只有一个线程执行该代码。因为当调用Synchronized修饰的代码时,并...
  • u011519624
  • u011519624
  • 2017年03月12日 19:56
  • 749

synchronized关键字与Lock锁机制的区别问题

synchronized关键字与Lock锁机制的区别
  • a2279860a
  • a2279860a
  • 2017年01月19日 16:22
  • 898

java多线程中 synchronized 和 Lock的区别

今天复习了一下多线程的知识,顺便总结了一下synchronized和lock的区别,这里重点只讲他们的区别。首先lock是java.util.concurrent类库中的类的对象(lock有读写锁,可...
  • xcxy2015
  • xcxy2015
  • 2017年10月17日 22:03
  • 50

详解synchronized与Lock的区别与使用

最近在看Java多线程这一大块,所以在CSDN上找了很多文章。今天看到synchronized和lock的用法,看了几篇文章,感觉这篇讲的挺好,就转载过来。 原文地址:http://blog.csd...
  • qpzkobe
  • qpzkobe
  • 2017年11月20日 20:58
  • 76
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Lock与synchronized是不一样的
举报原因:
原因补充:

(最多只允许输入30个字)