(三)线程------Java锁机制
来源:互联网 发布:jdbc怎么连接mysql 编辑:程序博客网 时间:2024/06/14 14:06
简述:
上两节中我们讲述的都是一些理论知识,感觉很枯燥但是又不得不说,从这篇博文开始突然有种“初极狭,才通人。复行数十步,豁然开朗”的感觉。理论指导实践开始吧!开始锁机制之前一定要清楚两件事情:1.锁的对象是谁,2.谁持有了锁。
锁的种类:
我们先来看一下synchronized锁的种类:
1、对象锁:带有synchronized的同步方法或者带有synchronized(this)的同步代码块。
public synchronized void getXXX(){}或者public void getXXX(){ synchronized(){ }}2、类锁:带有synchronized的static方法或者带有synchronized(xxx.class)、synchronized(this.getClass())的静态代码块。如下实例:
public static synchronized void getXXX(){}或者public void getXXX(){ synchronized(Main.class){ }}public void getXXX(){ synchronized(this.getClass()){ }}3、私有锁:在类中声明属性private final Object mMutex = new Object();并在方法中使用synchronized(mMutex);
锁的使用:
1、对象锁:
对象锁代码如下:
package com.lzy.test.suo;public class ObjectLock { public void print() { synchronized (this) { for(int i=0;i<1000;i++){ System.out.println("锁持有者线程ID:" + Thread.currentThread().getId()); System.out.println("锁的对象为:" + this.getClass().hashCode()); } } }}测试代码:
public class SuoTest { public static void main(String[] args) { new Thread(new Runnable() { public void run() { ObjectLock objectLock1 = new ObjectLock(); objectLock1.print(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); new Thread(new Runnable() { @Override public void run() { ObjectLock objectLock2 = new ObjectLock(); objectLock2.print(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); }}运行后截取部分日志如下:
锁持有者线程ID:11锁持有者线程ID:10锁的对象为:166939988锁持有者线程ID:10锁的对象为:166939988
从日志中可以看出:线程并没有同步而是交叉执行。为什么会出现不同步的现象呢?上面说过,锁机制我们应该清楚两件事情:1.锁的对象是谁,2.谁持有了锁。现在分析一下,线程ID为10的线程持有一把锁,这把锁对象为objectLock1,而线程ID为11的线程也持有一把锁,锁的对象为objectLock2。到这里我们就突然明白了,两个线程持有的不是同一把锁,因此也不存在锁的竞争关系,所以也就没有得到线程同步结果。那如何改进呢?我们改进的方法有两种,为了让锁唯一,我们可以将ObjectLock类使用单例模式,这样就能确保锁唯一啦,另外我们不采用单例,我们可以将ObjectLock实例置于外部类中,这样其实例将会保存在堆中,而其引用保存在栈中,这样就可以每次使用同一个对象的引用就OK啦,但是这样存在一个非常复杂的问题:每次都要就行对象引用的参数传递,非常地麻烦,因此建议使用单利模式。
注意:直接使用对象锁的时候最好是在单例模式中使用。
2、私有锁的使用
对象锁存在的问题就是得使用单例,如果说这个操作的对象正好是单例(如数据库操作)倒没什么,如果说明显不能使用单例的话,显然上述锁存在明显的限制性。私有锁可以解决这个问题,其实私有锁也属于对象锁的一种。
我们对仅仅对ObjectLock进行如下改动:public class ObjectLock { private Object mMutex = new Object(); public void print() { synchronized (mMutex) { for (int i = 0; i < 1000; i++) { System.out.println("锁持有者线程ID:" + Thread.currentThread().getId()); System.out.println("锁的对象为:" + mMutex.hashCode()); } } }}运行后的部分日志如下:
锁持有者线程ID:10锁的对象为:1652365127锁持有者线程ID:11锁持有者线程ID:10锁的对象为:1652365127锁持有者线程ID:10锁的对象为:1652365127锁持有者线程ID:10锁的对象为:1652365127锁的对象为:1799665936锁持有者线程ID:11锁的对象为:1799665936咦!怎么跟想象的不太一样呢?对的,我们分析一下日志:线程ID为10的线程持有一把私有锁,私有锁的对象为objectLock1.mMutex,线程ID为11的线程持有一把私有锁,私有锁的对象为objectLock2.mMutex。OK,他们两个线程压根就没有持有同一把锁对象,所以也就不存在竞争关系。可能有人问那你刚才不是已经说私有锁能够解决上面的问题吗?现在也没有解决呀,各位吃瓜群众稍安勿躁,他们现在的问题是没有持有同一把锁,我给私有锁加上两个关键字再看一下结果会不会一样:
public class ObjectLock { private static final Object mMutex = new Object(); public void print() { synchronized (mMutex) { for (int i = 0; i < 1000; i++) { System.out.println("锁持有者线程ID:" + Thread.currentThread().getId()); System.out.println("锁的对象为:" + mMutex.hashCode()); } } }}运行一下果然解决了这个问题。在实际开发中我们经常会用到的私有锁的形式也是上面的形式。所以要谨记:在使用私有锁的时候一定要在私有锁对象上加上静态关键字static或者直接加上static和final两个关键字让其变为一个常量。
3、类锁的使用
类锁的使用就比较简单了,因为每个线程都会持有同一把锁对象,而其锁的对象为唯一且为Class对象,所以类锁一定存在竞争的关系,同步方法也一定会同步的。
public class ClassLock { public void print() { synchronized (ClassLock.class) { System.out.println("锁持有者线程:" + Thread.currentThread().getName()); System.out.println("锁对象为:" + "ClassLock.class"); } }}
0 0
- (三)线程------Java锁机制
- Java中的线程(三)-Java的锁机制
- 线程(三)---锁机制
- Java synchronized同步线程机制(三)
- Java基本线程机制(三)
- Java线程锁机制
- Java线程(三)
- java线程(三)
- java锁机制(三)-----------ReentrantLock
- 基本的线程机制(三)
- java线程锁机制Synchronized
- 线程基础----java对象锁(三)
- Java线程(三):同步与锁
- java多线程机制三--线程的常用方法
- Java反射机制(三)
- java反射机制(三)
- 驯服Java线程(三)
- java线程介绍(三)
- PHP面试题——PHP字符串翻转函数
- Gradle for Android 系列:初识 Gradle 文件
- android 反编译、反调试方法总结
- bootstrap table 分页
- 数据结构与算法描述(习题3答案)
- (三)线程------Java锁机制
- js左移右移规律
- The Linux Programming Interface 07 Memory Allocation 分配内存
- Docker的安装和基本命令
- 截取通知栏信息
- JDBC Resultset获取日期时间型的几种方法
- 自动闭包
- 勤奋提高效率。 方法提高效率。
- Hibernate使用xml配置持久化类