Synchronized是我们常用来维持线程安全时使用的一个关键字,内部通过monitor(监视器锁,由C++实现)来实现。而monitor本质又是依赖底层操作系统的mutex lock来实现。而操作系统实现线程之间的切换,需要从用户态切换到核心态,这个的成本非常高,状态之间的转换需要相对较长的时间,这也就是为什么synchronized效率低的原因。这种依赖mutex lock所实现的锁统称为“重量级锁”。jdk对Synchronized的优化,核心都是减少这种重量级锁的使用。

monitor:由C++实现,可以与对象一起创建和销毁,或者在线程尝试获取对象锁时创建。

一个线程尝试获取对象锁,会先令_owner指向该线程,同时_count自增1,这时候有其他线程尝试获取锁时,会先存入_EntryList集合,并进入阻塞。当前线程执行完成后,令_count自减1,_owner=null,同时将其放到_WaitSet中去。

如果线程被调用了wait()等方式,释放锁时,也会令_owner=null,_count减1,同时将该线程放入_WaitSet集合,等待唤醒。

Synchronized可以使用在三个地方:

  1. 非静态方法
  2. 静态方法
  3. 代码块

首先我们先看一个普通的多线程方法

public class Main6  {

    public void method1() {
System.out.println("method1 start");
try {
System.out.println("method1 exec");
Thread.sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("method1 completion");
} public void method2() {
System.out.println("method2 start");
try {
System.out.println("method2 exec");
Thread.sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("method2 completion");
} public static void main(String[] args) {
Main6 m1 = new Main6();
Main6 m2 = new Main6(); new Thread(new Runnable() {
@Override
public void run() {
m1.method1();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
m1.method2();
}
}).start();
}
}

在这个方法执行的时候,method1()和method2()并行运行,但由于method2()的运行时间短,因此总是会先运行结束。

1.非静态方法

public class Main6  {

    public synchronized void method1() {
System.out.println("method1 start");
try {
System.out.println("method1 exec");
Thread.sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("method1 completion");
} public synchronized void method2() {
System.out.println("method2 start");
try {
System.out.println("method2 exec");
Thread.sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("method2 completion");
} public static void main(String[] args) {
Main6 m1 = new Main6();
Main6 m2 = new Main6(); new Thread(new Runnable() {
@Override
public void run() {
m1.method1();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
m1.method2();
}
}).start();
}
}

在非静态方法上添加synchronized关键字,当我们使用同一个对象分别在不同线程中调用该方法时候,会出现如下顺序的结果:

method1 start
method1 exec
method1 completion
method2 start
method2 exec
method2 completion

这是由于synchronized会获取当前调用对象的锁,当线程1在执行m1.method1();时候获取到了m1对象的锁,线程2企图执行m1.method2()方法时发现m1的锁已经被其他线程获取,因此会进入阻塞状态,直到线程1对method1()方法执行完。

注意,由于调用线程1的start()方法并不是一定会立即开始执行线程1,它先会与main线程争夺cpu等资源,因此有可能method2先执行,method1进入阻塞,直到method2执行完成后才会开始执行。

同时由于synchronized使用在非静态方法上获取的是当前对象的锁,因此假如线程1和2分别使用的是两个不同对象的mthod1方法和method2方法,则不会发生阻塞。

2.静态方法

public class Main6  {

    public static synchronized void method1() {
System.out.println("method1 start");
try {
System.out.println("method1 exec");
Thread.sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("method1 completion");
} public static synchronized void method2() {
System.out.println("method2 start");
try {
System.out.println("method2 exec");
Thread.sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("method2 completion");
} public static void main(String[] args) {
Main6 m1 = new Main6();
Main6 m2 = new Main6(); new Thread(new Runnable() {
@Override
public void run() {
m1.method1();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
m2.method2();
}
}).start();
}
}

静态方法与非静态方法的不同处在于方法是属于类,而不是属于当前对象。

这样即使我们调用的是不同对象的method1方法和method2方法,但是它们获取的锁是类对象的锁(Class对象的锁),因此,不论使用的是哪个对象的method1和method2,均会发生线程阻塞的情况。只有当一个线程中的静态synchronized执行完成后才会执行另一个线程中的静态synchronized方法。

3.代码块

public class Main6  {

    public void method1() {
System.out.println("method1 start");
synchronized(this) {
try {
System.out.println("method1 exec");
Thread.sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("method1 completion");
} public void method2() {
System.out.println("method2 start");
synchronized (this) {
try {
System.out.println("method2 exec");
Thread.sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("method2 completion");
} public static void main(String[] args) {
Main6 m1 = new Main6();new Thread(new Runnable() {
@Override
public void run() {
m1.method1();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
m1.method2();
}
}).start();
}
}

输出的结果可能如下:

method1 start
method1 exec
method2 start
method1 completion
method2 exec
method2 completion

当synchronized被使用在代码块上时,它所获取的锁是括号中接收的对象的锁(如样例代码中的this指代的是当前调用它的对象),这样,假设method1方法正在执行,并且进入代码块休眠3秒,这时method2获取cpu,开始执行第一行代码打印"method2 start",然后遇见代码块,尝试获取this对象的锁,却发现已经被method1获取(sleet不释放锁),因此method2进入阻塞状态,直到method1执行完毕后释放锁,method2获取锁开始执行代码块。

在代码块中调用Synchronized(下图),侧重点在于第3步monitorenter,获取monitor,并对当前_count加一。里面内容执行完成后,在第15步monitorexit,令_count减一,同时释放monitor。

而当Synchronized被使用在方法上时,添加的是一个ACC_SYNCHRONIZED标志位,如果有该标志位,则判定当前方法使用了Synchronized

Synchronized使用方法的更多相关文章

  1. Java多线程5:synchronized锁方法块

    synchronized同步代码块 用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个较长时间的任务,那么B线程必须等待比较长的时间.这种情况下可以尝试使用 ...

  2. java 为什么wait(),notify(),notifyAll()必须在同步(Synchronized)方法/代码块中调用?

    wait()作用:该方法用来将当前线程置入休眠状态,直到接到通知或被中断为止.条件:在调用wait()之前,线程必须要获得该对象的对象级别锁,即只能在同步方法或同步块中调用wait()方法.进入wai ...

  3. JAVA 同步之 synchronized 修饰方法

    在JAVA多线程编程中,将需要并发执行的代码放在Thread类的run方法里面,然后创建多个Thread类的对象,调用start()方法,线程启动执行. 当某段代码需要互斥时,可以用 synchron ...

  4. synchronized修饰方法和对象的区别

    使用synchronized(object) { 代码块.... } 能对代码块进行加锁,不允许其他线程访问,其的作用原理是:在object内有一个变量,当有线程进入时,判断是否为0,如果为0,表示可 ...

  5. java基础之synchronized使用方法

    首先.參考文章:http://www.cnblogs.com/devinzhang/archive/2011/12/14/2287675.html PS:參考文章非常长,但内容非常丰富,若是刚開始学习 ...

  6. java中synchronized使用方法

    synchronized的一个简单样例 public class TextThread { /**  * @param args  */ public static void main(String[ ...

  7. Collections 的 synchronized XXX方法

    摘要 static <T> Collection<T> synchronizedCollection(Collection<T> c) 返回指定 collectio ...

  8. 一张图讲解对象锁和关键字synchronized修饰方法

    每个对象在出生的时候就有一把钥匙(监视器),那么被synchronized 修饰的方法相当于给方法加了一个锁,这个方法就可以进行同步,在多线程的时候,不会出现线程安全问题. 下面通过一张图片进行讲解: ...

  9. 为什么Java中 wait 方法需要在 synchronized 的方法中调用?

    另一个棘手的核心 Java 问题,wait 和 notify.它们是在有 synchronized 标记的方法或 synchronized 块中调用的,因为 wait 和 modify 需要监视对其上 ...

随机推荐

  1. ELK日志系统介绍

    ELK介绍 需求背景: 业务发展越来越庞大,服务器越来越多 各种访问日志.应用日志.错误日志量越来越多,导致运维人员无法很好的去管理日志 开发人员排查问题,需要到服务器上查日志,不方便 运营人员需要一 ...

  2. 在python项目中导出项目依赖的模块信息

    1.安装pipreqs pip install pipreqs 2.导出requriements.txt文件 在windows中,终端切换到项目所在的文件夹下: 运行: pipreqs ./ 如果遇到 ...

  3. MySQL中实现连续日期内数据统计,缺省天数0补全

    某一日,需要查询订单表中一个月每天的金额数 查询出数据如下: array(14) { [0] => array(2) { ["money"] => string(7) ...

  4. Xpath语法详解

    1.简介 XPath是一门在XML和HTML文档中查找信息的语言,可以用来在XML和HTML文档中对元素和属性进行遍历 XPath的安装 Chrome插件XPath Helper 点Chrome浏览器 ...

  5. OAuth2.0配置

    一:授权服务器相关代码 AuthorizationServer.java import org.springframework.beans.factory.annotation.Autowired; ...

  6. python 查找日志关键字

    1.抓取出含有关键字”xiaoming”的行 2.在上一个问题的基础上,假设所在行的格式为location=xiaoming, value=xxx,请筛选出value值 #!/usr/bin/pyth ...

  7. .NET Core 事件总线,分布式事务解决方案:CAP 基于Kafka

    背景 相信前面几篇关于微服务的文章也介绍了那么多了,在构建微服务的过程中确实需要这么一个东西,即便不是在构建微服务,那么在构建分布式应用的过程中也会遇到分布式事务的问题,那么 CAP 就是在这样的背景 ...

  8. 数据结构与算法JS实现

      行解算法题,没错,就是这么方便. 当然也可以使用 Node.js 环境来执行,具体参考Node.js官方文档即可. 二 对象和面向对象编程 js中5种数据类型,并没有定义更多的数据类型,但是运用j ...

  9. 光圈,快门, 曝光,焦距, ISO,景深。

    光圈,快门, 曝光,焦距, ISO,景深. ISO(感光度)与图片质量 ISO -- 感光度,是一个曝光率极高的词,我们在超市买饼干的时候就可能会看见包装袋上写:本公司已通过ISO9001质量体系认证 ...

  10. cookie小结

    cookie的用处:当不同的用户访问同一家网站时(采用相同的请求地址),服务器如何区分不同用户的请求操作呢?需要浏览器对发出的每个请求进行标识.属于同一个会话的请求,都带有相同的标识,不同的会话带有不 ...