目录:

Java并发编程之三:volatile关键字解析 转载

volatile之一--volatile不能保证原子性

Synchronized之一:基本使用

Synchronized作用

1、Synchronized可以保证在同一时刻,只有一个线程可以执行某一个方法或者代码块。

2、同步的作用不仅仅是互斥,它的另一个作用就是共享可变性,当某个线程修改了可变数据并释放锁后,其它线程可以获取被修改变量的最新值。如果没有正确的同步,这种修改对其它线程是不可见的。

一、Synchronized的基本使用

  Synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法。Synchronized的作用主要有三个:(1)确保线程互斥的访问同步代码(2)保证共享变量的修改能够及时可见(3)有效解决重排序问题。从语法上讲,Synchronized总共有四种不同的同步块:

1.同步的实例方法(锁用的是其实例对象本身。所有的非静态同步方法执行需要顺序执行,即不能并行执行。)
2.同步的静态方法(锁用的是其类对象本身。所有的静态同步方法执行需要顺序执行,即不能并行执行。)
3.实例方法中的同步块(锁是自己指定的,但不能是引用性对象及null对象)
4.静态方法中的同步块(锁是自己指定的,但不能是引用性对象及null对象)

静态同步方法与非静态同步方法区别:
所有的非静态同步方法用的都是同一把锁——实例对象本身,也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁,所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。

而所有的静态同步方法用的也是同一把锁——类对象本身,这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,而不管是同一个实例对象的静态同步方法之间,还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象!
同步块:
而对于同步块,由于其锁是可以选择的,所以只有使用同一把锁的同步块之间才有着竞态条件,这就得具体情况具体分析了,但这里有个需要注意的地方,同步块的锁是可以选择的,但是不是可以任意选择的!!!!这里必须要注意一个物理对象和一个引用对象的实例变量之间的区别!使用一个引用对象的实例变量作为锁并不是一个好的选择,因为同步块在执行过程中可能会改变它的值,其中就包括将其设置为null,而对一个null对象加锁会产生异常,并且对不同的对象加锁也违背了同步的初衷!这看起来是很清楚的,但是一个经常发生的错误就是选用了错误的锁对象,因此必须注意:同步是基于实际对象而不是对象引用的!多个变量可以引用同一个对象,变量也可以改变其值从而指向其他的对象,因此,当选择一个对象锁时,我们要根据实际对象而不是其引用来考虑!作为一个原则,不要选择一个可能会在锁的作用域中改变值的实例变量作为锁对象!

 Java中多线程锁释放的条件:
1)执行完同步代码块,就会释放锁。(synchronized)
2)在执行同步代码块的过程中,遇到异常而导致线程终止,锁也会被释放。(exception)
3)在执行同步代码块的过程中,执行了锁所属对象的wait()方法,这个线程会释放锁,进入对象的等待池。(wait)
从上面的三点我就可以看到stop方法释放锁是在第二点的,通过抛出异常来释放锁,通过证明,这种方式是不安全的,不可靠的。

  接下来我就通过几个例子程序来说明一下这三种使用方式(为了便于比较,三段代码除了Synchronized的使用方式不同以外,其他基本保持一致)。

1.1、没有同步的情况:

package com.dxz.synchronize;

public class SynchronizedTest {
public void method1() {
System.out.println(Thread.currentThread().getName() + " Method 1 start");
try {
System.out.println(Thread.currentThread().getName() + " Method 1 execute");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 1 end");
} public void method2() {
System.out.println(Thread.currentThread().getName() + " Method 2 start");
try {
System.out.println(Thread.currentThread().getName() + " Method 2 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 2 end");
} public static void main(String[] args) {
final SynchronizedTest test = new SynchronizedTest(); new Thread(new Runnable() {
@Override
public void run() {
test.method1();
}
},"thread1").start(); new Thread(new Runnable() {
@Override
public void run() {
test.method2();
}
},"thread2").start();
}
}

执行结果如下,线程1和线程2同时进入执行状态,线程2执行速度比线程1快,所以线程2先执行完成,这个过程中线程1和线程2是同时执行的。

thread1 Method 1 start
thread1 Method 1 execute
thread2 Method 2 start
thread2 Method 2 execute
thread2 Method 2 end
thread1 Method 1 end

1.2、对普通方法同步:

package com.dxz.synchronize;

public class SynchronizedTest2 {
public synchronized void method1() {
System.out.println(Thread.currentThread().getName() + " Method 1 start");
try {
System.out.println(Thread.currentThread().getName() + " Method 1 execute");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 1 end");
} public synchronized void method2() {
System.out.println(Thread.currentThread().getName() + " Method 2 start");
try {
System.out.println(Thread.currentThread().getName() + " Method 2 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 2 end");
} public void method3() {
System.out.println(Thread.currentThread().getName() + " Method 3 start");
try {
System.out.println(Thread.currentThread().getName() + " Method 3 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 3 end");
} public static void main(String[] args) {
//test2demo1();
//test2demo2();
test2demo3();
}
private static void test2demo1() {
final SynchronizedTest2 test = new SynchronizedTest2(); new Thread(new Runnable() {
@Override
public void run() {
test.method1();
}
},"thread1").start(); new Thread(new Runnable() {
@Override
public void run() {
test.method2();
}
},"thread2").start();
}
private static void test2demo2() {
final SynchronizedTest2 test = new SynchronizedTest2();
final SynchronizedTest2 test2 = new SynchronizedTest2(); new Thread(new Runnable() {
@Override
public void run() {
test.method1();
}
},"thread1").start(); new Thread(new Runnable() {
@Override
public void run() {
test2.method2();
}
},"thread2").start();
} private static void test2demo3() {
final SynchronizedTest2 test = new SynchronizedTest2(); new Thread(new Runnable() {
@Override
public void run() {
test.method1();
}
},"thread1").start(); new Thread(new Runnable() {
@Override
public void run() {
test.method3();
}
},"thread2").start();
}
}

test2demo1的执行结果:

thread1 Method 1 start
thread1 Method 1 execute
thread1 Method 1 end
thread2 Method 2 start
thread2 Method 2 execute
thread2 Method 2 end

test2demo2的执行结果:

thread2 Method 2 start
thread2 Method 2 execute
thread1 Method 1 start
thread1 Method 1 execute
thread2 Method 2 end
thread1 Method 1 end

test2demo3的执行结果:

thread1 Method 1 start
thread1 Method 1 execute
thread2 Method 3 start
thread2 Method 3 execute
thread2 Method 3 end
thread1 Method 1 end

2.1、test2demo2测试方法中说明:test和test2属于不同的对象,所以同步互不干扰,线程1和线程2会并行执行。

2.2、通过test2demo1与test2demo3对比说明:同一个类中的所有synchronized修饰的方法是不能同时调用的,也就是说同时只能调用其中一个方法,比如线程1调用method1方法,在method1方法执行完之前,线程2调用method2方法,这个时候线程2就会阻塞,直到线程1调用完method1方法后,线程2才开始执行method2方法。(不仅仅是多个线程调用同一个同步方法)。

2.3、如果线程拥有同步和非同步方法,则非同步方法可以被多个线程自由访问而不受锁的限制。

3、静态方法(类)同步

package com.dxz.synchronize;

public class SynchronizedTest3 {
public static synchronized void method1() {
System.out.println(Thread.currentThread().getName() + " Method 1 start");
try {
System.out.println(Thread.currentThread().getName() + " Method 1 execute");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 1 end");
} public static synchronized void method2() {
System.out.println(Thread.currentThread().getName() + " Method 2 start");
try {
System.out.println(Thread.currentThread().getName() + " Method 2 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 2 end");
} public synchronized void method3() {
System.out.println(Thread.currentThread().getName() + " Method 2 start");
try {
System.out.println(Thread.currentThread().getName() + " Method 2 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 2 end");
} public static void main(String[] args) {
//test3demo1();
//test3demo2();
test3demo3();
} private static void test3demo1() {
final SynchronizedTest3 test = new SynchronizedTest3(); new Thread(new Runnable() {
@Override
public void run() {
test.method1();
}
},"thread1").start(); new Thread(new Runnable() {
@Override
public void run() {
test.method2();
}
},"thread2").start();
} private static void test3demo2() {
final SynchronizedTest3 test = new SynchronizedTest3();
final SynchronizedTest3 test2 = new SynchronizedTest3(); new Thread(new Runnable() {
@Override
public void run() {
test.method1();
}
},"thread1").start(); new Thread(new Runnable() {
@Override
public void run() {
test2.method2();
}
},"thread2").start();
} private static void test3demo3() {
final SynchronizedTest3 test = new SynchronizedTest3(); new Thread(new Runnable() {
@Override
public void run() {
test.method1();
}
},"thread1").start(); new Thread(new Runnable() {
@Override
public void run() {
test.method3();
}
},"thread2").start();
}
}

test3demo1执行结果:

thread1 Method 1 start
thread1 Method 1 execute
thread1 Method 1 end
thread2 Method 2 start
thread2 Method 2 execute
thread2 Method 2 end

test3demo2执行结果:

thread1 Method 1 start
thread1 Method 1 execute
thread1 Method 1 end
thread2 Method 2 start
thread2 Method 2 execute
thread2 Method 2 end

test3demo3执行结果:

thread1 Method 1 start
thread1 Method 1 execute
thread2 Method 2 start
thread2 Method 2 execute
thread2 Method 2 end
thread1 Method 1 end

3.1、test3demo1与test3demo2对比:对静态方法的同步本质上是对类的同步(静态方法本质上是属于类的方法,而不是对象上的方法),所以即使test和test2属于不同的对象,但是它们都属于SynchronizedTest类的实例,所以也只能顺序的执行method1和method2,不能并发执行。

3.2、test3demo2与test3demo3对比说明:实例对象上的锁与类对象的锁是两个,所以可以并行的执行method1和method3。

4、代码块同步

package com.dxz.synchronize;

public class SynchronizedTest4 {
public void method1(){
System.out.println(Thread.currentThread().getName() + " Method 1 start");
try {
synchronized (this) {
System.out.println(Thread.currentThread().getName() + " Method 1 execute");
Thread.sleep(3000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 1 end");
} public void method2(){
System.out.println(Thread.currentThread().getName() + " Method 2 start");
try {
synchronized (this) {
System.out.println(Thread.currentThread().getName() + " Method 2 execute");
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 2 end");
} public static void main(String[] args) {
test4demo1();
} private static void test4demo1() {
final SynchronizedTest4 test = new SynchronizedTest4(); new Thread(new Runnable() {
@Override
public void run() {
test.method1();
}
},"thread1").start(); new Thread(new Runnable() {
@Override
public void run() {
test.method2();
}
},"thread2").start();
} private static void test4demo2() {
final SynchronizedTest4 test = new SynchronizedTest4();
final SynchronizedTest4 test2 = new SynchronizedTest4(); new Thread(new Runnable() {
@Override
public void run() {
test.method1();
}
},"thread1").start(); new Thread(new Runnable() {
@Override
public void run() {
test2.method2();
}
},"thread2").start();
}
}

test4demo1执行结果:

thread1 Method 1 start
thread1 Method 1 execute
thread2 Method 2 start
thread1 Method 1 end
thread2 Method 2 execute
thread2 Method 2 end

test4demo2执行结果:

thread1 Method 1 start
thread2 Method 2 start
thread2 Method 2 execute
thread1 Method 1 execute
thread2 Method 2 end
thread1 Method 1 end

4.1、虽然线程1和线程2都进入了对应的方法开始执行,但是线程2在进入同步块之前,需要等待线程1中同步块执行完成。

 

锁之“重量级锁”Synchronized的更多相关文章

  1. 线程安全(中)--彻底搞懂synchronized(从偏向锁到重量级锁)

    接触过线程安全的同学想必都使用过synchronized这个关键字,在java同步代码快中,synchronized的使用方式无非有两个: 通过对一个对象进行加锁来实现同步,如下面代码. synchr ...

  2. synchronized底层实现原理&CAS操作&偏向锁、轻量级锁,重量级锁、自旋锁、自适应自旋锁、锁消除、锁粗化

    进入时:monitorenter 每个对象有一个监视器锁(monitor).当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:1 ...

  3. java 偏向锁、轻量级锁及重量级锁synchronized原理

    Java对象头与Monitor java对象头是实现synchronized的锁对象的基础,synchronized使用的锁对象是存储在Java对象头里的. 对象头包含两部分:Mark Word 和 ...

  4. 【锁】synchronized的实现(偏向锁、轻量级锁、重量级锁)

    synchronized的三种应用方式 一. 修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁. 二. 修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁. 三. ...

  5. 关于Synchronized的偏向锁,轻量级锁,重量级锁,锁升级过程,自旋优化,你该了解这些

    前言 相信大部分开发人员,或多或少都看过或写过并发编程的代码.并发关键字除了Synchronized(如有不懂请移至传送门,关于Synchronized的偏向锁,轻量级锁,重量级锁,锁升级过程,自旋优 ...

  6. synchronized和 synchronized 了解偏向锁、轻量级锁、重量级锁的概念以及升级机制、以及和ReentrantLock的区别。

    并发 synchronized 了解偏向锁.轻量级锁.重量级锁的概念以及升级机制.以及和ReentrantLock的区别.       https://www.cnblogs.com/deltadeb ...

  7. JVM锁简介:偏向锁、轻量级锁和重量级锁

    转自:https://www.aimoon.site/blog/2018/05/21/biased-locking/ 比较复杂,简略见另一篇:https://www.cnblogs.com/twohe ...

  8. Java 多线程之:偏向锁,轻量级锁,重量级锁

    一:java多线程互斥,和java多线程引入偏向锁和轻量级锁的原因? --->synchronized的重量级别的锁,就是在线程运行到该代码块的时候,让程序的运行级别从用户态切换到内核态,把所有 ...

  9. JAVA对象分析之偏向锁、轻量级锁、重量级锁升级过程

    在HotSpot虚拟机里,对象在堆内存中的存储布局可以划分为三个部分: 对象头(Header) 实例数据(Instance Data) 对齐填充(Padding). 对象头 HotSpot虚拟机(后面 ...

  10. jvm锁的四种状态 无锁状态 偏向锁状态 轻量级锁状态 重量级锁状态

    一:java多线程互斥,和java多线程引入偏向锁和轻量级锁的原因? --->synchronized是在jvm层面实现同步的一种机制.  jvm规范中可以看到synchronized在jvm里 ...

随机推荐

  1. C#之使用AutoResetEvent实现线程的顺序执行

    前几天一朋友问我如何实现线程的顺序执行,说真的,虽然看过CLR这本书,也把线程部分拜读了两遍,但是这个问题出来之后还是没有一个思路.今天在搜索资料的时候无意中再次看到AutoResetEvent这个东 ...

  2. Android SurfaceView + MediaPlayer实现分段视频无缝播放

    Android当中实现视频播放的方式有两种,即:通过VideoView实现或者通过SurfaceView + MediaPlayer实现. 由浅至深,首先来看下想要在Android上播放一段视频,我们 ...

  3. Oracle ->> 连续聚合

    select id, grp_factor, sum (id) over( partition by grp_factor order by id rows between unbounded pre ...

  4. Socket网络通讯开发总结之:Java 与 C进行Socket通讯 + [备忘] Java和C之间的通讯

    Socket网络通讯开发总结之:Java 与 C进行Socket通讯 http://blog.sina.com.cn/s/blog_55934df80100i55l.html (2010-04-08 ...

  5. spry菜单栏(二)

    自定义选项卡式面板构件 尽管使用属性检查器可以简化对选项卡式面板构件的编辑,但是属性检查器并不支持自定义的样式设置任务.您可以修改选项卡式面板构件的 CSS 规则,并创建根据自己的喜好设置样式的构件. ...

  6. Linux 下sleep()函数

    调试程序发现起了一个子线程后,主线程上的sleep不生效了,看到这才明白... — Function: unsigned int sleep (unsigned int seconds) The sl ...

  7. AbsListView.OnScrollListener 使用注意事项

    这个类没什么特别的,但是使用的时候我确出错了 abstract void onScroll(AbsListView view, int firstVisibleItem, int visibleIte ...

  8. Diving Into Lync Client Logins

    Now that we have a fully functional UC lab it's time to start using the lab to explore various aspec ...

  9. Android之TelephonyManager类的使用案例

    TelephonyManager类主要提供了一系列用于访问与手机通讯相关的状态和信息的get方法.其中包括手机SIM的状态和信息.电信网络的状态及手机用户的信息.在应用程序中可以使用这些get方法获取 ...

  10. powerdesigner 15 如何导出sql schema

    PowerDesigner导出所有SQL脚本 操作:Database=>Generate Database PowerDesigner怎么导出建表sql脚本 1 按照数据库类型,切换数据库. D ...