synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:

1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;

2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;

3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;

4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

记住:

一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。

二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。

三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。

四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。

五、以上规则对其它对象锁同样适用.

修饰一个代码块

一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞。我们看下面一个例子

【Demo1】:synchronized的用法

/*** 同步线程*/

class SyncThread implementsRunnable {private static intcount;publicSyncThread() {

count= 0;

}public voidrun() {synchronized(this) {for (int i = 0; i < 5; i++) {try{

System.out.println(Thread.currentThread().getName()+ ":" + (count++));

Thread.sleep(100);

}catch(InterruptedException e) {

e.printStackTrace();

}

}

}

}public intgetCount() {returncount;

}

}

SyncThread的调用:

SyncThread syncThread = newSyncThread();

Thread thread1= new Thread(syncThread, "SyncThread1");

Thread thread2= new Thread(syncThread, "SyncThread2");

thread1.start();

thread2.start();

结果如下:

SyncThread1:0SyncThread1:1SyncThread1:2SyncThread1:3SyncThread1:4SyncThread2:5SyncThread2:6SyncThread2:7SyncThread2:8SyncThread2:9*

当两个并发线程(thread1和thread2)访问同一个对象(syncThread)中的synchronized代码块时,在同一时刻只能有一个线程得到执行,另一个线程受阻塞,必须等待当前线程执行完这个代码块以后才能执行该代码块。Thread1和thread2是互斥的,因为在执行synchronized代码块时会锁定当前的对象,只有执行完该代码块才能释放该对象锁,下一个线程才能执行并锁定该对象。

我们再把SyncThread的调用稍微改一下:

Thread thread1 = new Thread(new SyncThread(), "SyncThread1");

Thread thread2= new Thread(new SyncThread(), "SyncThread2");

thread1.start();

thread2.start();

结果如下:

SyncThread1:0SyncThread2:1SyncThread1:2SyncThread2:3SyncThread1:4SyncThread2:5SyncThread2:6SyncThread1:7SyncThread1:8SyncThread2:9

不是说一个线程执行synchronized代码块时其它的线程受阻塞吗?为什么上面的例子中thread1和thread2同时在执行。这是因为synchronized只锁定对象,每个对象只有一个锁(lock)与之相关联,而上面的代码等同于下面这段代码:

SyncThread syncThread1 = newSyncThread();

SyncThread syncThread2= newSyncThread();

Thread thread1= new Thread(syncThread1, "SyncThread1");

Thread thread2= new Thread(syncThread2, "SyncThread2");

thread1.start();

thread2.start();

这时创建了两个SyncThread的对象syncThread1和syncThread2,线程thread1执行的是syncThread1对象中的synchronized代码(run),而线程thread2执行的是syncThread2对象中的synchronized代码(run);我们知道synchronized锁定的是对象,这时会有两把锁分别锁定syncThread1对象和syncThread2对象,而这两把锁是互不干扰的,不形成互斥,所以两个线程可以同时执行。

2.当一个线程访问对象的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该对象中的非synchronized(this)同步代码块。

【Demo2】:多个线程访问synchronized和非synchronized代码块

class Counter implementsRunnable{private intcount;publicCounter() {

count= 0;

}public voidcountAdd() {synchronized(this) {for (int i = 0; i < 5; i ++) {try{

System.out.println(Thread.currentThread().getName()+ ":" + (count++));

Thread.sleep(100);

}catch(InterruptedException e) {

e.printStackTrace();

}

}

}

}//非synchronized代码块,未对count进行读写操作,所以可以不用synchronized

public voidprintCount() {for (int i = 0; i < 5; i ++) {try{

System.out.println(Thread.currentThread().getName()+ " count:" +count);

Thread.sleep(100);

}catch(InterruptedException e) {

e.printStackTrace();

}

}

}public voidrun() {

String threadName=Thread.currentThread().getName();if (threadName.equals("A")) {

countAdd();

}else if (threadName.equals("B")) {

printCount();

}

}

}

调用代码:

Counter counter = newCounter();

Thread thread1= new Thread(counter, "A");

Thread thread2= new Thread(counter, "B");

thread1.start();

thread2.start();

结果如下:

A:0B count:1A:1B count:2A:2B count:3A:3B count:4A:4B count:5

上面代码中countAdd是一个synchronized的,printCount是非synchronized的。从上面的结果中可以看出一个线程访问一个对象的synchronized代码块时,别的线程可以访问该对象的非synchronized代码块而不受阻塞。

指定要给某个对象加锁

【Demo3】:指定要给某个对象加锁

/*** 银行账户类*/

classAccount {

String name;floatamount;public Account(String name, floatamount) {this.name =name;this.amount =amount;

}//存钱

public void deposit(floatamt) {

amount+=amt;try{

Thread.sleep(100);

}catch(InterruptedException e) {

e.printStackTrace();

}

}//取钱

public void withdraw(floatamt) {

amount-=amt;try{

Thread.sleep(100);

}catch(InterruptedException e) {

e.printStackTrace();

}

}public floatgetBalance() {returnamount;

}

}/*** 账户操作类*/

class AccountOperator implementsRunnable{privateAccount account;publicAccountOperator(Account account) {this.account =account;

}public voidrun() {synchronized(account) {

account.deposit(500);

account.withdraw(500);

System.out.println(Thread.currentThread().getName()+ ":" +account.getBalance());

}

}

}

调用代码:

Account account = new Account("zhang san", 10000.0f);

AccountOperator accountOperator= newAccountOperator(account);final int THREAD_NUM = 5;

Thread threads[]= newThread[THREAD_NUM];for (int i = 0; i < THREAD_NUM; i ++) {

threads[i]= new Thread(accountOperator, "Thread" +i);

threads[i].start();

}

结果如下:

Thread3:10000.0Thread2:10000.0Thread1:10000.0Thread4:10000.0Thread0:10000.0

在AccountOperator 类中的run方法里,我们用synchronized 给account对象加了锁。这时,当一个线程访问account对象时,其他试图访问account对象的线程将会阻塞,直到该线程访问account对象结束。也就是说谁拿到那个锁谁就可以运行它所控制的那段代码。

当有一个明确的对象作为锁时,就可以用类似下面这样的方式写程序。

public voidmethod3(SomeObject obj)

{//obj 锁定的对象

synchronized(obj)

{//todo

}

}

当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的对象来充当锁:

class Test implementsRunnable

{private byte[] lock = new byte[0]; //特殊的instance变量

public voidmethod()

{synchronized(lock) {//todo 同步代码块

}

}public voidrun() {

}

}

说明:零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。

修饰一个方法

Synchronized修饰一个方法很简单,就是在方法的前面加synchronized,public synchronized void method(){//todo}; synchronized修饰方法和修饰一个代码块类似,只是作用范围不一样,修饰代码块是大括号括起来的范围,而修饰方法范围是整个函数。如将【Demo1】中的run方法改成如下的方式,实现的效果一样。

*【Demo4】:synchronized修饰一个方法

public synchronized voidrun() {for (int i = 0; i < 5; i ++) {try{

System.out.println(Thread.currentThread().getName()+ ":" + (count++));

Thread.sleep(100);

}catch(InterruptedException e) {

e.printStackTrace();

}

}

}

Synchronized作用于整个方法的写法。

写法一:

public synchronized voidmethod()

{//todo

}

写法二:

public voidmethod()

{synchronized(this) {//todo

}

}

写法一修饰的是一个方法,写法二修饰的是一个代码块,但写法一与写法二是等价的,都是锁定了整个方法时的内容。

在用synchronized修饰方法时要注意以下几点:

1. synchronized关键字不能继承。

虽然可以使用synchronized来定义方法,但synchronized并不属于方法定义的一部分,因此,synchronized关键字不能被继承。如果在父类中的某个方法使用了synchronized关键字,而在子类中覆盖了这个方法,在子类中的这个方法默认情况下并不是同步的,而必须显式地在子类的这个方法中加上synchronized关键字才可以。当然,还可以在子类方法中调用父类中相应的方法,这样虽然子类中的方法不是同步的,但子类调用了父类的同步方法,因此,子类的方法也就相当于同步了。这两种方式的例子代码如下:

在子类方法中加上synchronized关键字

classParent {public synchronized voidmethod() { }

}class Child extendsParent {public synchronized voidmethod() { }

}

在子类方法中调用父类的同步方法

classParent {public synchronized voidmethod() { }

}class Child extendsParent {public void method() { super.method(); }

}

在定义接口方法时不能使用synchronized关键字。

构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步。

修饰一个静态的方法

Synchronized也可修饰一个静态方法,用法如下:

public synchronized static voidmethod() {//todo

}

我们知道静态方法是属于类的而不属于对象的。同样的,synchronized修饰的静态方法锁定的是这个类的所有对象。我们对Demo1进行一些修改如下:

【Demo5】:synchronized修饰静态方法

/*** 同步线程*/

class SyncThread implementsRunnable {private static intcount;publicSyncThread() {

count= 0;

}public synchronized static voidmethod() {for (int i = 0; i < 5; i ++) {try{

System.out.println(Thread.currentThread().getName()+ ":" + (count++));

Thread.sleep(100);

}catch(InterruptedException e) {

e.printStackTrace();

}

}

}public synchronized voidrun() {

method();

}

}

调用代码:

SyncThread syncThread1 = newSyncThread();

SyncThread syncThread2= newSyncThread();

Thread thread1= new Thread(syncThread1, "SyncThread1");

Thread thread2= new Thread(syncThread2, "SyncThread2");

thread1.start();

thread2.start();

结果如下:

SyncThread1:0SyncThread1:1SyncThread1:2SyncThread1:3SyncThread1:4SyncThread2:5SyncThread2:6SyncThread2:7SyncThread2:8SyncThread2:9

syncThread1和syncThread2是SyncThread的两个对象,但在thread1和thread2并发执行时却保持了线程同步。这是因为run中调用了静态方法method,而静态方法是属于类的,所以syncThread1和syncThread2相当于用了同一把锁。这与Demo1是不同的。

修饰一个类

Synchronized还可作用于一个类,用法如下:

classClassName {public voidmethod() {synchronized(ClassName.class) {//todo

}

}

}

我们把Demo5再作一些修改。

【Demo6】:修饰一个类

/*** 同步线程*/

class SyncThread implementsRunnable {private static intcount;publicSyncThread() {

count= 0;

}public static voidmethod() {synchronized(SyncThread.class) {for (int i = 0; i < 5; i ++) {try{

System.out.println(Thread.currentThread().getName()+ ":" + (count++));

Thread.sleep(100);

}catch(InterruptedException e) {

e.printStackTrace();

}

}

}

}public synchronized voidrun() {

method();

}

}

总结:

A. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。

B. 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。

C. 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。

打个比方:

一个object就像一个大房子,大门永远打开。房子里有 很多房间(也就是方法)。这些房间有上锁的(synchronized方法), 和不上锁之分(普通方法)。房门口放着一把钥匙(key),这把钥匙可以打开所有上锁的房间。另外我把所有想调用该对象方法的线程比喻成想进入这房子某个 房间的人。所有的东西就这么多了,下面我们看看这些东西之间如何作用的。在此我们先来明确一下我们的前提条件。该对象至少有一个synchronized方法,否则这个key还有啥意义。当然也就不会有我们的这个主题了。一个人想进入某间上了锁的房间,他来到房子门口,看见钥匙在那儿(说明暂时还没有其他人要使用上锁的 房间)。于是他走上去拿到了钥匙,并且按照自己 的计划使用那些房间。注意一点,他每次使用完一次上锁的房间后会马上把钥匙还回去。即使他要连续使用两间上锁的房间,中间他也要把钥匙还回去,再取回来。因此,普通情况下钥匙的使用原则是:“随用随借,用完即还。”这时其他人可以不受限制的使用那些不上锁的房间,一个人用一间可以,两个人用一间也可以,没限制。但是如果当某个人想要进入上锁的房间,他就要跑到大门口去看看了。有钥匙当然拿了就走,没有的话,就只能等了。

要是很多人在等这把钥匙,等钥匙还回来以后,谁会优先得到钥匙?Not guaranteed。象前面例子里那个想连续使用两个上锁房间的家伙,他中间还钥匙的时候如果还有其他人在等钥匙,那么没有任何保证这家伙能再次拿到。 (JAVA规范在很多地方都明确说明不保证,像Thread.sleep()休息后多久会返回运行,相同优先权的线程那个首先被执行,当要访问对象的锁被释放后,处于等待池的多个线程哪个会优先得到,等等。我想最终的决定权是在JVM,之所以不保证,就是因为JVM在做出上述决定的时候,绝不是简简单单根据 一个条件来做出判断,而是根据很多条。而由于判断条件太多,如果说出来可能会影响JAVA的推广,也可能是因为知识产权保护的原因吧。SUN给了个不保证 就混过去了。无可厚非。但我相信这些不确定,并非完全不确定。因为计算机这东西本身就是按指令运行的。即使看起来很随机的现象,其实都是有规律可寻。学过 计算机的都知道,计算机里随机数的学名是伪随机数,是人运用一定的方法写出来的,看上去随机罢了。另外,或许是因为要想弄的确定太费事,也没多大意义,所 以不确定就不确定了吧。)

再来看看同步代码块。和同步方法有小小的不同。

1.从尺寸上讲,同步代码块比同步方法小。你可以把同步代码块看成是没上锁房间里的一块用带锁的屏风隔开的空间。

2.同步代码块还可以人为的指定获得某个其它对象的key。就像是指定用哪一把钥匙才能开这个屏风的锁,你可以用本房的钥匙;你也可以指定用另一个房子的钥匙才能开,这样的话,你要跑到另一栋房子那儿把那个钥匙拿来,并用那个房子的钥匙来打开这个房子的带锁的屏风。

记住你获得的那另一栋房子的钥匙,并不影响其他人进入那栋房子没有锁的房间。

为什么要使用同步代码块呢?我想应该是这样的:首先对程序来讲同步的部分很影响运行效率,而一个方法通常是先创建一些局部变量,再对这些变量做一些 操作,如运算,显示等等;而同步所覆盖的代码越多,对效率的影响就越严重。因此我们通常尽量缩小其影响范围。如何做?同步代码块。我们只把一个方法中该同 步的地方同步,比如运算。

另外,同步代码块可以指定钥匙这一特点有个额外的好处,是可以在一定时期内霸占某个对象的key。还记得前面说过普通情况下钥匙的使用原则吗。现在不是普通情况了。你所取得的那把钥匙不是永远不还,而是在退出同步代码块时才还。

还用前面那个想连续用两个上锁房间的家伙打比方。怎样才能在用完一间以后,继续使用另一间呢。用同步代码块吧。先创建另外一个线程,做一个同步代码 块,把那个代码块的锁指向这个房子的钥匙。然后启动那个线程。只要你能在进入那个代码块时抓到这房子的钥匙,你就可以一直保留到退出那个代码块。也就是说 你甚至可以对本房内所有上锁的房间遍历,甚至再sleep(10*60*1000),而房门口却还有000个线程在等这把钥匙呢。很过瘾吧。

在此对sleep()方法和钥匙的关联性讲一下。一个线程在拿到key后,且没有完成同步的内容时,如果被强制sleep()了,那key还一直在 它那儿。直到它再次运行,做完所有同步内容,才会归还key。记住,那家伙只是干活干累了,去休息一下,他并没干完他要干的事。为了避免别人进入那个房间 把里面搞的一团糟,即使在睡觉的时候他也要把那唯一的钥匙戴在身上。

最后,也许有人会问,为什么要一把钥匙通开,而不是一个钥匙一个门呢?我想这纯粹是因为复杂性问题。一个钥匙一个门当然更安全,但是会牵扯好多问题。钥匙 的产生,保管,获得,归还等等。其复杂性有可能随同步方法的增加呈几何级数增加,严重影响效率。这也算是一个权衡的问题吧。为了增加一点点安全性,导致效 率大大降低,是多么不可取啊。

java synchronized 使用_Java中Synchronized的用法相关推荐

  1. java synchronized 使用_Java中synchronized的使用实例

    Java中synchronized的使用实例 synchronized关键字,代表这个方法加锁,相当于不管哪一个线程(例如线程A),运行到这个方法时,都要检查有没有其它线程B(或者C.D等)正在用这个 ...

  2. java final 函数_JAVA中Final的用法

    1.         修饰基础数据成员的final 这是final的主要用途,其含义相当于C/C++的const,即该成员被修饰为常量,意味着不可修改.如java.lang.Math类中的PI和E是f ...

  3. java同步关键字_Java中synchronized关键字修饰方法同步的用法详解

    Java的最基本的同步方式,即使用synchronized关键字来控制一个方法的并发访问. 每一个用synchronized关键字声明的方法都是临界区.在Java中,同一个对象的临界区,在同一时间只有 ...

  4. java synchronized静态_Java中Synchronized的用法(简单介绍)

    简单介绍 synchronized是Java中的关键字,是一种同步锁.它修饰的对象有以下几种: 1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调 ...

  5. java statement 参数_java中Statement详细用法。

    1.创建 Statement 对象 建立了到特定数据库的连接之后,就可用该连接发送 SQL 语句.Statement 对象用 Connection 的方法 createStatement 创建,如下列 ...

  6. java boolean 语法_java中boolean的用法

    展开全部 boolean是java中的布尔型(逻辑型)数据类型,在java中boolean值只能是true和false,而不能e68a84e8a2ad62616964757a686964616f313 ...

  7. java setproperty作用_Java中System.setProperty()用法

    /* * 设置指定键对值的系统属性 * setProperty (String prop, String value); * * 参数: * prop - 系统属性的名称. * value - 系统属 ...

  8. 从不同步的代码块中调用了对象同步方法。_Java中Synchronized的用法

    Java中,synchronized关键字是用来控制线程同步的,就是在多线程的环境下,控制synchronized代码段不被多个线程同时执行.拉勾IT课小编为大家分析. synchronized是Ja ...

  9. java implements用法_JAVA中implements的用法

    implements使用 interface Sport { void run(); void jump(); } class Athlete implements Sport { public vo ...

最新文章

  1. linux free命令详解和使用实例(查看内存使用率)
  2. Matlab编程与数据类型 -- 奇数阶魔方矩阵的编程
  3. 排序算法时间复杂度分析
  4. 21天的你长这样?剑桥博士小姐姐研究「人造胚胎」,2篇Nature到手
  5. laravel判断HTTP请求是否ajax
  6. AutoHotkey 使用笔记
  7. Hadoop学习笔记(8) ——实战 做个倒排索引
  8. 深入理解 Java G1 垃圾收集器
  9. MicroPython支持图形化编辑了:Python Editor带你轻松玩转MicroPython
  10. 打印机乱码不停打印_关于东芝复合机连接打印,图文教你如何轻松连接至电脑...
  11. Web完整渗透测试实例(windows)
  12. python安装xlrd和xlwt及应用
  13. 为什么越来越多的人喜欢旅游?
  14. 如何卸载eclipse?
  15. linux查询本机IP地址(可用于SSH访问)
  16. mac iphone模拟器 真机 操作
  17. led灯光衰怎么解决_揭秘LED灯具光衰原因
  18. 服务器上挂网站怎么挂,网站怎样上挂到云服务器
  19. Redis:二、Redis常见命令
  20. 【Python/工具】Pycharm中如何查看一个函数需要哪些参数

热门文章

  1. 华为:Access、Hybrid和Trunk三种模式的理解
  2. 维基链锚定行业缺口,定位发展一体化DeFi,持续开发出各类产品应用,包括去中心化抵押借贷系统Wayki-CDP(含稳定币WUSD)、去中心化交易所Wayki-DEX、去中心化合成资产协议Wayki-X
  3. 挪动以太坊:比特币现金的新功能使其成为智能合约竞争者
  4. 微服务系列(七):将单体应用改造为微服务
  5. 对FCKEditor = 2.6.4 通杀 exploit 的分析
  6. 记一次数据库查询语句的优化
  7. zuul filter
  8. svn idea使用
  9. Paxos算法之旅(四)zookeeper代码解析--转载
  10. iphone,ipad,关于icon图标的那些事