Java类锁和对象锁实践

简介:

感谢[jiehao]同学的投稿,投稿可将文章发送到tengfei@ifeve.com

类锁和对象锁是否会冲突?对象锁和私有锁是否会冲突?通过实例来进行说明。

一、相关约定

为了明确后文的描述,先对本文涉及到的锁的相关定义作如下约定:

1. 类锁:在代码中的方法上加了static和synchronized的锁,或者synchronized(xxx.class)的代码段,如下文中的increament();

2.对象锁:在代码中的方法上加了synchronized的锁,或者synchronized(this)的代码段,如下文中的synOnMethod()和synInMethod();

3.私有锁:在类内部声明一个私有属性如private Object lock,在需要加锁的代码段synchronized(lock),如下文中的synMethodWithObj()。

二、测试代码

1.编写一个启动类ObjectLock


public class ObjectLock {
	public static void main(String[] args) {
		System.out.println("start time = " + System.currentTimeMillis()+"ms");
		LockTestClass test = new LockTestClass();
		for (int i = 0; i < 3; i++) {
			Thread thread = new ObjThread(test, i);
			thread.start();
		}
	}
}


2.编写一个线程类ObjThread,用于启动同步方法(注意它的run方法可能会调整以进行不同的测试)


public class ObjThread extends Thread {
	LockTestClass lock;
	int i = 0;

	public ObjThread(LockTestClass lock, int i) {
		this.lock = lock;
		this.i = i;
	}

	public void run() {
		//无锁方法
//		lock.noSynMethod(this.getId(),this);
		//对象锁方法1,采用synchronized synInMethod的方式
		lock.synInMethod();
		//对象锁方法2,采用synchronized(this)的方式
//		lock.synOnMethod();
		//私有锁方法,采用synchronized(object)的方式
//		lock.synMethodWithObj();
		//类锁方法,采用static synchronized increment的方式
		LockTestClass.increment();
	}
}


3.再编写一个锁的测试类LockTestClass,包括各种加锁方法


public class LockTestClass {
	//用于类锁计数
	private static int i = 0;
    //私有锁
	private Object object = new Object();

	/**
	 * <p>
	 * 无锁方法
	 *
	 * @param threadID
	 * @param thread
	 */
	public void noSynMethod(long threadID, ObjThread thread) {
		System.out.println("nosyn: class obj is " + thread + ", threadId is"
				+ threadID);
	}

	/**
	 * 对象锁方法1
	 */
	public synchronized void synOnMethod() {
		System.out.println("synOnMethod begins" + ", time = "
				+ System.currentTimeMillis() + "ms");
		try {
			Thread.sleep(2000L);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("synOnMethod ends");
	}

	/**
	 * 对象锁方法2,采用synchronized (this)来加锁
	 */
	public void synInMethod() {
		synchronized (this) {
			System.out.println("synInMethod begins" + ", time = "
					+ System.currentTimeMillis() + "ms");
			try {
				Thread.sleep(2000L);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("synInMethod ends");
		}

	}

	/**
	 * 对象锁方法3
	 */
	public void synMethodWithObj() {
		synchronized (object) {
			System.out.println("synMethodWithObj begins" + ", time = "
					+ System.currentTimeMillis() + "ms");
			try {
				Thread.sleep(2000L);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("synMethodWithObj ends");
		}
	}

	/**
	 * 类锁
	 */
	public static synchronized void increament() {
		System.out.println("class synchronized. i = " + i + ", time = "
				+ System.currentTimeMillis() + "ms");
		i++;
		try {
			Thread.sleep(2000L);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		 System.out.println("class synchronized ends.");
	}

}


三、测试结果

1.测试类锁和对象锁,ObjectThread的run方法修改如下:


public void run() {
		//无锁方法
//		lock.noSynMethod(this.getId(),this);
		//对象锁方法1,采用synchronized synInMethod的方式
		lock.synInMethod();
		//对象锁方法2,采用synchronized(this)的方式
//		lock.synOnMethod();
		//私有锁方法,采用synchronized(object)的方式
//		lock.synMethodWithObj();
		//类锁方法,采用static synchronized increment的方式
		LockTestClass.increament();
	}


终端输出:


start time = 1413101360231ms
synInMethod begins, time = 1413101360233ms
synInMethod ends
class synchronized. i = 0, time = 1413101362233ms
synInMethod begins, time = 1413101362233ms
class synchronized ends.
synInMethod ends
class synchronized. i = 1, time = 1413101364233ms
synInMethod begins, time = 1413101364233ms
class synchronized ends.
synInMethod ends
class synchronized. i = 2, time = 1413101366234ms
class synchronized ends.


可以看到对象锁方法(synInMothod)第一次启动时比类锁方法(increament)快2秒,这是因为在synInMehtod执行时sleep了2秒再执行的increament,而这两个方法共用一个线程,所以会慢2秒,如果increament在run中放到synInMethod前面,那么第一次启动时就是increament快2秒。
而当类锁方法启动时,另一个线程时的对象锁方法也几乎同时启动,说明二者使用的并非同一个锁,不会产生竞争。
结论:类锁和对象锁不会产生竞争,二者的加锁方法不会相互影响。

2.私有锁和对象锁,ObjectThread的run方法修改如下:


public void run() {
		//无锁方法
//		lock.noSynMethod(this.getId(),this);
		//对象锁方法1,采用synchronized synInMethod的方式
		lock.synInMethod();
		//对象锁方法2,采用synchronized(this)的方式
//		lock.synOnMethod();
		//私有锁方法,采用synchronized(object)的方式
		lock.synMethodWithObj();
		//类锁方法,采用static synchronized increment的方式
//		LockTestClass.increament();
	}


终端输出:


start time = 1413121912406ms
synInMethod begins, time = 1413121912407ms.
synInMethod ends.
synMethodWithObj begins, time = 1413121914407ms
synInMethod begins, time = 1413121914407ms.
synInMethod ends.
synMethodWithObj ends
synInMethod begins, time = 1413121916407ms.
synMethodWithObj begins, time = 1413121916407ms
synInMethod ends.
synMethodWithObj ends
synMethodWithObj begins, time = 1413121918407ms
synMethodWithObj ends


和类锁和对象锁非常类似。

结论:私有锁和对象锁也不会产生竞争,二者的加锁方法不会相互影响。

3.synchronized直接加在 方法上和synchronized(this),ObjectThread的run方法修改如下:

public void run() {
		//无锁方法
//		lock.noSynMethod(this.getId(),this);
		//对象锁方法1,采用synchronized synInMethod的方式
		lock.synInMethod();
		//对象锁方法2,采用synchronized(this)的方式
		lock.synOnMethod();
		//私有锁方法,采用synchronized(object)的方式
//		lock.synMethodWithObj();
		//类锁方法,采用static synchronized increment的方式
//		LockTestClass.increament();
	}


终端输出:


start time = 1413102913278ms
synInMethod begins, time = 1413102913279ms
synInMethod ends
synInMethod begins, time = 1413102915279ms
synInMethod ends
synOnMethod begins, time = 1413102917279ms
synOnMethod ends
synInMethod begins, time = 1413102919279ms
synInMethod ends
synOnMethod begins, time = 1413102921279ms
synOnMethod ends
synOnMethod begins, time = 1413102923279ms
synOnMethod ends


       可以看到,二者严格地串行输出(当然再次执行时先运行synInMethod还是先运行synOnMethod并不是确定的,取决于谁获得了锁)。

    结论:synchronized直接加在方法上和synchronized(this)都是对当前对象加锁,二者的加锁方法够成了竞争关系,同一时刻只能有一个方法能执行。

目录
相关文章
|
6天前
|
安全 Java 编译器
Java对象一定分配在堆上吗?
本文探讨了Java对象的内存分配问题,重点介绍了JVM的逃逸分析技术及其优化策略。逃逸分析能判断对象是否会在作用域外被访问,从而决定对象是否需要分配到堆上。文章详细讲解了栈上分配、标量替换和同步消除三种优化策略,并通过示例代码说明了这些技术的应用场景。
Java对象一定分配在堆上吗?
|
3天前
|
存储 安全 Java
java.util的Collections类
Collections 类位于 java.util 包下,提供了许多有用的对象和方法,来简化java中集合的创建、处理和多线程管理。掌握此类将非常有助于提升开发效率和维护代码的简洁性,同时对于程序的稳定性和安全性有大有帮助。
28 17
|
4天前
|
存储 安全 Java
如何保证 Java 类文件的安全性?
Java类文件的安全性可以通过多种方式保障,如使用数字签名验证类文件的完整性和来源,利用安全管理器和安全策略限制类文件的权限,以及通过加密技术保护类文件在传输过程中的安全。
|
9天前
|
存储 安全 Java
系统安全架构的深度解析与实践:Java代码实现
【11月更文挑战第1天】系统安全架构是保护信息系统免受各种威胁和攻击的关键。作为系统架构师,设计一套完善的系统安全架构不仅需要对各种安全威胁有深入理解,还需要熟练掌握各种安全技术和工具。
35 10
|
2天前
|
Java 程序员 数据库连接
Java中的异常处理:理解与实践
【10月更文挑战第29天】在Java编程的世界里,异常像是不请自来的客人,它们可能在任何时候闯入我们的程序宴会。了解如何妥善处理这些意外访客,不仅能够保持我们程序的优雅和稳健,还能确保它不会因为一个小小的失误而全盘崩溃。本文将通过浅显易懂的方式,带领读者深入异常处理的核心概念,并通过实际示例展现如何在Java代码中实现有效的异常管理策略。
|
6天前
|
缓存 Java 调度
Java中的多线程编程:从基础到实践
【10月更文挑战第24天】 本文旨在为读者提供一个关于Java多线程编程的全面指南。我们将从多线程的基本概念开始,逐步深入到Java中实现多线程的方法,包括继承Thread类、实现Runnable接口以及使用Executor框架。此外,我们还将探讨多线程编程中的常见问题和最佳实践,帮助读者在实际项目中更好地应用多线程技术。
14 3
|
8天前
|
Java 数据格式 索引
使用 Java 字节码工具检查类文件完整性的原理是什么
Java字节码工具通过解析和分析类文件的字节码,检查其结构和内容是否符合Java虚拟机规范,确保类文件的完整性和合法性,防止恶意代码或损坏的类文件影响程序运行。
|
8天前
|
Java API Maven
如何使用 Java 字节码工具检查类文件的完整性
本文介绍如何利用Java字节码工具来检测类文件的完整性和有效性,确保类文件未被篡改或损坏,适用于开发和维护阶段的代码质量控制。
|
8天前
|
监控 安全 Java
Java多线程编程的艺术与实践
【10月更文挑战第22天】 在现代软件开发中,多线程编程是一项不可或缺的技能。本文将深入探讨Java多线程编程的核心概念、常见问题以及最佳实践,帮助开发者掌握这一强大的工具。我们将从基础概念入手,逐步深入到高级主题,包括线程的创建与管理、同步机制、线程池的使用等。通过实际案例分析,本文旨在提供一种系统化的学习方法,使读者能够在实际项目中灵活运用多线程技术。
|
6天前
|
缓存 安全 Java
Java中的多线程编程:从基础到实践
【10月更文挑战第24天】 本文将深入探讨Java中的多线程编程,包括其基本原理、实现方式以及常见问题。我们将从简单的线程创建开始,逐步深入了解线程的生命周期、同步机制、并发工具类等高级主题。通过实际案例和代码示例,帮助读者掌握多线程编程的核心概念和技术,提高程序的性能和可靠性。
10 2