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线程中的同步的更多相关文章

  1. java线程中的同步锁和互斥锁有什么区别?

    两者都包括对资源的独占. 区别是 1:互斥是通过竞争对资源的独占使用,彼此没有什么关系,也没有固定的执行顺序. 2:同步是线程通过一定的逻辑顺序占有资源,有一定的合作关系去完成任务.

  2. java线程中的sleep和wait区别

                                                                            面试题:java线程中sleep和wait的区别以及其资 ...

  3. Java线程状态及同步锁

    线程的生命历程 线程的五大状态 创建状态:简而言之,当创建线程对象的代码出现的时候,此时线程就进入了创建状态.这时候的线程只是行代码而已.只有调用线程的start()方法时,线程的状态才会改变,进入就 ...

  4. java线程中的sleep/wait/notify/yield/interrupt方法 整理

    java线程中的sleep/wait/notify/yield/interrupt方法 sleep 该方法能够使当前线程休眠一段时间 休眠期间,不释放锁 休眠时间结束之后,进入可执行状态,加入到线程就 ...

  5. 在Java 线程中返回值的用法

    http://icgemu.iteye.com/blog/467848 在Java 线程中返回值的用法 博客分类: Java Javathread  有时在执行线程中需要在线程中返回一个值:常规中我们 ...

  6. JAVA线程及简单同步实现的原理解析

    线程 一.内容简介: 本文主要讲述计算机中有关线程的相关内容,以及JAVA中关于线程的基础知识点,为以后的深入学习做铺垫.如果你已经是高手了,那么这篇文章并不适合你. 二.随笔正文: 1.计算机系统组 ...

  7. 线程中的同步辅助类CountDownLatch

    四个类可协助实现常见的专用同步语句.Semaphore 是一个经典的并发工具.CountDownLatch 是一个极其简单但又极其常用的实用工具,用于在保持给定数目的信号.事件或条件前阻塞执行.Cyc ...

  8. JAVA 线程中的异常捕获

    在java多线程程序中,所有线程都不允许抛出未捕获的checked exception(比如sleep时的InterruptedException),也就是说各个线程需要自己把自己的checked e ...

  9. Java线程中yield与join方法的区别

    长期以来,多线程问题颇为受到面试官的青睐.虽然我个人认为我们当中很少有人能真正获得机会开发复杂的多线程应用(在过去的七年中,我得到了一个机会),但是理解多线程对增加你的信心很有用.之前,我讨论了一个w ...

随机推荐

  1. 常见jquery面试题

    jQuery 面试问题和答案 JavaScript 是客户端脚本的标准语言,而 jQuery 使得编写 JavaScript 更加简单.你可以只用写几行的jQuery 代码就能实现更多的东西. 它是最 ...

  2. overlay 文件系统

    overlay文件系统浅析 overlayfs文件系统类似于aufs,相比aufs,overlay实现更简洁,很早就合入了linux主线, 合入主线后overlayfs修改为overlay. dock ...

  3. 集腋成裘-13-git使用-02进阶篇

    上一篇我们简单介绍了如何安装和使用git,本次将简单介绍如何创建分支.处理冲突.提交与回滚 创建和合并分支是Git的一大特色. 使用场景:当有一个新功能要开发,但是又没有确定上线时间,不能合并到主干上 ...

  4. Javascript数据结构与算法--栈的实现与用法

    栈数据结构 栈是一种遵从后进先出(LIFO)原则的有序集合.新添加的或者待删除的元素都保存在栈的同一端,称作栈顶,另一端就叫栈底.在栈里,新元素都靠近栈顶,旧元素都接近栈底. 我们在生活中常能看到栈的 ...

  5. python 的os.getenv("PATH")和os.environ.get("PATH")的区别

    os.environ(x [,x]) raises an exception if the environmental variable does not exist. os.getenv(x) do ...

  6. 如何在本地数据中心安装Service Fabric for Windows集群

    概述 首先本文只是对官方文档(中文,英文)的一个提炼,详细的安装说明还请仔细阅读官方文档. 虽然Service Fabric的官方名称往往被加上Azure,但是实际上(估计很多人不知道)Service ...

  7. git 仓库中删除历史大文件

    git 仓库中删除历史大文件 在git中增加了一个很大的文件,而且被保存在历史提交记录中,每次拉取代码都很大,速度很慢.而且用删除 提交历史记录的方式不是很实际. 以下分几个步骤介绍如何减小.git文 ...

  8. bower 基础认识

    bower 跟 npm 很像 都是管理包的工具 只是 bower 偏向前端  npm 前后都能管理 npm init 后是  生产 package.json  下载的包在node_modules文件下 ...

  9. ISP PIPLINE (十) HDR

    在讲HDR之前先理解一些概念,要知道为什么进行HDR? 再去想如何进行HDR. 自然界的中光强度很宽,而人眼对高亮,极暗环境的细节分辨能力比较弱.而摄像头记录的范围更窄,真正的HDR技术就是记录视觉范 ...

  10. HeadDoc自动注释语法

    记录于2013/4/23: 关于HeaderDoc注释和标签的简要介绍 每个HeaderDoc注释可分为顶级标签和第二级标签: (1)顶级标签:宣布API的声明类型 (function, struct ...