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相关技术文章陆续奉上,转载请注明出处!

原文地址:https://www.cnblogs.com/shsxt/p/8427435.html

时间: 2024-11-05 12:15:28

Java线程中的同步的相关文章

Java线程中sleep()、wait()和notify()和notifyAll()、yield()、join()等方法的用法和区别

Java线程中sleep().wait()和notify()和notifyAll().suspend和resume().yield().join().interrupt()的用法和区别 从操作系统的角度讲,os会维护一个ready queue(就绪的线程队列).并且在某一时刻cpu只为ready queue中位于队列头部的线程服务. 但是当前正在被服务的线程可能觉得cpu的服务质量不够好,于是提前退出,这就是yield. 或者当前正在被服务的线程需要睡一会,醒来后继续被服务,这就是sleep. 

Java线程中锁的问题

Java线程中锁的问题: 同步代码块的锁是自己定义的类:object obj = new object 同步方法的锁是this 静态同步方法的锁是类名.class

在Java 线程中返回值的用法

http://icgemu.iteye.com/blog/467848 在Java 线程中返回值的用法 博客分类: Java Javathread 有时在执行线程中需要在线程中返回一个值:常规中我们会用Runnable接口和Thread类设置一个变量:在run()中改变变量的值,再用一个get方法取得该值,但是run何时完成是未知的:我们需要一定的机制来保证. 在在Java se5有个Callable接口:我们可以用该接口来完成该功能: 代码如: Java代码   package com.thr

模拟做饭系统(java+线程中的join方法)

(一)项目框架分析 妈妈要去做饭,发现没有酱油,让儿子去买酱油,然后回来做饭. 根据面向对象的思想,有两个对象,妈妈和儿子 主要有两个方法: (一)没有线程控制(即儿子没有买酱油回来妈妈就做好饭了)+(没有调用jion方法) (二)有线程控制(妈妈进程和儿子进程又先后影响)+(调用jion方法) 项目的结构图: (二)没有进程控制mintak Son.java主要是模拟儿子的行动,儿子买酱油的行动(与有进程控制的源程序相同) 代码如下: public class Son implements R

JAVA 线程中的异常捕获

在java多线程程序中,所有线程都不允许抛出未捕获的checked exception(比如sleep时的InterruptedException),也就是说各个线程需要自己把自己的checked exception处理掉.这一点是通过java.lang.Runnable.run()方法声明(因为此方法声明上没有throw exception部分)进行了约束.但是线程依然有可能抛出unchecked exception(如运行时异常),当此类异常跑抛出时,线程就会终结,而对于主线程和其他线程完全

深入分析java线程中的volatile

Volatile关键字 平时在阅读jdk源码的时候,经常看到源码中有写变量被volatile关键字修饰,但是却不是十分清除这个关键字到底有什么用处,现在终于弄清楚了,那么我就来讲讲这个volatile到底有什么用吧. 当一个变量被定义为volatile之后,就可以保证此变量对所有线程的可见性,即当一个线程修改了此变量的值的时候,变量新的值对于其他线程来说是可以立即得知的.可以理解成:对volatile变量所有的写操作都能立刻被其他线程得知.但是这并不代表基于volatile变量的运算在并发下是安

java线程中Exchanger使用

有时我们需要对元素进行配对和交换线程的同步点,使用exchange方法 返回其伙伴的对象,这时我们就需要使用线程类中的Exchanger类了, 我通过一个实例 来简单说明一下他的使用方法及其作用: import java.util.concurrent.Exchanger; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class TestExchanger {

Java线程安全与同步代码块

因为在电商网站工作的原因,对于秒杀.闪购等促销形式相当熟悉.无外乎商家拿出一定量的库存去做所谓的"亏本买卖",其目的是用有限的库存去吸引无限的流量.然而,我却碰到过因为系统问题,导致秒杀品超卖的情况.可怜的商户其实只提供了10双9.9元的童鞋做秒杀,却在瞬间内卖出了1000双! 类似这样的问题,在非线程安全的程序设计中十分常见,我们下面运行一个秒杀的程序: public class SalesThread implements Runnable { private int stock

Java面试问题,如何避免Java线程中的死锁?

如何避免Java中的死锁?是流行的Java面试问题之一,也是多线程的流行话题之一.尽管问题看起来很简单,但是一旦深入,大多数Java开发人员就会陷入困境. 面试问题以“什么是死锁?”当两个或多个线程正在等待彼此释放所需资源(锁定)并陷入无限时间的阻塞时,这种情况称为死锁,它只会在多任务或多线程的情况下发生. 如何检测Java中的死锁? 虽然这可能有很多答案,但我的版本是,如果我看到嵌套的同步块,或者从其他同步方法调用一个同步方法,或者试图在不同的对象上获得锁,那么如果开发人员不是非常小心,就很可