Java线程中的同步
1、对象与锁
每一个Object类及其子类的实例都拥有一个锁。其中,标量类型int,float等不是对象类型,但是标量类型可以通过其包装类来作为锁。单独的成员变量是不能被标明为同步的。锁只能用在使用了这些变量的方法上。成员变量可以被声明为volatile,这种方式会影响该变量的原子性,可见性以及排序性。
类似的,持有标量变量元素的数组对象拥有锁,但是其中的标量元素却不拥有锁。(也就是说,没有办法将数组成员声明为volatile类型的)。如果锁住了一个数组并不代表其数组成员都可以被原子的锁定。也没有能在一个原子操作中锁住多个对象的方法。
Class实例本质上是个对象。正如下所述,在静态同步方法中用的就是类对象的锁。
2、同步方法和同步块
使用synchronized关键字,有两种语法结构:同步代码块和同步方法。同步代码块需要提供一个作为锁的对象参数。这就允许了任意方法可以去锁任一一个对象。但在同步代码块中使用的最普通的参数却是this。
同步代码块被认为比同步方法更加的基础。如下两种声明方式是等同的:
1 | synchronized void f() { /* body */ } |
2 | void f() { synchronized(this) { /* body */ } } |
synchronized关键字并不是方法签名的一部分。所以当子类覆写父类中的同步方法或是接口中声明的同步方法的时候,synchronized修饰符是不会被自动继承的,另外,构造方法不可能是真正同步的(尽管可以在构造方法中使用同步块)。
同步实例方法在其子类和父类中使用同样的锁。但是内部类方法的同步却独立于其外部类, 然而一个非静态的内部类方法可以通过下面这种方式锁住其外部类:
1 | synchronized(OuterClass.this) { /* body */ } |
3、等待锁与释放锁
使用synchronized关键字须遵循一套内置的锁等待-释放机制。所有的锁都是块结构的。当进入一个同步方法或同步块的时候必须获得该锁,而退出的时候(即使是异常退出)必须释放这个锁。你不能忘记释放锁。
锁操作是建立在独立的线程上的而不是独立的调用基础上。一个线程能够进入一个同步代码的条件是当前锁未被占用或者是当前线程已经占用了这个锁,否则线程就会阻塞住。(这种可重入锁或是递归锁不同于POSIX线程)。这就允许一个同步方法可以去直接调用同一个锁管理的另一个同步方法,而不需要被冻结(注:即不需要再经历释放锁-阻塞-申请锁的过程)。
同步方法或同步块遵循这种锁获取/锁释放的机制有一个前提,那就是所有的同步方法或同步块都是在同一个锁对象上。如果一个同步方法正在执行中,其他的非同步方法也可以在任何时候执行。也就是说,同步不等于原子性,但是同步机制可以用来实现原子性。
当一个线程释放锁的时候,另一个线程可能正等待这个锁(也可能是同一个线程,因为这个线程可能需要进入另一个同步方法)。但是关于哪一个线程能够紧接着获得这个锁以及什么时候,这是没有任何保证的。另外,没有什么办法能够得到一个给定的锁正被哪个线程拥有着。
除了锁控制之外,同步也会对底层的内存系统带来副作用。
4、静态变量/方法
锁住一个对象并不会原子性的保护该对象类或其父类的静态成员变量。而应该通过同步的静态方法或代码块来保证访问一个静态的成员变量。静态同步使用的是静态方法锁声明的类对象所拥有的锁。类C的静态锁可以通过内置的实例方法获取到:synchronized(C.class) { /* body */ }
每个类所对应的静态锁和其他的类(包括其父类)没有任何的关系。通过在子类中增加一个静态同步方法来试图保护父类中的静态成员变量是无效的。应使用显式的代码块来代替。
如下这种方式也是一种不好的实践:
synchronized(getClass()) { /* body */ } // Do not use
这种方式,可能锁住的实际中的类,并不是需要保护的静态成员变量所对应的类(有可能是其子类)
Java虚拟机在类加载和类初始化阶段,内部获得并释放类锁。除非你要去写一个特殊的类加载器或者需要使用多个锁来控制静态初始顺序,这些内部机制不应该干扰普通类对象的同步方法和同步块的使用。Java虚拟机没有什么内部操作可以独立的获取你创建和使用的类对象的锁。然而当你继承java.*的类的时候,你需要特别小心这些类中使用的锁机制
关于Java线程问题,推荐上海尚学堂Java线程相关技术文章,如:
《认知Java 同步块(synchronized block)》;
《Java多线程爬虫实现》;
《Java线程的知识要点概述》;
《Java 线程池(ThreadPoolExecutor)原理分析与使用》等等。
感谢阅读上海Java培训文章,更多请关注收藏,后续Java相关技术文章陆续奉上,转载请注明出处!
Java线程中的同步的更多相关文章
- java线程中的同步锁和互斥锁有什么区别?
两者都包括对资源的独占. 区别是 1:互斥是通过竞争对资源的独占使用,彼此没有什么关系,也没有固定的执行顺序. 2:同步是线程通过一定的逻辑顺序占有资源,有一定的合作关系去完成任务.
- java线程中的sleep和wait区别
面试题:java线程中sleep和wait的区别以及其资 ...
- Java线程状态及同步锁
线程的生命历程 线程的五大状态 创建状态:简而言之,当创建线程对象的代码出现的时候,此时线程就进入了创建状态.这时候的线程只是行代码而已.只有调用线程的start()方法时,线程的状态才会改变,进入就 ...
- java线程中的sleep/wait/notify/yield/interrupt方法 整理
java线程中的sleep/wait/notify/yield/interrupt方法 sleep 该方法能够使当前线程休眠一段时间 休眠期间,不释放锁 休眠时间结束之后,进入可执行状态,加入到线程就 ...
- 在Java 线程中返回值的用法
http://icgemu.iteye.com/blog/467848 在Java 线程中返回值的用法 博客分类: Java Javathread 有时在执行线程中需要在线程中返回一个值:常规中我们 ...
- JAVA线程及简单同步实现的原理解析
线程 一.内容简介: 本文主要讲述计算机中有关线程的相关内容,以及JAVA中关于线程的基础知识点,为以后的深入学习做铺垫.如果你已经是高手了,那么这篇文章并不适合你. 二.随笔正文: 1.计算机系统组 ...
- 线程中的同步辅助类CountDownLatch
四个类可协助实现常见的专用同步语句.Semaphore 是一个经典的并发工具.CountDownLatch 是一个极其简单但又极其常用的实用工具,用于在保持给定数目的信号.事件或条件前阻塞执行.Cyc ...
- JAVA 线程中的异常捕获
在java多线程程序中,所有线程都不允许抛出未捕获的checked exception(比如sleep时的InterruptedException),也就是说各个线程需要自己把自己的checked e ...
- Java线程中yield与join方法的区别
长期以来,多线程问题颇为受到面试官的青睐.虽然我个人认为我们当中很少有人能真正获得机会开发复杂的多线程应用(在过去的七年中,我得到了一个机会),但是理解多线程对增加你的信心很有用.之前,我讨论了一个w ...
随机推荐
- js-将文本复制到剪切板
// 将文本复制到剪切板 var clipboard2 = new ClipboardJS('.add_wx_guide_float', { text: function(trigger) { ret ...
- 论文阅读笔记四十九:ScratchDet: Training Single-Shot Object Detectors from Scratch(CVPR2019)
论文原址:https://arxiv.org/abs/1810.08425 github:https://github.com/KimSoybean/ScratchDet 摘要 当前较为流行的检测算法 ...
- C#嵌入子窗体,判断子窗体是否打开了
/// <summary> /// 嵌入子窗体,判断子窗体是否打开了 /// </summary> public static Form1 f; public void For ...
- Oracle数据库体系结构之进程结构(4)
Oracle进程结构包括用户进程,服务进程,后台进程. 1. 用户进程 用户进程在数据库用户要求连接到Oracle服务器时开始启动. 用户进程是要求Oracle服务器交互的一种进程 它必须首先建立一个 ...
- IBOS二次开发之视图创建(PHP技术)
在 views 文件夹,我们创建一个跟控制器名称一样的文件夹list,新建一个index.php的视图文件. 我们讨论过多次,最后决定IBOS的视图机制还是以高效为主.因此我们没有使用模板,而是直接使 ...
- xiao_ren
- SpringBoot热部署
SpringBoot的热部署方式分为两种 1.SpringLoader 方式一:以maven插件方式使用SpringLoader <!-- Springloader插件 --> 1.1 ...
- redis对set(无序集合)的相关操作
redis对set类型(无序集合)操作的相关命令以及如何在python使用这些命令 redis对set类型操作的命令: 命令 语法 概述 返回值 Redis Sadd 命令 sadd key memb ...
- java获取某一字段日期并增加7天存入另一字段
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); String dateString = format.f ...
- IO流2
一.IO流简介及分类 1.IO流简介 IO流: 简单理解数据从一个地方流向另外一个地方 2.IO流分类 按照数据流动的方向 分为 输入流和输出流 按照数据流动的单位分为 字节流和字符流 二.四大 ...