synchronized同步代码块

用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个较长时间的任务,那么B线程必须等待比较长的时间。这种情况下可以尝试使用synchronized同步语句块来解决问题。看一下例子:

下面例子是优化后的例子 使用代码块锁,原先例子是方法锁,就是同步 必须要执行2个for

package org.java.base.sync;
public class ThreadDomain18
{public void doLongTimeTask() throws Exception{for (int i = 0; i < 100; i++){System.out.println("nosynchronized threadName = " + Thread.currentThread().getName() + ", i = " + (i + 1));}System.out.println();synchronized (this){for (int i = 0; i < 100; i++){System.out.println("synchronized threadName = " + Thread.currentThread().getName() + ", i = " + (i + 1));}}}
}复制代码

package org.java.base.sync;
public class MyThread18 extends Thread
{private ThreadDomain18 td;public MyThread18(ThreadDomain18 td){this.td = td;}public void run(){try{td.doLongTimeTask();} catch (Exception e){e.printStackTrace();}}
}复制代码

package org.java.base.sync;public class Test1 {public static void main(String[] args){ThreadDomain18 td = new ThreadDomain18();MyThread18 mt0 = new MyThread18(td);MyThread18 mt1 = new MyThread18(td);mt0.start();mt1.start();}
}复制代码

运行结果,分两部分来看:

synchronized threadName = Thread-1, i = 1
synchronized threadName = Thread-1, i = 2
nosynchronized threadName = Thread-0, i = 95
synchronized threadName = Thread-1, i = 3
nosynchronized threadName = Thread-0, i = 96
synchronized threadName = Thread-1, i = 4
nosynchronized threadName = Thread-0, i = 97
synchronized threadName = Thread-1, i = 5
nosynchronized threadName = Thread-0, i = 98
synchronized threadName = Thread-1, i = 6
nosynchronized threadName = Thread-0, i = 99
synchronized threadName = Thread-1, i = 7
nosynchronized threadName = Thread-0, i = 100复制代码

...
synchronized threadName = Thread-1, i = 98
synchronized threadName = Thread-1, i = 99
synchronized threadName = Thread-1, i = 100
synchronized threadName = Thread-0, i = 1
synchronized threadName = Thread-0, i = 2
synchronized threadName = Thread-0, i = 3
...复制代码

这个实验可以得出以下两个结论:

1、当A线程访问对象的synchronized代码块的时候,B线程依然可以访问对象方法中其余非synchronized块的部分,第一部分的执行结果证明了这一点

2、当A线程进入对象的synchronized代码块的时候,B线程如果要访问这段synchronized块,那么访问将会被阻塞,第二部分的执行结果证明了这一点

所以,从执行效率的角度考虑,有时候我们未必要把整个方法都加上synchronized,而是可以采取synchronized块的方式,对会引起线程安全问题的那一部分代码进行synchronized就可以了。

两个synchronized块之间具有互斥性

如果线程1访问了一个对象A方法的synchronized块,那么线程B对同一对象B方法的synchronized块的访问将被阻塞,写个例子来证明一下:

public class ThreadDomain19
{public void serviceMethodA(){synchronized (this){try{System.out.println("A begin time = " + System.currentTimeMillis());Thread.sleep(2000);System.out.println("A end time = " + System.currentTimeMillis());} catch (InterruptedException e){e.printStackTrace();}}}public void serviceMethodB(){synchronized (this){System.out.println("B begin time = " + System.currentTimeMillis());System.out.println("B end time = " + System.currentTimeMillis());}}
}复制代码

写两个线程分别调用这两个方法:

public class MyThread19_0 extends Thread
{private ThreadDomain19 td;public MyThread19_0(ThreadDomain19 td){this.td = td;}public void run(){td.serviceMethodA();}
}复制代码

public class MyThread19_1 extends Thread
{private ThreadDomain19 td;public MyThread19_1(ThreadDomain19 td){this.td = td;}public void run(){td.serviceMethodB();}
}复制代码

写个main函数:

public static void main(String[] args)
{ThreadDomain19 td = new ThreadDomain19();MyThread19_0 mt0 = new MyThread19_0(td);MyThread19_1 mt1 = new MyThread19_1(td);mt0.start();mt1.start();
}复制代码

看一下运行结果:

A begin time = 1443843271982
A end time = 1443843273983
B begin time = 1443843273983
B end time = 1443843273983复制代码

看到对于serviceMethodB()方法synchronized块的访问必须等到对于serviceMethodA()方法synchronized块的访问结束之后。那其实这个例子,我们也可以得出一个结论:synchronized块获得的是一个对象锁,换句话说,synchronized块锁定的是整个对象

synchronized块和synchronized方法

既然上面得到了一个结论synchronized块获得的是对象锁,那么如果线程1访问了一个对象方法A的synchronized块,线程2对于同一对象同步方法B的访问应该是会被阻塞的,因为线程2访问同一对象的同步方法B的时候将会尝试去获取这个对象的对象锁,但这个锁却在线程1这里。写一个例子证明一下这个结论:

public class ThreadDomain20
{public synchronized void otherMethod(){System.out.println("----------run--otherMethod");}public void doLongTask(){synchronized (this){for (int i = 0; i < 1000; i++){System.out.println("synchronized threadName = " + Thread.currentThread().getName() + ", i = " + (i + 1));try{Thread.sleep(5);}catch (InterruptedException e){e.printStackTrace();}}}}
}复制代码

写两个线程分别调用这两个方法:

public class MyThread20_0 extends Thread
{private ThreadDomain20 td;public MyThread20_0(ThreadDomain20 td){this.td = td;}public void run(){td.doLongTask();}
}复制代码

public class MyThread20_1 extends Thread
{private ThreadDomain20 td;public MyThread20_1(ThreadDomain20 td){this.td = td;}public void run(){td.otherMethod();}
}复制代码

写个main函数调用一下,这里”mt0.start()”后sleep(100)以下是为了确保mt0线程先启动:

public static void main(String[] args) throws Exception{ThreadDomain20 td = new ThreadDomain20();MyThread20_0 mt0 = new MyThread20_0(td);MyThread20_1 mt1 = new MyThread20_1(td);mt0.start();Thread.sleep(100);mt1.start();}复制代码

看一下运行结果:

...
synchronized threadName = Thread-0, i = 995
synchronized threadName = Thread-0, i = 996
synchronized threadName = Thread-0, i = 997
synchronized threadName = Thread-0, i = 998
synchronized threadName = Thread-0, i = 999
synchronized threadName = Thread-0, i = 1000
----------run--otherMethod复制代码

证明了我们的结论。为了进一步完善这个结论,把”otherMethod()”方法的synchronized去掉再看一下运行结果:

...
synchronized threadName = Thread-0, i = 16
synchronized threadName = Thread-0, i = 17
synchronized threadName = Thread-0, i = 18
synchronized threadName = Thread-0, i = 19
synchronized threadName = Thread-0, i = 20
----------run--otherMethod
synchronized threadName = Thread-0, i = 21
synchronized threadName = Thread-0, i = 22
synchronized threadName = Thread-0, i = 23
...复制代码

“otherMethod()”方法和”doLongTask()”方法中的synchronized块异步执行了

将任意对象作为对象监视器

总结一下前面的内容:

1、synchronized同步方法

(1)对其他synchronized同步方法或synchronized(this)同步代码块呈阻塞状态

(2)同一时间只有一个线程可以执行synchronized同步方法中的代码

2、synchronized同步代码块

(1)对其他synchronized同步方法或synchronized(this)同步代码块呈阻塞状态

(2)同一时间只有一个线程可以执行synchronized(this)同步代码块中的代码

前面都使用synchronized(this)的格式来同步代码块,其实Java还支持对”任意对象”作为对象监视器来实现同步的功能。这个”任意对象”大多数是实例变量方法的参数,使用格式为synchronized(非this对象)。看一下将任意对象作为对象监视器的使用例子:

public class ThreadDomain21
{private String userNameParam;private String passwordParam;private String anyString = new String();public void setUserNamePassword(String userName, String password){try{synchronized (anyString){System.out.println("线程名称为:" + Thread.currentThread().getName() + "在 " + System.currentTimeMillis() + " 进入同步代码块");userNameParam = userName;Thread.sleep(3000);passwordParam = password;System.out.println("线程名称为:" + Thread.currentThread().getName() + "在 " + System.currentTimeMillis() + " 离开同步代码块");}}catch (InterruptedException e){e.printStackTrace();}}
}复制代码

写两个线程分别调用一下:

public class MyThread21_0 extends Thread
{private ThreadDomain21 td;public MyThread21_0(ThreadDomain21 td){this.td = td;}public void run(){td.setUserNamePassword("A", "AA");}
}复制代码

public class MyThread21_1 extends Thread
{private ThreadDomain21 td;public MyThread21_1(ThreadDomain21 td){this.td = td;}public void run(){td.setUserNamePassword("B", "B");}
}复制代码

写一个main函数调用一下:

public static void main(String[] args)
{ThreadDomain21 td = new ThreadDomain21();MyThread21_0 mt0 = new MyThread21_0(td);MyThread21_1 mt1 = new MyThread21_1(td);mt0.start();mt1.start();
}复制代码

看一下运行结果:

线程名称为:Thread-0在 1443855101706 进入同步代码块
线程名称为:Thread-0在 1443855104708 离开同步代码块
线程名称为:Thread-1在 1443855104708 进入同步代码块
线程名称为:Thread-1在 1443855107708 离开同步代码块复制代码

这个例子证明了:多个线程持有”对象监视器”为同一个对象的前提下,同一时间只能有一个线程可以执行synchronized(非this对象x)代码块中的代码。

锁非this对象具有一定的优点:如果在一个类中有很多synchronized方法,这时虽然能实现同步,但会受到阻塞,从而影响效率。但如果同步代码块锁的是非this对象,则synchronized(非this对象x)代码块中的程序与同步方法是异步的,不与其他锁this同步方法争抢this锁,大大提高了运行效率。

其实无论是方法所还是代码锁都是要以一个对象监视器来锁定,锁定的代码是同步的,锁this是当前对象,锁String是String这个对象,锁Object是Object这个对象,互不干扰,如果有其它线程调用同样用到跟上面锁this、Objcet、String相同对象的方法或代码,就需要等待同步,锁代码块比锁方法更加灵活。因为锁方法锁的是this 也就是当前对象,当一个线程正在调用当前这个对象的所方法时,导致其它线程调用不了该对象的其它锁this的代码,也调不了所有该对象的锁方法

锁的是当前这个线程,针对锁的对象的这段代码或方法,一次只能一个线程运行,其它线程运行到此的话会暂停,如果是执行其它非锁的则是异步的,注意这里不要被多线程搞迷糊了。单个线程执行的时候都是同步的,当这个线程被阻塞后,之后的代码(锁内的和锁外的)无论什么都不会执行,只有当唤醒或者恢复正常时才会继续往下走,走完锁内的代码就会放锁,然后继续走剩余的代码

注意一下”private String anyString = new String();”这句话,现在它是一个全局对象,因此监视的是同一个对象。如果移到try里面,那么对象的监视器就不是同一个了,调用的时候自然是异步调用,可以自己试一下。

最后提一点,synchronized(非this对象x),这个对象如果是实例变量的话,指的是对象的引用,只要对象的引用不变,即使改变了对象的属性,运行结果依然是同步的

synchronized锁机制 之 代码块锁相关推荐

  1. [java多线程] - 锁机制同步代码块信号量

    在美眉图片下载demo中,我们可以看到多个线程在公用一些变量,这个时候难免会发生冲突.冲突并不可怕,可怕的是当多线程的情况下,你没法控制冲突.按照我的理解在java中实现同步的方式分为三种,分别是:同 ...

  2. synchronized锁(方法锁, 代码块锁)

    synchronized锁可以解决线程安全问题,但是相应的,只要是锁,就会带来性能开销,所以尽可能减小锁的范围尤为重要. synchronized锁无非修饰普通方法,修饰静态方法,修饰代码块,我认为无 ...

  3. Synchronized代码块锁的是什么?全局锁?对象锁?

    对象锁:锁的是这个对象. 全局锁:锁的是那个写了synchronized关键字的方法或者代码块. 这里主要去聊全局锁 当java关键字 Synchronized()代码块锁随便的一个类比如,Integ ...

  4. JUC多线程:synchronized锁机制原理 与 Lock锁机制

    前言: 线程安全是并发编程中的重要关注点,造成线程安全问题的主要原因有两点,一是存在共享数据(也称临界资源),二是存在多条线程共同操作共享数据.因此为了解决这个问题,我们可能需要这样一个方案,当存在多 ...

  5. 6※、线程同步、同步锁、同步代码块的使用、同步锁释放的时机、ReentrantLock可重入锁、公平锁与非公平锁的区别、什么是死锁、线程间的通信(生产者和消费者模式)

    线程锁 1.※线程的同步:(要确保对象锁是一致的) 1.未使用同步锁的抢票 2.使用了同步锁的抢票 3.线程-同步代码块的使用 4.同步方法和代码块的区别 5.同步锁释放的时机 练习:多线程生产手机 ...

  6. java线程锁机制_多线程之锁机制

    前言 在Java并发编程实战,会经常遇到多个线程访问同一个资源的情况,这个时候就需要维护数据的一致性,否则会出现各种数据错误,其中一种同步方式就是利用Synchronized关键字执行锁机制,锁机制是 ...

  7. 从操作系统的PV操作理解JAVA的synchronized同步方法,同步代码块实现,及比较

    也有利于理解操作系统的同步 知识果然是息息相关的 JAVA同样离不开操作系统的原理,不过他体现在虚拟机JVM中 synchronized关键字是同步关键字 首先我们知道在操作系统里 PV两者操作信息量 ...

  8. python锁机制_python基础(锁机制,守护线程,线程队列,线程池)

    一. 互斥锁(Lock)与递归锁(RLock)机制 1.1 由互斥锁(Lock)产生的死锁现象: #互斥锁(死锁现象): #死锁现象: from threading importLock lock=L ...

  9. mysql innodb 的锁机制_Mysql之Innodb锁机制详解

    InnoDB与MyISAM的最大不同有两点:一是支持事务(TRANSACTION):二是采用了行级锁.关于事务我们之前有专题介绍,这里就着重介绍下它的锁机制. 总的来说,InnoDB按照不同的分类共有 ...

最新文章

  1. 苹果的新Siri:不男,也不女
  2. springboot学习笔记(二)
  3. 后台系统可扩展性学习笔记(十三)缓存
  4. 1107: 回文数猜想(函数专题)
  5. 如何识别一个人是web前端程序员
  6. python爬虫的时候要用的url修改方法总结
  7. ccs中如何插入字体
  8. Lua的多任务机制——协程(coroutine)
  9. 面向对象组件开发一个弹窗
  10. linux系统学习第二天
  11. HDU2147 kiki's game
  12. 10W+字C语言从入门到精通保姆级教程(2021版下)
  13. Python标准库(各种模块介绍)
  14. 编程题(买帽子求第三便宜价格)
  15. JVM中的-Xms -Xmx -XXnewSize -XXMaxnewSize -Xmn -XXPermSize -XXMaxPermSize区别介绍
  16. 爱快最新版3.6用docker安装Jellyfin最新教程
  17. booting from hard disk
  18. Linux rpm 命令 【转】
  19. 读取csv时中文乱码问题
  20. MYSQL的地理信息数据库_国家基础地理信息系统数据库

热门文章

  1. 北京java培训机构收费,Java开发者必看
  2. 第 7 章 原型模式
  3. CSS使表格不变形(原创)
  4. php主控,IcePHP框架中的快速后台中的通用CRUD功能框架(五) SCrud 主控类
  5. kotlin android 镜像,【54】Kotlin android Anko 神兵利器
  6. nbear分页 效率低_为什么大家都说“SELECT *”效率低?
  7. Vue Router 4 快速入门
  8. 2018年网络开发者应该关注什么
  9. JavaScript内存那点事
  10. html文件如何转php文件,怎么把动态的php文件转换成静态的html文件,html文件是php文件…...