JAVA之旅(十三)

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

JAVA之旅(十三)一.线程的安全性当我们开启四个窗口(线程)把票陆陆续续的卖完了之后,我们要反思一下,这里面有没有安全隐患呢?在实际情况中,这种事情我们是必须要去考虑安全问题的,那我们模拟一下错误packagecom.lgl.hellojava;importjavax.security.auth.callback.TextInputCallback;//公共的类类名publicclassHelloJJAVA{publicstaticvoidmain(String[]args){/***需求:简单的卖票程序,多个线程同时卖票*/MyThreadmyThread=newMyThread();Threadt1=newThread(myThread);Threadt2=newThread(myThread);Threadt3=newThread(myThread);Threadt4=newThread(myThread);t1.start();t2.start();t3.start();t4.start();}}/***卖票程序**@authorLGL**/classMyThreadimplementsRunnable{//票数privateinttick=100;@Overridepublicvoidrun(){while(true){if(tick0){try{Thread.sleep(1000);}catch(InterruptedExceptione){//TODOAuto-generatedcatchblocke.printStackTrace();}System.out.println(卖票:+tick--);}}}}我们输出的结果这里出现了0票,如果你继续跟踪的话,你会发现,还会出现-1,-2之类的票,这就是安全隐患,那原因是什么呢?当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一个部分,还没有执行完,另外一个线程参与了执行,导致共享数据的错误解决办法:对多条操作共享数据的语句,只能让一个线程都执行完再执行过程中其他线程不可以参与运行Java对多线程的安全问题提供了专业的解决办法,就是同步代码块synchronized(对象){//需要同步的代码}那我们怎么用呢?packagecom.lgl.hellojava;//公共的类类名publicclassHelloJJAVA{publicstaticvoidmain(String[]args){/***需求:简单的卖票程序,多个线程同时卖票*/MyThreadmyThread=newMyThread();Threadt1=newThread(myThread);Threadt2=newThread(myThread);Threadt3=newThread(myThread);Threadt4=newThread(myThread);t1.start();t2.start();t3.start();t4.start();}}/***卖票程序**@authorLGL**/classMyThreadimplementsRunnable{//票数privateinttick=100;Objectoj=newObject();@Overridepublicvoidrun(){while(true){synchronized(oj){if(tick0){try{Thread.sleep(10);}catch(InterruptedExceptione){//TODOAuto-generatedcatchblocke.printStackTrace();}System.out.println(卖票:+tick--);}}}}}这样,就输出二.多线程同步代码块我们为什么可以这样去同步线程?对象如同锁,持有锁的线程可以在同步中执行,没有执行锁的线程即使获取了CPU的执行权,也进不去,因为没有获取锁,我们可以这样理解四个线程,哪一个进去就开始执行,其他的拿不到执行权,所以即使拿到了执行权,也进不去,这个同步能解决线程的安全问题但是,同步是有前提的1.必须要有两个或者两个以上的线程,不然你同步也没必要呀2.必须是多个线程使用同一锁必须保证同步中只能有一个线程在运行但是他也有一个弊端:那就是多个线程都需要判断锁,较为消耗资源三.多线成同步函数我们可以写一段小程序,来验证这个线程同步的问题,也就是说我们看看下面这段程序是否有安全问题,有的话,如何解决?packagecom.lgl.hellojava;//公共的类类名publicclassHelloJJAVA{publicstaticvoidmain(String[]args){/***需求:银行里有一个金库有两个人要存钱300*/MyThreadmyThread=newMyThread();Threadt1=newThread(myThread);Threadt2=newThread(myThread);t1.start();t2.start();}}/***存钱程序,一次100*@authorLGL**/classMyThreadimplementsRunnable{privateBankb=newBank();@Overridepublicvoidrun(){for(inti=0;i3;i++){b.add(100);}}}/***银行*@authorLGL**/classBank{privateintsum;publicvoidadd(intn){sum=sum+n;System.out.println(sum:+sum);}}当你执行的时候你会发现这里是没错的,存了600块钱,但是,这个程序是有安全隐患的如何找到问题?1.明确哪些代码是多线成运行代码2.明确共享数据3.明确多线成运行代码中哪些语句是操作共享数据的那我们怎么找到安全隐患呢?我们去银行的类里面做些认为操作/***银行*@authorLGL**/classBank{privateintsum;publicvoidadd(intn){sum=sum+n;try{Thread.sleep(10);}catch(InterruptedExceptione){//TODOAuto-generatedcatchblocke.printStackTrace();}System.out.println(sum:+sum);}}让他sleep一下你就会发现这样的话,我们就可以使用我们的同步代码了/***银行**@authorLGL**/classBank{privateintsum;Objectj=newObject();publicvoidadd(intn){synchronized(j){sum=sum+n;try{Thread.sleep(10);}catch(InterruptedExceptione){//TODOAuto-generatedcatchblocke.printStackTrace();}System.out.println(sum:+sum);}}}这样代码就可以同步了哪些代码该同步,哪些不该同步,你一定要搞清楚,根据上面的3个条件大家有没有注意到,函数式具有封装代码的特定,而我们所操作的同步代码块也是有封装代码的特性,拿这样的话我们就可以换一种形式去操作,那就是写成函数的修饰符/***银行**@authorLGL**/classBank{privateintsum;publicsynchronizedvoidadd(intn){sum=sum+n;try{Thread.sleep(10);}catch(InterruptedExceptione){//TODOAuto-generatedcatchblocke.print();}System.out.println(sum:+sum);}}这样也是OK的四.同步函数的锁是this既然我们学习了另一种同步函数的写法,那我们就可以把刚才的买票小例子进一步封装一下了packagecom.lgl.hellojava;//公共的类类名publicclassHelloJJAVA{publicstaticvoidmain(String[]args){/***需求:简单的卖票程序,多个线程同时卖票*/MyThreadmyThread=newMyThread();Threadt1=newThread(myThread);Threadt2=newThread(myThread);Threadt3=newThread(myThread);Threadt4=newThread(myThread);t1.start();t2.start();t3.start();t4.start();}}/***卖票程序**@authorLGL**/classMyThreadimplementsRunnable{//票数privateinttick=100;@Overridepublicsynchronizedvoidrun(){while(true){if(tick0){try{Thread.sleep(10);}catch(InterruptedExceptione){//TODOAuto-generatedcatchblocke.printStackTrace();}System.out.println(Thread.currentThread()+卖票:+tick--);}}}}但是这样做,你却会发现一个很严重的问题,那就是永远只有0线程在执行卖票那是因为我们并没有搞清楚需要同步哪一个代码段,我们应该执行的只是里面的那两段代码,而不是整个死循环,所以我们得封装个函数进行线程同步packagecom.lgl.hellojava;//公共的类类名publicclassHelloJJAVA{publicstaticvoidmain(String[]args){/***需求:简单的卖票程序,多个线程同时卖票*/MyThreadmyThread=newMyThread();Threadt1=newThread(myThread);Threadt2=newThread(myThread);Threadt3=newThread(myThread);Threadt4=newThread(myThread);t1.start();t2.start();t3.start();t4.start();}}/***卖票程序**@authorLGL**/classMyThreadimplementsRunnable{//票数privateinttick=100;@Overridepublicvoidrun(){while(true){show();}}privatesynchronizedvoidshow(){if(tick0){try{Thread.sleep(10);}catch(InterruptedExceptione){//TODOAuto-generatedcatchblocke.printStackTrace();}System.out.println(Thread.currentThread()+卖票:+tick--);}}}这样输出解决了问题是被解决了,但是随之问题也就来了同步函数用的是哪一个锁呢?函数需要被对象调用,那么函数都有一个所属对象的引用,就是this,所以同步函数所引用的锁是this,我们来验证一下,我们把程序改动一下使用两个线程来卖票,一个线程在同步代码块中,一个线程在同步函数中,都在执行卖票动作packagecom.lgl.hellojava;//公共的类类名publicclassHelloJJAVA{publicstaticvoidmain(String[]args){/***需求:简单的卖票程序,多个线程同时卖票*/MyThreadmyThr

1 / 13
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功