参考链接:
https://blog.csdn.net/luoweifu/article/details/46613015
Java中并发编程使用中,最频繁和最简单的使用是synchronized关键字了吧,使用了synchronized关键字,代码或者对象只能同时被一个线程操作。synchronized可以分为以下几类:
1.同步代码块(synchronized修饰代码块)
2.同步方法(synchronized修饰方法)
3.同步静态方法(synchronized修饰静态方法)
4.同步类(synchronized修饰类)
5.同步对象(synchronized修饰对象)

1.同步代码块例子

public class SynchronizedCode implements Runnable {private static int count = 0;void functionA() {synchronized (this) {for (int i = 0; i < 5; i++) {count++;System.out.println(Thread.currentThread().getName() + ":"+ "count=" + count);try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}}public void run() {functionA();}public static void main(String[] args) {SynchronizedCode code = new SynchronizedCode();Thread thread1 = new Thread(code, "thread1");Thread thread2 = new Thread(code, "thread2");thread1.start();thread2.start();}
}

控制台输出:

thread1:count=1
thread1:count=2
thread1:count=3
thread1:count=4
thread1:count=5
thread2:count=6
thread2:count=7
thread2:count=8
thread2:count=9
thread2:count=10

分析:
可以看出,在同一个对象中,访问同步代码块时只能有一个线程thread1访问,另外一个线程thread2一直在等待,直到第一个线程完全执行完毕。
典型格式:

functionXXX() {synchronized (this) {//do sth}

2.同步方法例子

    synchronized void functionA() {for (int i = 0; i < 5; i++) {count++;System.out.println(Thread.currentThread().getName() + ":"+ "count=" + count);try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}

控制台输出:

thread1:count=1
thread1:count=2
thread1:count=3
thread1:count=4
thread1:count=5
thread2:count=6
thread2:count=7
thread2:count=8
thread2:count=9
thread2:count=10

分析:
可以看到,从代码上,和同步代码块极为类似,只是将synchronized挪到方法上了,这样synchronized的同步范围扩大为整个方法,但是实际作用和同步代码块基本一样。
典型格式:

    synchronized void functionXXX() {//do sth}

3.改进上述例子说明同步方法只锁方法,作用范围为同一个对象

对上述第二个例子简单修改如下:

public class SynchronizedCode implements Runnable {private static int count = 0;synchronized void functionA() {for (int i = 0; i < 5; i++) {count++;System.out.println(Thread.currentThread().getName() + ":"+ "count=" + count);try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}public void run() {functionA();}public static void main(String[] args) {SynchronizedCode code1 = new SynchronizedCode();SynchronizedCode code2 = new SynchronizedCode();Thread thread1 = new Thread(code1, "thread1");Thread thread2 = new Thread(code2, "thread2");thread1.start();thread2.start();}
}

控制台输出:

thread1:count=1
thread2:count=2
thread2:count=4
thread1:count=4
thread2:count=5
thread1:count=5
thread2:count=7
thread1:count=7
thread1:count=9
thread2:count=9

分析:从log上看,好像synchronized不起作用了?输出为两个线程交替进行,但是仔细观察main函数,会发现此时有两个SynchronizedCode对象,而对于同步方法或者同步代码块,它们只同步一个对象中的对应同步范围,对于其他对象内部的执行逻辑,则管不到了。所以同步方法或者同步代码块是一个对象锁。

4.同步静态方法的例子

修改上述Demo中的方法,更改如下:

public class SynchronizedCode implements Runnable {private static int count = 0;static synchronized void functionA() {for (int i = 0; i < 5; i++) {count++;System.out.println(Thread.currentThread().getName() + ":"+ "count=" + count);try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}public void run() {functionA();}public static void main(String[] args) {SynchronizedCode code1 = new SynchronizedCode();SynchronizedCode code2 = new SynchronizedCode();Thread thread1 = new Thread(code1, "thread1");Thread thread2 = new Thread(code2, "thread2");thread1.start();thread2.start();}
}

控制台输出:

thread1:count=1
thread1:count=2
thread1:count=3
thread1:count=4
thread1:count=5
thread2:count=6
thread2:count=7
thread2:count=8
thread2:count=9
thread2:count=10

分析:
观察main方法,可知,目前创建了SynchronizedCode的两个实例对象,各自有一个线程去访问SynchronizedCode类,可以看到线程1和线程2保持同步,为什么会这样呢?因为thread start调用了run方法,run里面的调用的functionA是一个静态同步方法,我们知道静态方法是属于类的,所以此时加的同步锁也是针对类的,同一时间,只能有一个SynchronizedCode类的对象来访问functionA方法,因此保持了线程的同步。

典型格式:

    static synchronized void functionA() {//do sth}

5.同步类(class)的例子

public class SynchronizedCode implements Runnable {private static int count = 0;void functionA() {synchronized (SynchronizedCode.class) {for (int i = 0; i < 5; i++) {count++;System.out.println(Thread.currentThread().getName() + ":"+ "count=" + count);try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}}public void run() {functionA();}public static void main(String[] args) {SynchronizedCode code1 = new SynchronizedCode();SynchronizedCode code2 = new SynchronizedCode();Thread thread1 = new Thread(code1, "thread1");Thread thread2 = new Thread(code2, "thread2");thread1.start();thread2.start();}

控制台输出:

thread1:count=1
thread1:count=2
thread1:count=3
thread1:count=4
thread1:count=5
thread2:count=6
thread2:count=7
thread2:count=8
thread2:count=9
thread2:count=10

分析:
可以对比例子三和例子四的代码和输出。这种加锁方式和同步静态方法类似,锁是加在类上,即同一个类的所有实例,一次只能有一个实例拥有锁,这是一把类锁。
典型格式:

    void functionXXX() {...synchronized (XXX.class) {//do sth}...}

6.同步对象的例子

此种情况稍显复杂,需要另外写个demo,如下

public class Acount {private String UserName;private float Money;public Acount(String userName, float money) {super();UserName = userName;Money = money;}public String getUserName() {return UserName;}public void setUserName(String userName) {UserName = userName;}public float getMoney() {return Money;}public void setMoney(float money) {Money = money;}}
public class BankOperator implements Runnable {Acount a;// 存钱public void deposit(float money) {a.setMoney(a.getMoney() + money);try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName() + ":"+ a.getMoney());}// 取钱public void withdraw(float money) {a.setMoney(a.getMoney() - money);try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName() + ":"+ a.getMoney());}public BankOperator(Acount a) {super();this.a = a;}public static void main(String[] args) {Acount acount = new Acount("chj", 100);BankOperator b = new BankOperator(acount);Thread t1 = new Thread(b, "thread1");Thread t2 = new Thread(b, "thread2");Thread t3 = new Thread(b, "thread3");Thread t4 = new Thread(b, "thread4");t1.start();t2.start();t3.start();t4.start();}public void run() {// 假设每个线程存50 取50withdraw(50);deposit(50);}}

控制台输出:

thread1:withdraw-100.0
thread4:withdraw-50.0
thread3:withdraw0.0
thread2:withdraw50.0
thread1:deposit100.0
thread4:deposit100.0
thread3:deposit100.0
thread2:deposit100.0

分析:观察main方法,我们目前模拟4个操作员对chj账户进行操作,由于没有加锁,四个操作员可以同时操作账户,于是出现了-100的情况,如果我们需要一个操作员的存取动作完全结束才能操作,应该如何做呢?
最简单的方式是改为

    public void run() {// 假设每个线程存50 取50synchronized (a) {withdraw(50);deposit(50);}}

这样对于a账户,一个线程的存取操作就变成一个原子操作,需要一同执行完毕,
此时,控制台输出:

thread1:withdraw50.0
thread1:deposit100.0
thread4:withdraw50.0
thread4:deposit100.0
thread3:withdraw50.0
thread3:deposit100.0
thread2:withdraw50.0
thread2:deposit100.0

典型格式:

public void functionXXX()
{//obj 锁定的对象synchronized(obj){// todo}
}

其他注意点

1.当一个线程访问对象的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该对象中的非synchronized(this)同步代码块,例子请看参考链接的Demo2 参考这里
2.synchronized方法可以被继承么?
答案是不可以,父类的synchronized修饰的方法是同步的,子类要想实现父类同名方法同步,第一可以使用super调用父类方法,第二只能自己继承方法后加上synchronized关键字
3.同步的弊端
a:性能下降,多线程的好处就是多个任务同时进行,但是synchronized的作用是一次只让一个线程操作一个对象或者方法,其他对象只能等待。过多的同步会导致性能的下降
b:导致死锁,某些情况同步还会导致死锁,比如:

    void test(){synchronized (a) {synchronized (b) {//do sth}}...}

如果有两个线程访问test方法,线程1先给a对象加锁,线程2先给b加锁,接下来就好戏上场了,线程1等2释放b的锁,线程2等1释放a的锁,然后就over了。
4.

当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的对象来充当锁:

class Test implements Runnable
{private byte[] lock = new byte[0];  // 特殊的instance变量public void method(){synchronized(lock) {// todo 同步代码块}}public void run() {}
}

说明:零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。

总结

synchronized锁加的同步锁分为两类:对象锁和类锁
修饰类和修饰静态方法的synchronized的锁是类锁,该类的所有实例拥有同一把锁。
修饰对象,修饰普通方法,修饰代码块的都是对象锁,只针对同一个对象,换了对象则不能对其他对象的内容起作用。

以上为原文https://blog.csdn.net/luoweifu/article/details/46613015的理解和整理,如有错误,请指出。

Java关键字synchronized的简单理解相关推荐

  1. 【java】java 关键字: synchronized详解

    1.概述 转载:关键字: synchronized详解 [Java]Synchronized 有几种用法 [java] 从hotspot底层对象结构理解锁膨胀升级过程 [java]动态高并发时为什么推 ...

  2. Java关键字synchronized详解

    synchronized 关键字,代表这个方法加锁,相当于不管哪一个线程A每次运行到这个方法时,都要检查有没有其它正在用这个方法的线程B(或者C D等),有的话要等正在使用这个方法的线程B(或者C D ...

  3. Java关键字synchronized 使用中的 Double-Checked Locking is Broken

    "Double-Checked Locking is Broken"声明 签名人: David Bacon (IBM Research) Joshua Bloch (Javasof ...

  4. python中关键字global的简单理解

    python用global关键字来标识函数里或类里的全局变量,下面以例子来看看global关键字的作用. 未使用global关键字 a=10 #全局变量 def sum(x):a=2 #局部变量x=a ...

  5. 对java中接口的简单理解

    接口属于一个特殊的抽象类,继承的时候用 implements 实现,接口的继承不叫继承,叫做实现 接口的实现和类的继承有些类似,但是类的继承是单继承,接口可以多实现(多继承) 类的继承是对共性的继承, ...

  6. 对C语言的关键字及部分关键字用法的简单理解

    小盆友,你是否有很多问号??? C语言的关键字是什么?有哪些?他们如何用??? 我也在学习中迷惑着,所以把今天学习到的稍微做一下总结,方便自己以后的复习(目前学到的比较粗浅,以后还需要深入学习). 哪 ...

  7. java中的二进制运算简单理解

    package test9;public class StreamTest {public static void main(String[] args) {int a = 15;// 0b1111i ...

  8. 深入理解并发的关键字-synchronized

    我们已经了解了Java内存模型的一些知识,并且已经知道出现线程安全的主要问题来源于JMM的设计,主要集中在主内存和线程的工作内存而导致的内存可见性问题,以及重排序导致的问题,进一步知道了happens ...

  9. 【Java】Synchronized解析以及多种用法

    1.概述 [Java]Synchronized 有几种用法 [java] 从hotspot底层对象结构理解锁膨胀升级过程 [java]动态高并发时为什么推荐重入锁而不是Synchronized? [j ...

最新文章

  1. 如何打造一支拖业务后腿的技术团队?
  2. 漫步者lollipods如何调节音量_漫步者MF5扩音器体验:老师值得入手
  3. 使用Netbeans开发App Engine Java
  4. Linux服务器安全防护十个方面
  5. 人工智能发展及其伦理问题思考
  6. linux整行剪切_云计算人员如何提高效率 要掌握哪些Linux命令
  7. 误差理论实际应用公式
  8. docker常用操作(三) docker安装maven私服
  9. matlab 图像分割-自定义函数T_SGM
  10. QCon校友会之柴锋:10次QCon之旅
  11. 2022软科中国最好学科排名——计算机科学与技术
  12. 容灾是什么意思?容灾基础知识介绍
  13. python高级数据筛选的方法_使用python对多个txt文件中的数据进行筛选的方法
  14. 深度linux打不开了,深度社区严重打不开
  15. SIM7600CE-CNSE 4G模块 树莓派/Windows连网指南
  16. Yarn Web页面 8088 端口在Windows浏览器无法访问
  17. 如何优雅的修改 Kubernetes Master 节点 IP?可没你想象中那么简单!
  18. 玩转JDBC打造数据库操作万能工具类JDBCUtil,加入了高效的数据库连接池,利用了参数绑定有效防止SQL注入
  19. 网络聊天室(Java)
  20. yum配置文件 重启后还原_电脑里重启后,重启前所有设置都还原到以前了,怎么办啊...

热门文章

  1. vue开发 - 将方法绑定到window对象,给app端调用
  2. 页面上插入flash文件
  3. 【NetWebApi】接口参数传递笔记
  4. [转]Aptana Studio 3配置Python开发环境图文教程
  5. jQuery 根据值或者文本选中select
  6. 最近总是淡淡的····
  7. 深度学习——用神经网络解决非线性问题
  8. 动态规划——买卖股票的最优时机含手续费(Leetcode 714)
  9. 动态规划——零钱兑换(Leetcode 322)
  10. Leetcode——最长递增子序列(leetcode 300)