1.synchronized的作用

首先synchronized可以修饰方法或代码块,可以保证同一时刻只有一个线程可以执行这个方法或代码块,从而达到同步的效果,同时可以保证共享变量的内存可见性

2.synchronized的实现原理

首先,每个java对象都可以作为一个对象锁,对象锁分为三种情况对应三种对象锁,分别是方法锁、对象锁和类锁,实际都是对象锁,只是针对不同用法方便区分所以分别定义了一种锁。这个是理解synchronized实现同步的基础

3.synchronized用法详解

synchronized主要有三种用法
修饰方法:方法锁,锁的对象是当前的对象
修饰静态方法:类锁,锁的对象是当前的类,实际是这个类的.class对象
修饰代码块:对象锁,锁的对象是synchronized修饰的对象

4.synchronized用法案例

synchroinzed主要的用法如下案例

public  class  SynchronizedDemo {

    /**
* synchronized只可修饰方法或代码块,不可修饰变量
* */ public static Integer age = 10; /**
* synchronized修饰非静态方法
* */
public synchronized void test1() {
System.out.println("这个是非静态方法,需要获得当前实例的对象锁才可以访问");
} /**
* synchronized修饰静态方法
* */
public synchronized static void test2() {
System.out.println("这个是静态方法,需要获得当前类的锁才可以访问");
} public void test3() {
/**
* synchronized修饰代码块:锁的对象是当前对象
* */
synchronized (this) {
System.out.println("锁代码块,需要获取当前对象的锁");
}
} public static void test4(){
/**
* synchronized修饰代码块:锁的对象是Integer类型对象age
* */
synchronized (age) {
System.out.println("锁代码块,需要获取age对象的锁");
}
} public void test5(){
/**
* synchronized修饰代码块:锁的对象是User.class这个对象,由于这个对象只有一个,所以效果相当于类锁
* */
synchronized(User.class){
System.out.println("锁代码块,需要获取User类.class对象的锁,相当于类锁");
}
}
}

5.synchronized关键字不同情况下同步情况(*面试必备*)

5.1、同步非静态方法test1(),不同线程调用同一个对象的该方法则同步,调用不同对象的该方法则不同步,即线程threadA和线程theadB两个线程,两个对象demo1,demo2
则threadA和threadB都调用demo1.test1()或demo2.test1()则同步,分别调用demo1.test1()和demo2.test1()方法则不同步,完整Demo如下:

/**
* 非静态方法test1方法测试(不同线程调用不同对象的非静态同步方法)
* */
public static void test1_test(){
SynchronizedDemo demoA = new SynchronizedDemo();
SynchronizedDemo demoB = new SynchronizedDemo(); Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("----->开始执行线程=>"+Thread.currentThread().getName()+":需要锁的对象是:"+demoA.toString());
demoA.test1();
}
}); Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("----->开始执行线程=>"+Thread.currentThread().getName()+":需要锁的对象是:"+demoA.toString());
demoA.test1();
}
});
Thread t3 = new Thread(new Runnable() { @Override
public void run() {
System.out.println("----->开始执行线程=>"+Thread.currentThread().getName()+":需要锁的对象是:"+demoB.toString());
demoB.test1();
}
});
//线程t1执行变量A的test1方法
t1.start();
//线程t2执行变量A的test1方法
t2.start();
//线程t3执行变量B的test1方法
t3.start();
}

执行结果如下:

----->开始执行线程=>Thread-2:需要锁的对象是:com.test.lucky.concurrent.SynchronizedDemo@46cf8b7b
----->开始执行线程=>Thread-0:需要锁的对象是:com.test.lucky.concurrent.SynchronizedDemo@6426ac69
----->开始执行线程=>Thread-1:需要锁的对象是:com.test.lucky.concurrent.SynchronizedDemo@6426ac69
这个是非静态方法,需要获得当前实例的对象com.test.lucky.concurrent.SynchronizedDemo@6426ac69锁才可以访问
这个是非静态方法,需要获得当前实例的对象com.test.lucky.concurrent.SynchronizedDemo@46cf8b7b锁才可以访问
Thread-2休眠第1秒
Thread-0休眠第1秒
Thread-2休眠第2秒
Thread-0休眠第2秒
Thread-2休眠第3秒
Thread-0休眠第3秒
Thread-0休眠第4秒
Thread-2休眠第4秒
Thread-2休眠第5秒
Thread-0休眠第5秒
这个是非静态方法,需要获得当前实例的对象com.test.lucky.concurrent.SynchronizedDemo@6426ac69锁才可以访问
Thread-1休眠第1秒
Thread-1休眠第2秒
Thread-1休眠第3秒
Thread-1休眠第4秒
Thread-1休眠第5秒

结论:

可以看出t1和t2执行同一个变量A的test1方法,t3执行另一个变量B的test1方法,则t1和t2同步,和t3则不同步。
因为t1和t2都需要获取对象demoA的对象锁,同一时间只能有一个线程能获取到,而t3是获取对象demoB的对象锁

5.2、同步静态方法test2(),不同线程不管是调用同一个对象还是不同对象的该方法,都是调用该类的静态方法,都需要同步
即线程threadA和线程theadB两个线程,两个对象demo1,demo2
则threadA和threadB都调用demo1.test2()则同步,分别调用demo1.test2()和demo2.test2()方法则也同步,直接调用该类的test2()方法也同步,完整Demo如下:

/**
* 静态方法test2方法测试(不同线程调用不同或相同对象的静态方法)
* */
public static void test2_test(){
final SynchronizedDemo demoA = new SynchronizedDemo();
final SynchronizedDemo demoB = new SynchronizedDemo(); Thread t1 = new Thread(new Runnable() { public void run() {
System.out.println("----->开始执行线程=>"+Thread.currentThread().getName()+":需要锁的对象是:"+SynchronizedDemo.class.toString());
demoA.test2();
}
}); Thread t2 = new Thread(new Runnable() { public void run() {
System.out.println("----->开始执行线程=>"+Thread.currentThread().getName()+":需要锁的对象是:"+SynchronizedDemo.class.toString());
demoB.test2();
}
}); Thread t3 = new Thread(new Runnable() {
public void run() {
System.out.println("----->开始执行线程=>"+Thread.currentThread().getName()+":需要锁的对象是:"+SynchronizedDemo.class.toString());
SynchronizedDemo.test2();
}
});
//线程t1执行变量A的test1方法
t1.start();
//线程t2执行变量A的test1方法
t2.start();
//线程t3执行变量B的test1方法
t3.start();
}

执行结果如下:

执行结果如下:
----->开始执行线程=>Thread-0:需要锁的对象是:class com.codehelp.luck.concurrent.SynchronizedDemo
这个是静态方法,需要获得当前类的锁class com.codehelp.luck.concurrent.SynchronizedDemo才可以访问
----->开始执行线程=>Thread-1:需要锁的对象是:class com.codehelp.luck.concurrent.SynchronizedDemo
----->开始执行线程=>Thread-2:需要锁的对象是:class com.codehelp.luck.concurrent.SynchronizedDemo
Thread-0休眠第1秒
Thread-0休眠第2秒
Thread-0休眠第3秒
Thread-0休眠第4秒
Thread-0休眠第5秒
这个是静态方法,需要获得当前类的锁class com.codehelp.luck.concurrent.SynchronizedDemo才可以访问
Thread-2休眠第1秒
Thread-2休眠第2秒
Thread-2休眠第3秒
Thread-2休眠第4秒
Thread-2休眠第5秒
这个是静态方法,需要获得当前类的锁class com.codehelp.luck.concurrent.SynchronizedDemo才可以访问
Thread-1休眠第1秒
Thread-1休眠第2秒
Thread-1休眠第3秒
Thread-1休眠第4秒
Thread-1休眠第5秒

结论:

线程1通过变量demoA调用静态方法test2(),线程2通过变量demoB调用静态方法test2(),线程3通过直接调用类SynchronizedDemo.test2(),不同线程不同调用方式,都是需要锁SynchronizedDemo.class这点对象,也就是都需要获取类锁,

所以静态同步方法,无论什么场景都会达到同步的效果。

5.3、同步代码块,锁的是当前对象,则不同线程调用该对象的所有同步方法都会同步,效果和5.1一样,案例代码如下:

首先定义两个逻辑完全一样的方法test3(),test33()

public void test3() {
/**
* synchronized修饰代码块:锁的对象是当前对象
* */
synchronized (this) {
System.out.println("锁代码块方法test3,需要获取当前对象的锁"+this.toString());
try {
for (int i = 1; i <= 5; i++) {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"休眠第"+i+"秒");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public void test33() {
/**
* synchronized修饰代码块:锁的对象是当前对象
* */
synchronized (this) {
System.out.println("锁代码块test33,需要获取当前对象的锁"+this.toString());
try {
for (int i = 1; i <= 5; i++) {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"休眠第"+i+"秒");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

测试代码如下:

    /**
* test3方法测试
* */
public static void test3_test(){
final SynchronizedDemo demoA = new SynchronizedDemo(); Thread t1 = new Thread(new Runnable() { public void run() {
System.out.println("----->开始执行线程=>"+Thread.currentThread().getName()+":需要锁的对象是:"+demoA.toString());
demoA.test3();
}
}); Thread t2 = new Thread(new Runnable() { public void run() {
System.out.println("----->开始执行线程=>"+Thread.currentThread().getName()+":需要锁的对象是:"+demoA.toString());
demoA.test3();
}
}); Thread t3 = new Thread(new Runnable() {
public void run() {
System.out.println("----->开始执行线程=>"+Thread.currentThread().getName()+":需要锁的对象是:"+demoA.toString());
demoA.test33();
}
});
//线程t1执行变量A的test1方法
t1.start();
//线程t2执行变量A的test1方法
t2.start();
//线程t3执行变量A的test1方法
t3.start();
}

执行结果如下:

----->开始执行线程=>Thread-1:需要锁的对象是:com.codehelp.luck.concurrent.SynchronizedDemo@568a65a0
----->开始执行线程=>Thread-0:需要锁的对象是:com.codehelp.luck.concurrent.SynchronizedDemo@568a65a0
锁代码块方法test3,需要获取当前对象的锁com.codehelp.luck.concurrent.SynchronizedDemo@568a65a0
----->开始执行线程=>Thread-2:需要锁的对象是:com.codehelp.luck.concurrent.SynchronizedDemo@568a65a0
Thread-1休眠第1秒
Thread-1休眠第2秒
Thread-1休眠第3秒
Thread-1休眠第4秒
Thread-1休眠第5秒
锁代码块test33,需要获取当前对象的锁com.codehelp.luck.concurrent.SynchronizedDemo@568a65a0
Thread-2休眠第1秒
Thread-2休眠第2秒
Thread-2休眠第3秒
Thread-2休眠第4秒
Thread-2休眠第5秒
锁代码块方法test3,需要获取当前对象的锁com.codehelp.luck.concurrent.SynchronizedDemo@568a65a0
Thread-0休眠第1秒
Thread-0休眠第2秒
Thread-0休眠第3秒
Thread-0休眠第4秒
Thread-0休眠第5秒

结论:

不管是调用对象demoA的test3方法还是test33方法,都是需要获取demoA的对象锁,所以不同线程只要是执行demeA的同步方法或是同步代码块,都需要获取demoA对象锁,所以都会同步

5.4、同步代码块,锁的对象是非静态变量则不同线程调用不同变量的该方法不同步,调用相同实例的该方法则同步;锁的对象是静态变量,则不管调用是哪个实例的该方法都需要同步

private User user1 = new User();//定义一个非静态变量

private static User user2 = new User();//定义一个静态变量

public void test4(){
/**
* synchronized修饰代码块:锁的对象是User类型对象user1
* */
synchronized (user1) {
System.out.println("锁代码块,需要获取对象"+user1.toString()+"的锁");
try {
for (int i = 1; i <= 5; i++) {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"休眠第"+i+"秒");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public void test44(){
/**
* synchronized修饰代码块:锁的对象是静态变量User类型对象user2
* */
synchronized (user2) {
System.out.println("锁代码块,需要获取对象"+user2.toString()+"的锁");
try {
for (int i = 1; i <= 5; i++) {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"休眠第"+i+"秒");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

SynchronizedDemo有两个属性,一个是变量user1,一个是静态变量user2,测试代码如下:

/**
* test4方法测试
* */
public static void test4_test(){
final SynchronizedDemo demoA = new SynchronizedDemo();
final SynchronizedDemo demoB = new SynchronizedDemo(); Thread t1 = new Thread(new Runnable() { public void run() {
System.out.println("----->开始执行线程=>"+Thread.currentThread().getName()+":需要锁的对象是:"+demoA.user1.toString());
demoA.test4();
}
}); Thread t2 = new Thread(new Runnable() { public void run() {
System.out.println("----->开始执行线程=>"+Thread.currentThread().getName()+":需要锁的对象是:"+demoB.user1.toString());
demoB.test4();
}
}); Thread t3 = new Thread(new Runnable() {
public void run() {
System.out.println("----->开始执行线程=>"+Thread.currentThread().getName()+":需要锁的对象是:"+user2.toString());
demoA.test44();
}
}); Thread t4 = new Thread(new Runnable() {
public void run() {
System.out.println("----->开始执行线程=>"+Thread.currentThread().getName()+":需要锁的对象是:"+user2.toString());
demoB.test44();
}
});
//线程t1执行变量A的test4方法
t1.start();
//线程t2执行变量B的test4方法
t2.start();
//线程t3执行变量A的test44方法
t3.start();
//线程t4执行变量B的test44方法
t4.start();
}

线程t1和t2分别调用实例demoA和demoB的test4方法,线程t3和t4分别调用实例demoA和demoB的test44方法,执行结果如下:

----->开始执行线程=>Thread-0:需要锁的对象是:com.codehelp.lucky.model.User@68ee5860
锁代码块方法test4,需要获取对象com.codehelp.lucky.model.User@68ee5860的锁
----->开始执行线程=>Thread-1:需要锁的对象是:com.codehelp.lucky.model.User@7d9f22fa
锁代码块方法test4,需要获取对象com.codehelp.lucky.model.User@7d9f22fa的锁
----->开始执行线程=>Thread-3:需要锁的对象是:com.codehelp.lucky.model.User@491c6a05
----->开始执行线程=>Thread-2:需要锁的对象是:com.codehelp.lucky.model.User@491c6a05
锁代码块方法test44,需要获取对象com.codehelp.lucky.model.User@491c6a05的锁
Thread-0休眠第1秒
Thread-1休眠第1秒
Thread-3休眠第1秒
Thread-0休眠第2秒
Thread-1休眠第2秒
Thread-3休眠第2秒
Thread-0休眠第3秒
Thread-1休眠第3秒
Thread-3休眠第3秒
Thread-0休眠第4秒
Thread-3休眠第4秒
Thread-1休眠第4秒
Thread-0休眠第5秒
Thread-3休眠第5秒
Thread-1休眠第5秒
锁代码块方法test44,需要获取对象com.codehelp.lucky.model.User@491c6a05的锁
Thread-2休眠第1秒
Thread-2休眠第2秒
Thread-2休眠第3秒
Thread-2休眠第4秒
Thread-2休眠第5秒

结论:

可以看出线程t1和t2获取的是不同对象的属性user1对象锁,不会同步,而t3和t4则获取的都是静态变量user2的锁,会同步

5.5、同步方法块,锁的对象是类,即User.class对象,则同于5.4案例的t3和t4线程结果一样,由于是类锁,所以肯定会同步(有兴趣同学可自行测试)

总结:
1.判断两个线程调用同步方法或同步块会不会同步的前提是需要理解线程需要获取的锁的对象是什么,获取的对象锁相同则会同步,不同对象锁则不会同步;
2.同步方法:锁的对象是当前对象,如SynchroizedDmoe demoA,则锁的是demoA这个对象
3.同步静态方法:锁的对象是当前类,即锁的是SynchronizedDemo.class这个对象
3.同步方法块,锁的对象是啥就是获取啥的对象锁,
   synchronized(this){},则锁的是当前对象,和同步方法一样
   synchronized(非静态变量),则锁的是这个变量的所属对象
   synchronized(类),则锁的是这个类的.class对象
4.方法锁、对象锁、类锁实际都是某个对象的锁,只需找到锁的对象是什么,即可判定会不会达到同步效果。

ps:本篇主要分析了Synchronized关键字的基本作用及基本用法,下一篇主要分析Synchronized的底层实现原理及Synchronized的优化

Java并发编程1--synchronized关键字用法详解的更多相关文章

  1. 【Java并发编程】Synchronized关键字实现原理

    想必在面试中经常会被问到Synchronized关键字,它有什么特性,原理什么 它的主要特性是同步锁.非公平锁.阻塞锁.可以保证线程安全(可见性.原子性.有序性) JDK1.6之后对Synchroni ...

  2. Java 并发编程(一) → LockSupport 详解

    开心一刻 今天突然收到花呗推送的消息,说下个月 9 号需要还款多少钱 我就纳了闷了,我很长时间没用花呗了,怎么会欠花呗钱? 后面我一想,儿子这几天玩了我手机,是不是他偷摸用了我的花呗 于是我找到儿子问 ...

  3. Java并发编程基础--基本线程方法详解

    什么是线程 线程是操作系统调度的最小单位,一个进程中可以有多个线程,这些线程可以各自的计数器,栈,局部变量,并且能够访问共享的内存变量.多线程的优势是可以提高响应时间和吞吐量. 使用多线程 一个进程正 ...

  4. Java并发编程:Synchronized及其实现原理

    Java并发编程系列: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) Java 并发编程 ...

  5. Java并发编程:volatile关键字解析

    Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在 ...

  6. Java并发编程:synchronized

    Java并发编程:synchronized 虽然多线程编程极大地提高了效率,但是也会带来一定的隐患.比如说两个线程同时往一个数据库表中插入不重复的数据,就可能会导致数据库中插入了相同的数据.今天我们就 ...

  7. (转)Java并发编程:volatile关键字解析

    转:http://www.cnblogs.com/dolphin0520/p/3920373.html Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或 ...

  8. Java并发编程:volatile关键字解析(转载)

    转自https://www.cnblogs.com/dolphin0520/p/3920373.html Java并发编程:volatile关键字解析   Java并发编程:volatile关键字解析 ...

  9. Java并发编程:Synchronized底层优化(偏向锁、轻量级锁)

    Java并发编程系列: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) Java 并发编程 ...

  10. 【转】Java并发编程:synchronized

    一.什么时候会出现线程安全问题? 在单线程中不会出现线程安全问题,而在多线程编程中,有可能会出现同时访问同一个资源的情况,这种资源可以是各种类型的资源:一个变量.一个对象.一个文件.一个数据库表等,而 ...

随机推荐

  1. Asp.net Mvc post表单提交多个实体模型

    上一遍说道用Tuple实现Asp.net Mvc action返回多个模型实体给view,此篇发过来,实现view表单提交多个实体模型到action. 1.view代码: @{ Layout = nu ...

  2. tomcat 的acceptCount、acceptorThreadCount、maxConnections、maxThreads 如何确定

    acceptCount 连接在被ServerSocketChannel accept之前就暂存在这个队列中,acceptCount就是这个队列的最大长度. ServerSocketChannel ac ...

  3. 3.0-uC/OS-III简介(操作系统结构)

    1.OS-III是一个第 3代的系统内核,支持现代的实时内核所期待的大部分功能. 例如资源管理, 同步, 任务间的通信等等.然而, uC/OS-III提供的特色功能在其它的实时内核中是找不到的, 比如 ...

  4. laravel用crud之index列出产品items

    前面我们说了laravel用crud修改产品items-新建resource controller和routing,现在我们要把产品items罗列出来,需要修改路由和模板,一起随ytakh来看看把 1 ...

  5. cobbler自动安装操作系统

    cobbler介绍 快速网络安装linux操作系统的服务,支持众多的Linux发行版: Red Hat, Fedora,CentOS,Debian,Ubuntu和SuSE 也可以支持网络安装windo ...

  6. WebStrom2018注册码

    2RRJMBXW33-eyJsaWNlbnNlSWQiOiIyUlJKTUJYVzMzIiwibGljZW5zZWVOYW1lIjoi5b285bK4IHNvZnR3YXJlMiIsImFzc2lnb ...

  7. 使用shape设置android控件只有部分边框有颜色

    <?xml version="1.0" encoding="UTF-8"?> <layer-list xmlns:android=" ...

  8. finally最常用的情况

    通常用于关闭(释放)资源例如:数据库连接   执行查询时查到需要的需要后   需要关闭连接,   此处连接就需要在获取到结果后 就断开连接   不然访问人数过多,数据库有最大连接数,超过了就只能等待前 ...

  9. wue父子通信和动态路由 还有点击事件直接赋值传参数

    这种直接在后面赋值 传值

  10. PHP学习路径及练手项目合集

    PHP 技术路径中包含入门知识.PHP 编程基础.PHP Web 框架.项目实战和项目进阶五个模块.模块中的课程将带着你逐步深入,学会如何使用 PHP 实现项目一个博客,聊天室,MVC框架及搜索系统等 ...