多线程总结-同步之synchronized关键字
目录
1.为什么要使用synchronized?
在并发编程中存在线程安全问题,主要原因有:
1.存在共享数据
2.多线程共同操作共享数据。关键字synchronized可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块,同时synchronized可以保证一个线程的变化可见(可见性)
2.synchronized锁什么,加锁的目的是什么?
synchronized锁的是对象,锁的的对象可能是this、临界资源对象、class类对象
加锁的目的是保证操作的原子性
3.代码示例
3.1锁this和临界资源对象
本例中:
- testSync1是synchronized代码块,锁的是object,临界资源对象
- testSync2和testSync3都是锁的是当前对象this
/**
* synchronized关键字
* 锁对象。synchronized(this)和synchronized方法都是锁当前对象。
*/
package com.bernardlowe.concurrent.t01;
import java.util.concurrent.TimeUnit;
public class Test_01 {
private int count = 0;
private Object object = new Object();
// 1.synchronized代码块,锁的是object,临界资源对象
public void testSync1(){
synchronized(object){
System.out.println(Thread.currentThread().getName()
+ " count = " + count++);
}
}
// 2.synchronized代码块,锁的是当前对象this
public void testSync2(){
synchronized(this){
System.out.println(Thread.currentThread().getName()
+ " count = " + count++);
}
}
// 2.synchronized代码,锁的也是this
public synchronized void testSync3(){
System.out.println(Thread.currentThread().getName()
+ " count = " + count++);
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
final Test_01 t = new Test_01();
new Thread(new Runnable() {
@Override
public void run() {
t.testSync3();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
t.testSync3();
}
}).start();
}
}
3.2锁class类对象
/**
* synchronized关键字
* 同步方法 - static
* 静态同步方法,锁的是当前类型的类对象。在本代码中就是Test_02.class
*/
package com.bernardlowe.concurrent.t01;
import java.util.concurrent.TimeUnit;
public class Test_02 {
private static int staticCount = 0;
public static synchronized void testSync4(){
System.out.println(Thread.currentThread().getName()
+ " staticCount = " + staticCount++);
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void testSync5(){
synchronized(Test_02.class){
System.out.println(Thread.currentThread().getName()
+ " staticCount = " + staticCount++);
}
}
}
3.3 什么时候锁临界资源,什么时候锁当前对象?
如果在加锁的时候需要对当前对象的访问限定,建议锁临界资源(即锁一个临界资源),如果对当前锁级别比较高的话,就锁当前对象。建议都以同步代码块的方式进行开发,这样可以避免锁的范围太高
同步方法和非同步方法
提问:同步方法是否影响其他线程调用非同步方法,或调用其他锁资源的同步方法?
代码示例
m1是非同步方法,m2是同步方法,m3同步代码块,锁的临界资源,这段代码的目的是为了证明在调用同步方法m1时,m2,m3是否能够执行
/**
* synchronized关键字
* 同步方法 - 同步方法和非同步方法的调用
* 同步方法只影响锁定同一个锁对象的同步方法。不影响其他线程调用非同步方法,或调用其他锁资源的同步方法。
*/
package com.bernardlowe.concurrent.t01;
public class Test_04 {
Object o = new Object();
public synchronized void m1(){ // 重量级的访问操作。
System.out.println("public synchronized void m1() start");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("public synchronized void m1() end");
}
public void m3(){
synchronized(o){
System.out.println("public void m3() start");
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("public void m3() end");
}
}
public void m2(){
System.out.println("public void m2() start");
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("public void m2() end");
}
public static class MyThread01 implements Runnable{
public MyThread01(int i, Test_04 t){
this.i = i;
this.t = t;
}
int i ;
Test_04 t;
public void run(){
if(i == 0){
t.m1();
}else if (i > 0){
t.m2();
}else {
t.m3();
}
}
}
public static void main(String[] args) {
Test_04 t = new Test_04();
new Thread(new MyThread01(0, t)).start();
new Thread(new MyThread01(1, t)).start();
new Thread(new MyThread01(-1, t)).start();
}
}
执行结果
可以看出,在执行同步方法m1时,m2,m3不受影响,依然可以执行
结论:同步方法只影响锁定同一个锁对象的同步方法。不影响其他线程调用非同步方法,或调用其他锁资源的同步方法。
但有一点需要注意,尽量在商业开发中避免同步方法。使用同步代码块。 细粒度解决同步问题。
3.3 重入锁
这里重入锁分为两类:
- 1.在同步方法里面调用其他同步方法
- 2.子类同步方法覆盖父类同步方法
下面来看第一种:在同步方法里面调用其他同步方法
思考:调用m1()方法,m2()方法是否会执行?
/**
*synchronized关键字
*同步方法 - 调用其他同步方法
*/
package com.bernardlowe.concurrent.t01;
import java.util.concurrent.TimeUnit;
public class Test_06 {
synchronized void m1(){ // 锁this
System.out.println("m1 start");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
m2();
System.out.println("m1 end");
}
synchronized void m2(){ // 锁this
System.out.println("m2 start");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("m2 end");
}
public static void main(String[] args) {
new Test_06().m1();
}
}
结果:在m1()方法调用时,m2()方法依然可执行。多次调用同步代码,锁定同一个锁对象,是可重入的
第二种情况:子类同步方法覆盖父类同步方法
思考:子类同步方法m()中,调用父类同步方法m(),是否可重入?
/**
* synchronized关键字
* 同步方法 - 继承
* 子类同步方法覆盖父类同步方法。可以指定调用父类的同步方法。
* 相当于锁的重入。
*/
package com.bernardlowe.concurrent.t01;
import java.util.concurrent.TimeUnit;
public class Test_07 {
synchronized void m(){
System.out.println("Super Class m start");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Super Class m end");
}
public static void main(String[] args) {
new Sub_Test_07().m();
}
}
class Sub_Test_07 extends Test_07{
synchronized void m(){
System.out.println("Sub Class m start");
super.m();
System.out.println("Sub Class m end");
}
}
结果:子类同步方法m()中,调用了父类同步方法m(),可以重入
3.4 锁与异常
思考:当同步方法或同步代码块中发生异常,是否会影响其他线程的执行?
下面来看一段代码
/**
* synchronized关键字
* 同步方法 - 锁与异常
*/
package com.bernardlowe.concurrent.t01;
import java.util.concurrent.TimeUnit;
public class Test_08 {
int i = 0;
synchronized void m(){
System.out.println(Thread.currentThread().getName() + " - start");
while(true){
i++;
System.out.println(Thread.currentThread().getName() + " - " + i);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 当i==5时会抛出异常
if(i == 5){
i = 1/0;
}
}
}
public static void main(String[] args) {
final Test_08 t = new Test_08();
new Thread(new Runnable() {
@Override
public void run() {
t.m();
}
}, "t1").start();
new Thread(new Runnable() {
@Override
public void run() {
t.m();
}
}, "t2").start();
}
}
这段代码中先运行了两个线程t1、t2,当其中一个线程发生异常时,另外一个线程是否能继续执行
结果:当同步方法中发生异常的时候,自动释放锁资源。不会影响其他线程的执行。
思考: 同步业务逻辑中,如果发生异常如何处理?
比如上面会发生异常的代码中,可以这样
if(i == 5){
try {
i = 1/0;
} catch (Exception e) {
i = 0;
}
}
3.5 锁对象变更问题
代码示例:
思考:当一个线程执行同步方法时,另一个线程修改了锁对象,是否还能执行同步代码块
/**
* synchronized关键字
* 锁对象变更问题
*/
package com.bernardlowe.concurrent.t01;
import java.util.concurrent.TimeUnit;
public class Test_13 {
Object o = new Object();
void m(){
System.out.println(Thread.currentThread().getName() + " start");
synchronized (o) {
while(true){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " - " + o);
}
}
}
public static void main(String[] args) {
final Test_13 t = new Test_13();
new Thread(new Runnable() {
@Override
public void run() {
t.m();
}
}, "thread1").start();
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
t.m();
}
}, "thread2");
t.o = new Object();
thread2.start();
}
}
结论:可以看出,其他线程依然可以执行同步方法
因为同步代码一旦加锁后,那么会有一个临时的锁引用执行锁对象,和真实的引用无直接关联。在锁未释放之前,修改锁对象引用,不会影响同步代码的执行。
注意:不要使用静态常量作为锁对象
如下代码,因为String常量池的问题,s1,s1是同一个对象,所以m1,m2方法锁的是也同一个对象,m1同步方法被执行后,m2方法不会被执行
/**
* synchronized关键字
* 常量问题
*/
package com.bernardlowe.concurrent.t01;
public class Test_14 {
String s1 = "hello";
String s2 = "hello";
// String s2 = new String("hello"); // new关键字,一定是在堆中创建一个新的对象。
void m1(){
synchronized (s1) {
System.out.println("m1()");
while(true){
}
}
}
void m2(){
synchronized (s2) {
System.out.println("m2()");
while(true){
}
}
}
public static void main(String[] args) {
final Test_14 t = new Test_14();
new Thread(new Runnable() {
@Override
public void run() {
t.m1();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
t.m2();
}
}).start();
}
}
多线程总结-同步之synchronized关键字的更多相关文章
- 多线程,线程同步,synchronized关键字的用法
一.什么是多线程 Java多线程实现方式主要有四种:继承Thread类.实现Runnable接口.实现Callable接口通过FutureTask包装器来创建Thread线程.使用ExecutorSe ...
- Java:多线程,线程同步,synchronized关键字的用法(同步代码块、非静态同步方法、静态同步方法)
关于线程的同步,可以使用synchronized关键字,或者是使用JDK 5中提供的java.util.concurrent.lock包中的Lock对象.本文探讨synchronized关键字. sy ...
- Java多线程(四)—— synchronized关键字续
1.synchronized原理 在java中,每一个对象有且仅有一个同步锁.这也意味着,同步锁是依赖于对象而存在.当我们调用某对象的synchronized方法时,就获取了该对象的同步锁.例如,sy ...
- Java多线程(三)—— synchronized关键字详解
一.多线程的同步 1.为什么要引入同步机制 在多线程环境中,可能会有两个甚至更多的线程试图同时访问一个有限的资源.必须对这种潜在资源冲突进行预防. 解决方法:在线程使用一个资源时为其加锁即可. 访问资 ...
- [多线程] 线程中的synchronized关键字锁
为什么要用锁? 在多线程中,难免会出现在多个线程中对同一个对象的实例变量或者全局静态变量进行并发访问的情况,如果不做正确的同步处理,那么产生的后果就是"脏读",也就是取到的数据其实 ...
- Java基础-多线程-③线程同步之synchronized
使用线程同步解决多线程安全问题 上一篇 Java基础-多线程-②多线程的安全问题 中我们说到多线程可能引发的安全问题,原因在于多个线程共享了数据,且一个线程在操作(多为写操作)数据的过程中,另一个线程 ...
- Java多线程的同步机制(synchronized)
一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在 java里边就是拿到某个同步对象的锁(一个对象只有一把锁): 如果这个时候同步对象的锁被其他线程拿走了,他(这个 ...
- Java 多线程(六) synchronized关键字详解
多线程的同步机制对资源进行加锁,使得在同一个时间,只有一个线程可以进行操作,同步用以解决多个线程同时访问时可能出现的问题. 同步机制可以使用synchronized关键字实现. 当synchroniz ...
- 简单的互斥同步方式——synchronized关键字详解
目录 1. 关于synchronized关键字 2. synchronized的原理和实现细节 2.1 synchronized可以用在那些地方 2.2 synchronized是如何实现线程互斥访问 ...
随机推荐
- windows下服务程序相关(别人提供的5种封装使用)
作者: daodaoliang 版本: V 0.0.1 日期: 2017年11月25日 1. Windows Service 编程实现 在windows平台下面编写 服务程序 免不了要去查看微软的开发 ...
- 微信小程序把玩(二十三)modal组件
原文:微信小程序把玩(二十三)modal组件 modal弹出框常用在提示一些信息比如:退出应用,清楚缓存,修改资料提交时一些提示等等. 常用属性: wxml <!--监听button点击事件-- ...
- Excel的Range对象(C#)
原文:Excel的Range对象(C#) Range 对象是 Excel 应用程序中最经常使用的对象:在操作 Excel 内的任何区域之前,都需要将其表示为一个 Range 对象,然后使用该 Rang ...
- Android零基础入门第63节:过时但仍值得学习的选项卡TabHost
原文:Android零基础入门第63节:过时但仍值得学习的选项卡TabHost 由于前几天参加一个学习培训活动,几乎每天都要从早晨7点到晚上一两点,没有什么时间来分享,实在抱歉中间断更了几天.从今天开 ...
- chrome 里面js提示Provisional headers are shown错误
参考:http://stackoverflow.com/questions/21177387/caution-provisional-headers-are-shown-in-chrome-debug ...
- spring.net的简单使用(四)对象属性注入
创建了对象,如果是简单对象就到此为止,如果是复杂对象,则需要为它的属性赋值. 属性赋值有两种方法:属性注入和构造器注入. 一.属性注入 在object节点下使用property就是属性注入,如下: & ...
- C#实现bitmap图像矫正
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...
- MinGW gcc 生成动态链接库 dll 的一些问题汇总(由浅入深,很详细)
网络上关于用 MinGW gcc 生成动态链接库的文章很多.介绍的方法也都略有不同.这次我在一个项目上刚好需要用到,所以就花了点时间将网上介绍的各种方法都实验了一遍.另外,还根据自己的理解试验了些网上 ...
- 在 Visual Studio中 将 Objective-C 编译为 C++
快速使用Romanysoft LAB的技术实现 HTML 开发Mac OS App,并销售到苹果应用商店中. <HTML开发Mac OS App 视频教程> 土豆网同步更新:http: ...
- 小试X64 inline HOOK,hook explorer.exe--->CreateProcessInternalW监视进程创建
原始函数是这样的 kernel32!CreateProcessInternalW: 00000000`7738e750 4c8bdc mov r11,rsp 00000000 ...