多线程并发编程(三):多线程同步互斥Synchronized 怼烎@ 2022-07-30 15:23 238阅读 0赞 # 前言 # > 其实就是针对线程安全问题进行的,最经典的问题就是银行转账的问题,A向B转账的同时,A也在存钱,比如A有1000元,向B转200元,A自己存300元,按道理最后是1100元,如果没有同步机制,那么就可能发生问题,如果在A转账的时候,余额还没有开始减,然后A存钱的时候,拿到的余额是没有减去的,然后A的余额开始减去,但是存钱的线程拿到的余额是没有减去的,那么最后计算赋值可能变成了1000+300=1300了,同样,有可能在存钱的时候还没有计算赋值,另外一个转账线程进来,拿到的值是还没有加起来的,也就是1000元,那么转账线程执行完,1000-200=800,按道理是1100的,所以可能出现钱多了,或者钱少了的问题。 # 线程安全问题 # package test01; public class TraditionalThreadSychronized { class Outputer{ // 这里打印,采取一个字母一个字母的打印,打印完一个名字再换行 public void output(String name){ int len = name.length(); for (int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(); } } public void init(){ // 注意这里要申明为final final Outputer out = new Outputer(); // 线程1:打印zhangsan new Thread(new Runnable() { @Override public void run() { while(true){ try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } out.output("zhangsan"); } } }).start(); // 线程2:打印lisi new Thread(new Runnable() { @Override public void run() { while(true){ try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } out.output("lisi"); } } }).start(); } public static void main(String[] args) { new TraditionalThreadSychronized().init(); } } 如果不考虑线程安全问题的话,理想结果应该是zhangsan,lisi 循环打印,但是这里没有使用同步机制,输出结果中可以发现,出现了原本不想要的结果: ![这里写图片描述][20160419143350596] 可以发现,打印出来的数据有些有问题。 > 解决办法: 采用同步机制,使用**synchronized** # 使用同步解决线程安全问题 # ### 1、使用当前对象锁 ### class Outputer{ // 这里打印,采取一个字母一个字母的打印,打印完一个名字再换行 public void output(String name){ synchronized(this){ int len = name.length(); for (int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(); } } } ### 2、使用当前对象属性的对象锁 ### class Outputer{ private String xxx = ""; // 这里打印,采取一个字母一个字母的打印,打印完一个名字再换行 public void output(String name){ synchronized(xxx){ int len = name.length(); for (int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(); } } } ### 3、使用同步方法 ### class Outputer{ private String xxx = ""; // 这里打印,采取一个字母一个字母的打印,打印完一个名字再换行 public synchronized void output(String name){ int len = name.length(); for (int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(); } } > **其实,使用同步方法,就是使用的当前对象的对象锁** 输出结果: ![这里写图片描述][20160419144044899] > 打印结果中不再出现之前的打印问题,zhangsan,lisi都完整打印出来了,当然如果要实现轮流依次打印那种,之后会给出实现方案的,这里只讨论线程安全机制。 # 同步之间的互斥问题 # ## 第一种情况:同步代码块与同步方法 ## package test01; public class TraditionalThreadSychronized { class Outputer{ // 同步代码块形式 public void output(String name){ synchronized(this){ int len = name.length(); for (int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(); } } // 同步方法形式 public synchronized void output2(String name){ int len = name.length(); for (int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(); } } public void init(){ // 注意这里要申明为final final Outputer out = new Outputer(); // 线程1:打印zhangsan new Thread(new Runnable() { @Override public void run() { while(true){ try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } // 调用的是同步代码块形式的方法output out.output("zhangsan"); } } }).start(); // 线程2:打印lisi new Thread(new Runnable() { @Override public void run() { while(true){ try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } // 调用的是同步方法形式的方法output2 out.output2("lisi"); } } }).start(); } public static void main(String[] args) { new TraditionalThreadSychronized().init(); } } > 结果:不会出现线程安全问题。 > > 分析:第一个线程调用的是同步代码块的方法output,第二个线程调用的是同步方法形式的方法output2,那么结果会怎么样了,**会出现线程安全问题吗?答案当然是不会**,前面说到了,**同步方法其实使用的就是当前对象的对象锁,而同步代码块中用的也是当前对象的对象锁,所以肯定是互斥的**,即不能当一个线程拿到了这个对象的对象锁的时候,其他对象不能调用当前对象需要对象锁的方法,说的通俗点就是,线程1进入output方法的时候,拿到了当前对象的对象锁,线程2没有当前对象的对象锁,所以访问不了方法output2,同样线程2拿到之后,线程1也访问不了output方法。 > > **特殊情况:当然如果同步代码块中使用的是其他对象锁,那么就会出现线程安全问题了,因为两个方法使用的不是同一个对象的对象锁。** ## 第二种情况:同步代码块与静态同步方法 ## package test01; public class TraditionalThreadSychronized { static class Outputer{ // 同步代码块形式 public void output(String name){ synchronized(this){ int len = name.length(); for (int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(); } } // 同步方法形式 public synchronized void output2(String name){ int len = name.length(); for (int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(); } // 静态同步代码块 public static synchronized void output3(String name){ int len = name.length(); for (int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(); } } public void init(){ // 注意这里要申明为final final Outputer out = new Outputer(); // 线程1:打印zhangsan new Thread(new Runnable() { @Override public void run() { while(true){ try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } // 调用的是同步代码块形式的方法output out.output("zhangsan"); } } }).start(); // 线程2:打印lisi new Thread(new Runnable() { @Override public void run() { while(true){ try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } // 调用的是静态同步方法形式的方法output3 out.output3("lisi"); } } }).start(); } public static void main(String[] args) { new TraditionalThreadSychronized().init(); } } ![这里写图片描述][20160419150433733] > 结果:会出现线程安全问题。 > 分析:同步代码块使用的是当前对象的对象锁,而静态同步方法使用的是当前静态类class的对象锁。 > **解决办法:同步代码块中使用当前类class的锁**,如下: public void output(String name){ synchronized(Outputer.class){ int len = name.length(); for (int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(); } } ## 第三种情况:同步方法与静态同步方法 ## > 代码就省略了 > 结果:会出现线程安全问题 > 分析:同步方法使用的是当前类的对象的对象锁,而静态同步方法使用的是当前类的class对象的对象锁 [20160419143350596]: /images/20220724/f55b560eb6bc40fbba0ac2989b2681ef.png [20160419144044899]: /images/20220724/127899c3d11141bf8865f669d08beb99.png [20160419150433733]: /images/20220724/a9c3f596920c4c5b9ca84edc7fea6a89.png
相关 Java多线程编程:同步和互斥问题 在Java多线程编程中,同步和互斥问题是关键概念。这两个概念主要用来解决多个线程对共享资源的访问顺序以及防止数据不一致的问题。 1. **同步**: - 使用synch Bertha 。/ 2024年09月17日 12:27/ 0 赞/ 11 阅读
相关 并发编程难题:Java多线程同步与互斥案例 在Java多线程编程中,同步和互斥是两个重要的概念,用于保证共享资源的正确使用。 1. **同步(Synchronization)**: - **锁(Locks)**: 迈不过友情╰/ 2024年09月10日 13:24/ 0 赞/ 23 阅读
相关 【高并发基石】多线程、守护线程、线程安全、线程同步、互斥锁 很久之前就听过互联架构中有三高,高可用、高并发、高性能,多线程是处理高并发问题的基石,起步阶段一定要对线程有一个系统深刻的印象,为以后做准备 拼搏现实的明天。/ 2024年04月22日 19:15/ 0 赞/ 33 阅读
相关 多线程并发编程(四):多线程同步互斥Wait/Notify 前言 > 前面说了使用Synchronized来进行线程之间的同步,接下来说明wait/notify的使用。 > > 首先wait/notify必须结合synchron 旧城等待,/ 2022年07月30日 15:27/ 0 赞/ 384 阅读
相关 多线程并发编程(三):多线程同步互斥Synchronized 前言 > 其实就是针对线程安全问题进行的,最经典的问题就是银行转账的问题,A向B转账的同时,A也在存钱,比如A有1000元,向B转200元,A自己存300元,按道理最后是 怼烎@/ 2022年07月30日 15:23/ 0 赞/ 239 阅读
相关 Linux多线程编程(二)-----同步与互斥 Linux多线程编程(一):[http://blog.csdn.net/llzk\_/article/details/55670172][http_blog.csdn.net_ àì夳堔傛蜴生んèń/ 2022年07月12日 08:54/ 0 赞/ 241 阅读
相关 多线程编程-Synchronized同步方法(二) 2.1 synchronized 同步方法 “非线程安全”会在多个线程对同一个对象中的实例变量进行并发访问时发生,产生的后果就是“脏读”,也就是读到的数据其实是被更改过的。 偏执的太偏执、/ 2022年06月02日 09:39/ 0 赞/ 201 阅读
相关 多线程与并发----线程互斥技术 1、使用synchronized代码块 2、使用synchronized方法 3、分析静态方法锁使用的同步监视器对象是什么? 4、wait与notify实现线程间的通信 柔光的暖阳◎/ 2021年09月15日 09:42/ 0 赞/ 454 阅读
相关 Linux多线程——使用互斥量同步线程 前文再续,书接上一回,在上一篇文章: [Linux多线程——使用信号量同步线程][Linux]中,我们留下了一个如何使用互斥量来进行线程同步的问题,本文将会给出互斥量的详细解说 深碍√TFBOYSˉ_/ 2021年09月02日 08:33/ 0 赞/ 419 阅读
还没有评论,来说两句吧...