reeten lock java_Java多线程分析(八)----Syschronized VS ReentrankLock

1.Synchronized Vs ReentrantLock 的区别:在低并发量时期两者性能没有较大的区别,在高并发量的情况下,Sync的阻塞性的锁会严重的损害其性能

在Java1.5之前写并发程序的时候,Synchronized对多个线程共享共同的资源变量中为了保证其共享资源的线程安全的性能,采用Synchronized对共享资源表持同步机制,但是这种同步机制提供的锁是一种悲观锁,在任何的时候只有一个线程才能够进入临界区域,对其他的线程产生一种阻塞的状态,如果一步注意的话,一般有可能会产生死锁的状态(线程永远处在等待的状态,一种十分糟糕的情况)。将这种锁叫做是内置锁,缺点:内置锁在执行的过程中无法被中断。Synchronized 关键词锁住的只能是某个对象,或者是某个方法,不能锁住某个变量,LOck可以锁住中某个变量,Synchronized对某些线程不安全的方法,直接在这种方法外进行锁的封装。通过增加这个关键词,将不是很安全线程进行线程安全操作

下面模拟火车站买票的情况,总的票数AllTotal,分为三个窗口售票,采用Synchronized这个内置锁的关键字进行模拟操作。

Sync锁一般要么锁住方法或者锁住同步码块,在这里锁住代码块,最好采用锁住同步码块减少锁的力度,有利于系统操作

1 packageExecutorDemoPagage;

2

3 public class TranDemo implementsRunnable{

4 public static int AllTotal=20;

5 public static int i=1;

6 public static Object obj=newObject();

7 public voidrun()

8 {//在售票的过程中,每个窗口可以同时访问主票数

9 while(true)

10 {

11 //System.out.println(Thread.currentThread().getName()+"窗口开始进行售票");

12 //sync关键锁住必须要是一个对象,或者一个方法

13 synchronized(this){

14 if(AllTotal>0)//将此放入锁之内,因为,必须要锁住这个代码

15 {

16 try{

17 Thread.sleep(200);

18 } catch(InterruptedException e) {

19 // TODO Auto-generated catch block

20 e.printStackTrace();

21 }

22 System.out.println(Thread.currentThread().getName()+"窗口售票出第"+i+++"票");

23 AllTotal=AllTotal-1;

24

25 }

26 else

27 {

28 break;

29 }

30 }

31 }

32 }

33

34 public static voidmain(String[] args) {

35 // TODO Auto-generated method stub

36 //采用5个线程进行模拟表示的是5个窗口,直到所有的票多卖完

37 TranDemo T=newTranDemo();

38 Thread [] t= new Thread[5];

39 for (int i=0;i<5;i++)

40 {

41 t[i]=new Thread(T);//

42 t[i].start();

43

44 }

45 }

46

47 }

结果显示:

8f900a89c6347c561fdf2122f13be562.pngView Code

Java中ReentrantLock为我们重入锁的概念是一种乐观锁,采用CAS的原子性操作,能够实现非阻塞类结构的算法,同时能够与异常进行交互式操作。这种锁显示锁,必须要人工的进行解锁操作,但是synchronized自动的进行解锁。

总结:Lock有比synchronized更精确的线程语义和更好的性能。synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放。最重要的一点,每一次在使用锁的过程中必须保证这把锁是解锁状态,特别在while,for循环中,UNLock写入到循环中这样才能保证此线程在循环过程对共享资源进行操作,这也是为什么叫可重入锁的原因。

同样一个采用ReentrantLock:模拟火车票的购票情况:

1 packageExecutorDemoPagage;

2

3 importjava.util.concurrent.locks.ReentrantLock;

4

5 public class TranDemo1 implementsRunnable {

6 public static int AllTotal=20;

7 public static int i=0;

8 //多个线程采用同一把锁

9 public static ReentrantLock lock1=newReentrantLock();

10

11 public voidrun()

12 {

13 while(true)

14 {

15 try{

16 lock1.lock();

17 if (AllTotal>0)

18 {

19 System.out.println(Thread.currentThread().getName()+"窗口售票出第"+i+++"票");

20 AllTotal=AllTotal--;

21 }

22 }finally{

23 lock1.unlock();

24 }

25 if(AllTotal<=0)

26 {

27 break;

28 }

29 }

30 }

31

32 public static voidmain(String[] args) {

33 // TODO Auto-generated method stub

34 //采用5个线程进行模拟表示的是5个窗口,直到所有的票多卖完

35 TranDemo T=newTranDemo();

36 Thread [] t= new Thread[5];

37 for (int i=0;i<5;i++)

38 {

39 t[i]=new Thread(T);//

40 t[i].start();

41

42 }

43 }

44 }

1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;

2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;

3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;

4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。

5)Lock可以提高多个线程进行读操作的效率。

在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。

2.Synchronized与ReentrantLock相比,如果在线程中发生了死锁的情况,对于Sync锁而言导致两个线程一直处于中断,对于ReentrantLock可以采用Lock.LockInterruptibly()对这种死锁的情况进行可中断的处理,抛出异常,让人理解,同时可以采用ReentrantLock中可实现的方法来避免中断。

下面通过模拟死锁的对其可中断的情况进行处理:

1 packageJavaLockPackage;

2

3 importjava.util.concurrent.ExecutorService;

4 importjava.util.concurrent.Executors;

5 importjava.util.concurrent.locks.ReentrantLock;

6

7 //在这个过程中模拟死锁的过程

8 public class ReentrantLockInt implementsRunnable{

9 //开辟连个锁,在开始的时候锁住1线程,其中锁住2线程,在2线程过程中锁住1线程

10 public intlock;

11 public static ReentrantLock lock1=newReentrantLock();

12 public static ReentrantLock lock2=newReentrantLock();

13 public ReentrantLockInt(intL)

14 {

15 this.lock=L;

16 }

17 public voidrun()

18 {try{

19 if(lock==1)

20 { lock1.lockInterruptibly();

21 //开始锁定线程1

22 try{

23 Thread.sleep(100);

24 } catch(InterruptedException e) {

25 // TODO Auto-generated catch block

26 e.printStackTrace();

27 }

28 lock2.lockInterruptibly();//只能够用这个方法,普通的lock无用的,

29 //因为lock2在线程2中没有解锁,所以在此发生死锁的情况

30 }

31 else

32 {

33 lock2.lockInterruptibly();

34 try{

35 Thread.sleep(100);

36 } catch(InterruptedException e) {

37 // TODO Auto-generated catch block

38 e.printStackTrace();

39 }

40 lock1.lockInterruptibly();//只能够用这个方法,普通的lock无用的

41 }

42 }catch(InterruptedException e)

43 {

44 e.printStackTrace();

45 }

46 finally{

47 if(lock1.isHeldByCurrentThread())//或者采用lock1.getHoldCount(1)判断是否得到锁

48 {

49 lock1.unlock();

50 }

51 if(lock2.isHeldByCurrentThread())

52 {

53 lock2.unlock();

54 }

55 System.out.println(Thread.currentThread().getId()+"线程退出");

56 }

57 }

58 public static void main(String[] args) throwsInterruptedException {

59 // TODO Auto-generated method stub

60 Thread t1=new Thread(new ReentrantLockInt(1));

61 Thread t2=new Thread(new ReentrantLockInt(2));

62 t1.start();t2.start();

63 Thread.sleep(1000);

64 DeadlockChecker.check();//这里必须要有一个死锁检查程序,不然使用中断的无法进行结束。

65 }

66

67 }

1 packageJavaLockPackage;

2

3 importjava.lang.management.ManagementFactory;

4 importjava.lang.management.ThreadInfo;

5 importjava.lang.management.ThreadMXBean;

6

7 //此程序最主要对死锁过程中的锁进行检查

8 public classDeadLockCheck {

9 private final static ThreadMXBean mbean=ManagementFactory.getThreadMXBean();

10 //此功能是对CPU的运行时间和竞争征用进行监视,评估某个线程运行的时间

11 final static Runnable deadLockChecker=newRunnable(){

12 public voidrun()

13 { while(true)

14 {//通过ThreadMXBean找到所有死锁中断程序,与输入程序的线程ThreadInfo进行比较,Id相同则进行中断

15 long [] deadlockedThreadIds=mbean.findDeadlockedThreads();

16 if(deadlockedThreadIds!=null){

17 ThreadInfo [] threadInfos=mbean.getThreadInfo(deadlockedThreadIds);

18 for(Thread t:Thread.getAllStackTraces().keySet())

19 {

20 for(int i=0;i

21 {

22 if(t.getId()==threadInfos[i].getThreadId())

23 {

24 t.interrupt();

25 }

26 }

27 }

28 }

29 try{

30 Thread.sleep(500);

31 }catch(InterruptedException e)

32 {

33 e.printStackTrace();

34 }

35 }

36

37 }

38 };

39 public static voidcheck()

40 {

41 Thread t=new Thread(deadLockChecker);//传替一种方法

42 t.setDaemon(true);

43 t.start();

44 }

45 }

通过jps,jstack来查看系统中虚拟机的运行情况:

7049c1cd9ad16374488fb72e7e66c42a.png

a36af6b3f39e78c39d2abebbe3a8d5e0.png

第二种避免死锁的情况而已进行可现实,使用lock.tryLock()方法,这样,不需要直接写其中断的方法

1 packageJavaLockPackage;

2

3 importjava.util.concurrent.TimeUnit;

4 importjava.util.concurrent.locks.ReentrantLock;

5

6 public class TimeLock implementsRunnable {

7 public static ReentrantLock lock=newReentrantLock();

8 public voidrun()

9 {

10 try{

11 if(lock.tryLock(5,TimeUnit.SECONDS ))//在规定的时间中竞争同一把锁

12 {

13 System.out.println(Thread.currentThread().getName()+"Get Successed Lock");

14 Thread.sleep(5000);

15 }

16 else

17 {

18 System.out.println(Thread.currentThread().getName()+"Get Failed Lock");

19 }

20 } catch(InterruptedException e)

21 {

22 e.printStackTrace();

23 }

24

25 finally

26 {

27 if(lock.isHeldByCurrentThread())

28 lock.unlock();

29 }

30 }

31 public static voidmain(String[] args) {

32 // TODO Auto-generated method stub

33 TimeLock L1=newTimeLock();

34 Thread p1=newThread(L1);

35 Thread p2=newThread(L1);

36 //俩个线程竞争同一把锁

37 p1.start();p2.start();

38

39 }

40

41 }

结果显示:第一种结果:

8f900a89c6347c561fdf2122f13be562.pngView Code

第二种结果:

8f900a89c6347c561fdf2122f13be562.pngView Code

3.练习题:

问题的描述

启动3个线程打印递增的数字, 线程1先打印1,2,3,4,5, 然后是线程2打印6,7,8,9,10, 然后是线程3打印11,12,13,14,15. 接着再由线程1打印16,17,18,19,20....以此类推, 直到打印到75. 程序的输出结果应该为:

线程1: 1

线程1: 2

线程1: 3

线程1: 4

线程1: 5

线程2: 6

线程2: 7

线程2: 8

线程2: 9

线程2: 10

...

线程3: 71

线程3: 72

线程3: 73

线程3: 74

线程3: 75

第一种方法,采用Synchronized,notify(),wait()

1 packageThreadTest;

2 //用这种方法不在main函数中将pn写成全局变量这样导致id,id1,id2值发生修改

3 public class NUmbelPrint implementsRunnable{

4 static int state=1;

5 static int n=1;

6 private int id=0,id1=0,id2=0;

7 static NUmbelPrint pn=newNUmbelPrint();

8 public static voidmain(String[] args) {

9 // TODO Auto-generated method stub

10 pn.id=1;new Thread(pn).start();try{

11 Thread.sleep(20);

12 } catch(InterruptedException e) {

13 // TODO Auto-generated catch block

14 e.printStackTrace();

15 }

16 pn.id=0;pn.id1=2;newThread(pn).start();

17 try{

18 Thread.sleep(20);

19 } catch(InterruptedException e) {

20 // TODO Auto-generated catch block

21 e.printStackTrace();

22 }

23 pn.id1=0;pn.id=0;pn.id2=3;newThread(pn).start();

24 try{

25 Thread.sleep(20);

26 } catch(InterruptedException e) {

27 // TODO Auto-generated catch block

28 e.printStackTrace();

29 }

30

31 }

32 public voidrun()

33 { if(id==1)

34 work(id);

35 if(id1==2)

36 work1(id1);

37 if(id2==3)

38 work2(id2);

39 }

40 public void work(inttemp){

41 for(int i=0;i<5;i++)

42 {

43 synchronized(pn){

44 while(state !=temp)

45 {

46 try{

47 this.wait();

48 } catch(InterruptedException e) {

49 // TODO Auto-generated catch block

50 e.printStackTrace();

51 }

52 }

53 for(int j=0;j<5;j++)

54 {

55 System.out.println(Thread.currentThread().getName()+"线程 "+n++);

56 }

57 state=2;

58 pn.notifyAll();

59 if(i==4)

60 System.out.println("线程1"+state);

61 }

62 }

63 }

64 public void work1(inttemp){

65 for(int i=0;i<=5;i++){

66 synchronized(pn){

67 while(state !=temp)

68 {

69 try{

70 this.wait();

71 } catch(InterruptedException e) {

72 // TODO Auto-generated catch block

73 e.printStackTrace();

74 }

75 }

76 for(int j=0;j<5;j++)

77 {

78 System.out.println(Thread.currentThread().getName()+"线程 "+n++);

79 }

80 state=3;

81 pn.notifyAll();

82 }

83 if(i==4)

84 System.out.println("线程2"+state);

85 }

86 }

87 public void work2(inttemp){

88 for(int i=0;i<5;i++){//第三个线程运行了4次

89 synchronized(pn){

90 while(state !=temp)

91 {

92 try{

93 this.wait();

94 } catch(InterruptedException e) {

95 // TODO Auto-generated catch block

96 e.printStackTrace();

97 }

98 }

99 for(int j=0;j<5;j++)

100 {

101 System.out.println(Thread.currentThread().getName()+"线程 "+n++);

102 }

103 state=1;

104 pn.notifyAll();

105 }

106

107 System.out.println("线程3--"+state+"---"+i);

108 }

109 }

110 }

111 //为什么最后一次中会0 1 0,因为线程1结束后就没有了,在整个程序中只有俩个线程,所以1 2 3修改成1 2,现在只有两个线程

112 //在这里竞争,所以最后才会出现0 1 0,线程发生变化

1 public classNumberPrintDemo {

2 // n为即将打印的数字,这里采用的匿名类部类的方法:

3 private static int n = 1;

4 // state=1表示将由线程1打印数字, state=2表示将由线程2打印数字, state=3表示将由线程3打印数字

5 private static int state = 1;

6

7 public static voidmain(String[] args) {

8 final NumberPrintDemo pn = newNumberPrintDemo();

9 new Thread(newRunnable() {

10 public voidrun() {

11 // 3个线程打印75个数字, 单个线程每次打印5个连续数字, 因此每个线程只需执行5次打印任务. 3*5*5=75

12 for (int i = 0; i < 5; i++) {

13 // 3个线程都使用pn对象做锁, 以保证每个交替期间只有一个线程在打印

14 synchronized(pn) {

15 // 如果state!=1, 说明此时尚未轮到线程1打印, 线程1将调用pn的wait()方法, 直到下次被唤醒

16 while (state != 1)

17 try{

18 pn.wait();

19 } catch(InterruptedException e) {

20 e.printStackTrace();

21 }

22 // 当state=1时, 轮到线程1打印5次数字

23 for (int j = 0; j < 5; j++) {

24 // 打印一次后n自增

25 System.out.println(Thread.currentThread().getName()

26 + ": " + n++);

27 }

28 System.out.println();

29 // 线程1打印完成后, 将state赋值为2, 表示接下来将轮到线程2打印

30 state = 2;

31 // notifyAll()方法唤醒在pn上wait的线程2和线程3, 同时线程1将退出同步代码块, 释放pn锁.

32 // 因此3个线程将再次竞争pn锁

33 // 假如线程1或线程3竞争到资源, 由于state不为1或3, 线程1或线程3将很快再次wait, 释放出刚到手的pn锁.

34 // 只有线程2可以通过state判定, 所以线程2一定是执行下次打印任务的线程.

35 // 对于线程2来说, 获得锁的道路也许是曲折的, 但前途一定是光明的.

36 pn.notifyAll();

37 }

38 }

39 }

40 }, "线程1").start();

41

42 new Thread(newRunnable() {

43 public voidrun() {

44 for (int i = 0; i < 5; i++) {

45 synchronized(pn) {

46 while (state != 2)

47 try{

48 pn.wait();

49 } catch(InterruptedException e) {

50 e.printStackTrace();

51 }

52 for (int j = 0; j < 5; j++) {

53 System.out.println(Thread.currentThread().getName()

54 + ": " + n++);

55 }

56 System.out.println();

57 state = 3;

58 pn.notifyAll();

59 }

60 }

61 }

62 }, "线程2").start();

63

64 new Thread(newRunnable() {

65 public voidrun() {

66 for (int i = 0; i < 5; i++) {

67 synchronized(pn) {

68 while (state != 3)

69 try{

70 pn.wait();

71 } catch(InterruptedException e) {

72 e.printStackTrace();

73 }

74 for (int j = 0; j < 5; j++) {

75 System.out.println(Thread.currentThread().getName()

76 + ": " + n++);

77 }

78 System.out.println();

79 state = 1;

80 pn.notifyAll();

81 }

82 }

83 }

84 }, "线程3").start();

85 }

86 }

第二种:ReentrantLock,await(),signal()

1 packageJavaLockPackage;

2

3 importjava.util.concurrent.locks.Condition;

4 importjava.util.concurrent.locks.ReentrantLock;

5

6 //三个线程轮换打印12345 678910,。。。。。有序的打印直到75

7 public classLockAndConditionExam {

8 private static int n=1;

9 private static int state=1;

10

11 private static ReentrantLock lock1=newReentrantLock();

12 //获取其中的三个分支线程

13 private static Condition c1=lock1.newCondition();

14 private static Condition c2=lock1.newCondition();

15 private static Condition c3=lock1.newCondition();

16 //的是一个实例,必须要加上static关键字,不然会成为class类,

17 private static class CreateThread1 implementsRunnable{

18 public voidrun()

19 {

20 for(int i=0;i<5;i++)

21 { try{

22 lock1.lock();

23 while(state!=1)

24 {

25 try{

26 c1.await();

27

28 }

29 catch(InterruptedException e)

30 {

31 e.printStackTrace();

32 }

33 }

34 for(int j=0;j<5;j++)

35 {

36 System.out.println(Thread.currentThread().getName()+"-线程--"+n++);

37 }

38 System.out.println();

39 state=2;

40 c2.signal();

41

42 }

43 finally{

44 lock1.unlock();

45 }

46

47 }

48 }

49 }

50

51 private static class CreateThread2 implementsRunnable{

52 public voidrun()

53 {

54 for(int i=0;i<5;i++)

55 { try{

56 lock1.lock();

57 while(state!=2)

58 {

59 try{

60 c2.await();

61

62 }

63 catch(InterruptedException e)

64 {

65 e.printStackTrace();

66 }

67 }

68 for(int j=0;j<5;j++)

69 {

70 System.out.println(Thread.currentThread().getName()+"-线程--"+n++);

71 }

72 System.out.println();

73 state=3;

74 c3.signal();

75

76 }

77 finally{

78 lock1.unlock();

79 }

80

81 }

82 }

83 }

84

85 private static class CreateThread3 implementsRunnable{

86 public voidrun()

87 {

88 for(int i=0;i<5;i++)

89 { try{

90 lock1.lock();

91 while(state!=3)

92 {

93 try{

94 c3.await();

95

96 }

97 catch(InterruptedException e)

98 {

99 e.printStackTrace();

100 }

101 }

102 for(int j=0;j<5;j++)

103 {

104 System.out.println(Thread.currentThread().getName()+"-线程--"+n++);

105 }

106 System.out.println();

107 state=1;

108 c1.signal();

109

110 }

111 finally{

112 lock1.unlock();

113 }

114

115 }

116 }

117 }

118 public static voidmain(String[] args) {

119 // TODO Auto-generated method stub

120

121 //对run方法进行重载

122 Thread T1=newThread (){

123

124 public voidrun()

125 {

126 for(int i=0;i<5;i++)

127 { try{

128 lock1.lock();

129 while(state!=1)

130 {

131 try{

132 c1.await();

133

134 }

135 catch(InterruptedException e)

136 {

137 e.printStackTrace();

138 }

139 }

140 for(int j=0;j<5;j++)

141 {

142 System.out.println(Thread.currentThread().getName()+"-线程--"+n++);

143 }

144 System.out.println();

145 state=2;

146 c2.signal();

147

148 }

149 finally{

150 lock1.unlock();

151 }

152

153 }

154 }

155 };

156 T1.start();

157

158 Thread T2=new Thread(newCreateThread2());

159 T2.start();//方法是进行new Runnable 实例

160

161 Thread T3=new Thread(newCreateThread3());

162 T3.start();

163

164 }

165

166 }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值