为什么要使用synchronized?
主要针对并发编程的安全问题:共享数据或多线程共同操作共享数据时保证同一时刻只有一个线程可以使用共享资源。
问题发现:定义一个售票类,继承Thread,重写run方法,初试ticket值为100。
运行结果:省略前面部分,只截取出错展示
问题分析:在程序中我们定义了如果票数小于等于0时则立即停止循环,但是现在出现了第0张票和第-1张票。
出错原因:我们程序是多线程执行的,假设当四号线程线程获得CPU,此时还有一张票,则一路执行到sleep时休眠0.1秒,此时CPU切换交由一号线程执行,也一路执行到sleep时休眠0.1秒,此时CPU切换交由二号线程执行,也一路执行到sleep时休眠0.1秒。然后又由四号获取CPU从sleep往后执行售票打印语句动作,一号、二号和四号一样先后获取CPU从sleep往后执行售票打印语句动作。此时只有一张票的情况因为CPU的切换所以让三个售票窗口都卖了一次,导致多卖了两张出现了错误。如果我们能够有一种机制可以使售票动作每次当有一个线程进入执行时,其余线程只能等待其执行完毕后方可获得执行权,这样就可以保证不出错了。
-----------------------------------分割线-----------------------------------
线程同步概念:当多线程并发,有多段代码同时执行时,我们希望某一段代码执行的过程中CPU不要切换到其它线程工作,这时就需要同步。如果两段代码是同步的,那么同一时间只会执行其中一个,这个结束之前另外一段代码就不会被执行。
一、同步代码块
格式:synchronized(锁对象)
{执行代码块}
注意:锁对象可以是任意的,可以是随意创建一个对象放进去,但是多个线程执行只能是同一个锁对象,当其中一个线程获取锁对象开始执行被锁代码时其余线程只能在外边等待。不能在括号内使用匿名对象(new 对象名),这样多个线程使用的就是多个锁了。
注意:同步代码块不要去嵌套,容易出现死锁。
二、同步方法
1.非静态的同步方法的锁对象是当前实例对象,如果线程正在执行某一个同步方法即得到当前实例对象锁,而锁只有一把。所以其余同步方法需要等待其执行完毕释放对象锁后才能执行,非同步方法不受影响,可以和同步方法并发执行。
①一个线程执行同步方法获得对象锁后,另外一个线程也来执行同步方法
输出结果:
分析:当一个线程访问同步方法获得对象锁后,执行完新睡眠10毫秒,此时线程会释放CPU但是不会释放对象锁,因为程序方法体还未全部执行完毕。所以此时别的线程虽然可以获得CPU执行权,但是无法获取对象锁执行此同步方法(此类中的其余同步方法也不行,因为一个对象只有一把锁,但是每个同步方法的执行都需要对象锁),只能去访问非同步方法。
②一个线程执行同步方法获得对象锁后,另外一个线程去执行非同步方法
输出结果:
分析:当一个线程访问同步方法获得对象锁后,其它线程可以并发执行非同步方法,不受影响。
2.静态的同步方法的锁对象是:类型.class(字节码对象)。如果一个实例对象线程正在访问当前类的静态同步方法,即获得当前类的锁(锁住了类)。此时其余此类的实例对象线程无法访问此类中的方法,需要等待获取类的锁。
输出结果:
分析:一个实例对象线程访问静态同步方法,获取当前被实例化类的锁,然后执行输出完“新”后进入休眠状态释放CPU执行权,但是因为方法未执行完毕所以类锁并未释放。此时其余实例化对象线程获得CPU执行权,但是无法获取类锁,所以无法访问此类中的方法,只能等待获取类锁的线程执行完毕。
————————————————
版权声明:本文为CSDN博主「一名小白的进阶之路」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_40275740/article/details/104121967