传智播客-多线程(3)-同步

来源:互联网 发布:cf算法 编辑:程序博客网 时间:2024/05/23 00:33

虽然说是多线程,但是每一个时刻点jvm执行的时候只能执行一句代码,对于一段程序而言,或者说一个业务流而言,代码的执行是在多个线程里相互不断切换的,即一个业务流中多个任务的分派在执行过程中的顺序是不定的。以下面的代码为例:

 

从打印的结果中可以看到:
线程一、线程二、线程三可能(1)售卖同样序号的票;(2)而当票即将售空时,还会出现序号为负数的票(注意到“Thread.sleep(100)”了吗,这段代码最好留着,不然程序的并发风险效果不明显);(3)可能没打印出序号为0的票程序就终止了。

 

分析:
主要抓住2个要点:一是多线程间代码跳转执行;二是java中表达式“i--”是先赋值再减一;


(1)假定此时tickets=99,当线程一执行完[1]语句后,因为99大于0,所以进入if语句代码块并执行[2]语句,打印出tickets--的值为99,但是在if语句还没结束,即到达了[3]语句但还没执行的时候,jvm跳转到了线程二执行完[1]语句时,tickets仍然为99(关键点A),大于0,也会进入if语句代码块,执行线程二的[2]语句,打印出的tickets--值仍为为99。
分析关键点A:表达式tickets--在jvm里执行的过程可以拆分为tickets=tickets(赋值)和tickets=tickets-1(减一)
可以试试下面代码:

 

(2)假定此时tickets=1,当线程一执行完[1]语句后,因为1大于0,所以进入if语句代码块,但是线程一的[2]语句还没执行jvm就跳转到线程二了,而此时tickets的值仍然为1,所以线程二执行完[1]语句后,也会进入if语句代码块,线程二的[2]语句还没执行jvm又跳转到线程三了,jvm再跳转回来执行线程一的[2]语句,打印出的tickets值为1,执行完毕后又跳转回线程二,执行线程二的[2]语句,打印出的值为0;又跳转回线程三,执行线程三的[2]语句,打印出的值为-1。需要注意,这里的打印语句是连续执行的,即连续执行了三次“tickets--”;而情况(1)中执行了“tickets--”后直接先跳转到另一个线程的“(tickets > 0)”。

 

(3)参见(1)(2)。

 

通常如果没有特别的控制手段的话,多线程并发的过程中很容易遇到上述并发风险。而java提供的控制手段则是同步--synchronized。java程序可以在两种情况下应用synchronized关键字,一是同步代码块,二是同步方法。synchronized机制要求使用一个监控器,即加锁对象,同步代码块中是明确指明某个对象,同步方法则默认是this对象。

 

不过同步的过程中也有风险--死锁。如果两个线程的锁对象和代码操作对象互为对方的代码操作对象和锁对象时,就会出现死锁。这个如果业务逻辑脉络清晰的话还是可以避免的。

 

因为同步控制了并发,所以加同步的代码效率一般比没加同步的要低。鱼和熊掌不可兼得,自行掂量吧。

原创粉丝点击