java synchronized 示例_用示例说明synchronized

一 .简单介绍synchronized

关键字 synchronized可以在多线程并发中使用,保证同步。

非常重要的一点,保证线程安全的三个特性:原子性、可见性、有序性。而synchronized可以保证原子性和可见性。

synchronized主要有3种用法:

(1)修饰普通方法:作用于实例对象

(2)修饰静态方法:作用于类对象

(3)修饰代码块:可以指定作用的对象

二. 用Demo说明synchronized主要的用法

1.模拟多线程环境

Thread thread = new Thread(runnable); // runnable里面做操作

thread.start(); // 用Thread才能模拟

基本来说,synchronized修饰一个方法,多线程调用这个方法的时候,肯定会同步,这个大家都知道,就不用写Demo来说明了

2.多个普通方法使用synchronized 修饰(示例1)

假设有两个方法

(1)一个方法用synchronized修饰,一个不用

Thread thread = new Thread(new Runnable() {

@Override

public void run() {

test.one("AA");

}

});

thread.start();

test.two();

public synchronized void one(String tag){

for (int i = 0; i < 100; i++) {

Log.v("mmp","one 【"+tag+" "+i);

}

}

public void two(){

for (int i = 0; i < 100; i++) {

Log.v("mmp","two "+i);

}

}

这样得到的结果是两个方法不会同步,打印的结果是乱的。

(2)两个方法都加锁

两个方法都用synchronized 修饰

public synchronized void one(String tag){

for (int i = 0; i < 10000; i++) {

Log.v("mmp","one 【"+tag+" "+i);

}

}

public synchronized void two(){

for (int i = 0; i < 10000; i++) {

Log.v("mmp","two "+i);

}

}

得到的结果是数据没有乱,两个方法实现了同步

(3)synchronized修饰的方法调用不被synchronized修饰的方法

public synchronized void one(String tag){

for (int i = 0; i < 10000; i++) {

Log.v("mmp","one 【"+tag+" "+i);

}

three();

}

public synchronized void two(){

for (int i = 0; i < 10000; i++) {

Log.v("mmp","two "+i);

}

}

public void three(){

for (int i = 0; i < 10000; i++) {

Log.v("mmp","three "+i);

}

}

这种情况数据也不会乱,和上面的一样,调用two的时候,one都没进去,更不可能调用three

从示例1可以有一种感觉,synchronized 作用的是对象,一个对象只有一个锁。

3.静态方法用synchronized修饰

(1)一个线程调普通的方法,主线程调这个类的静态方法,两个方法都用synchronized修饰(示例2)

Thread thread = new Thread(new Runnable() {

@Override

public void run() {

test.one("AA");

}

});

thread.start();

Test.three();

public synchronized void one(String tag){

for (int i = 0; i < 100; i++) {

Log.v("mmp","one 【"+tag+" "+i);

}

}

public synchronized static void three(){

for (int i = 0; i < 100; i++) {

Log.v("mmp","three "+i);

}

}

9a2c1279aa5f

可以看到打印的数据是乱的,这就示例1的不同,上面synchronized修饰两个普通方法,这个是synchronized修饰一个普通方法和一个静态方法。这样就能证明修饰普通方法和修饰静态方法不是作用在同一个对象。

那如果要静态方法和普通方法里面的操作实现同步怎么做?

说实话我好像基本没见过这样的要求,那是不能做吗?当然能做,没什么是不能做的。

静态方法和基本方法保持同步(示例3)

public void one(String tag){

synchronized (Test.class) {

for (int i = 0; i < 100; i++) {

Log.v("mmp", "one 【" + tag + " " + i);

}

}

}

public synchronized static void three(){

for (int i = 0; i < 100; i++) {

Log.v("mmp", "three " + i);

}

}

这样就行了,这样数据就不会乱了。这里用到了同步代码块,把锁的对象设置成类,两个都是对类的锁,就会产生互斥。

4.同步代码块(示例4)

如果我们对某个方法,不想让这个方法的全部代码都同步,只想让部分代码同步,在上面的栗子中加入一个循环打印(循环次数改小方便截图)

public void one(String tag){

for (int i = 0; i < 10; i++) {

Log.v("mmp", "非同步块:" + tag + " " + i);

}

synchronized (Test.class) {

for (int i = 0; i < 10; i++) {

Log.v("mmp", "one 【" + tag + " " + i);

}

}

}

public synchronized static void three(){

for (int i = 0; i < 10; i++) {

Log.v("mmp", "three " + i);

}

}

9a2c1279aa5f

可以从结果中看出,只有synchronized 同步块里面的代码才会同步。

5.小结

示例1演示了synchronized 作用于类的实例对象。

示例2演示了synchronized 作用于类对象。

示例3和示例4演示了synchronized 使用同步代码块作用于指定的对象。

比如你想作用于当前的类的实例对象,可以这样写

synchronized (this) {

.......

}

如果你想作用于类对象,可以这样写

synchronized (类名.class) {

.......

}

三. 原理

简单来看看synchronized的原理,为什么说简单呢,因为底层的代码我也看不懂,所以大概就只能去看别人总结的,大致了解一下,详细的话以后看得懂代码再来详细说吧。

将synchronized反编译之后

9a2c1279aa5f

网上找的图

我自己也试着反编译了下,发现找到的不是monitorenter和monitorexit,我这边看到的是monitor-enter和monitor-exit

9a2c1279aa5f

可以看出,锁的进入和退出都有和monitor有关。

1.对象头

这就要涉及到java的对象头了,那对象头是什么。

Java对象保存在内存中时,由以下三部分组成: 对象头、实例数据、 对齐填充字节。也就是可以把JAVA对象抽象的认为是: 对象头 + 实例数据 + 对象填充

那这个对象头反过来就是Java对象的一部分,那肯定是用来存储某部分的数据。

对象头又由两部分组成,一部分用于存储自身的运行时数据,称之为 Mark Word,另外一部分是类型指针(先了解就好)。

所以 对象头 = Mark Word + 类型指针

Mark Word主要用来存储对象自身的运行时数据,如hashcode、gc分代年龄等。当然这个我也是看别人说的。然后可以看看别人画的结构图,会比较清晰。

9a2c1279aa5f

网上的图

// 32位系统的

|-------------------------------------------------------|--------------------|

| Mark Word (32 bits) | State |

|-------------------------------------------------------|--------------------|

| identity_hashcode:25 | age:4 | biased_lock:1 | lock:2 | Normal |

|-------------------------------------------------------|--------------------|

| thread:23 | epoch:2 | age:4 | biased_lock:1 | lock:2 | Biased |

|-------------------------------------------------------|--------------------|

| ptr_to_lock_record:30 | lock:2 | Lightweight Locked |

|-------------------------------------------------------|--------------------|

| ptr_to_heavyweight_monitor:30 | lock:2 | Heavyweight Locked |

|-------------------------------------------------------|--------------------|

| | lock:2 | Marked for GC |

|-------------------------------------------------------|--------------------|

// 64位系统的

|------------------------------------------------------------------------------|--------------------|

| Mark Word (64 bits) | State |

|------------------------------------------------------------------------------|--------------------|

| unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | lock:2 | Normal |

|------------------------------------------------------------------------------|--------------------|

| thread:54 | epoch:2 | unused:1 | age:4 | biased_lock:1 | lock:2 | Biased |

|------------------------------------------------------------------------------|--------------------|

| ptr_to_lock_record:62 | lock:2 | Lightweight Locked |

|------------------------------------------------------------------------------|--------------------|

| ptr_to_heavyweight_monitor:62 | lock:2 | Heavyweight Locked |

|------------------------------------------------------------------------------|--------------------|

| | lock:2 | Marked for GC |

|------------------------------------------------------------------------------|--------------------|

9a2c1279aa5f

网上的图

大概了解下就行。

2.Monitor

Monitor 是一种同步工具,也是一个对象。

Object 类本身就是监视者对象,可以想象成他们本身就带了一把看不见的锁,可以看看Java Monitor 工作原理的图。

9a2c1279aa5f

网上的图

这里也是简单理解一下,详细的流程就没这么简单了。

3.锁的状态

之前的synchronized是重量级的锁,在JAVA 6之后做了优化,锁的状态有4种,看上面的图就能看出,分为无锁状态、偏向锁、轻量级锁和重量级锁。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值