java总结下.docx
- 文档编号:10272123
- 上传时间:2023-05-24
- 格式:DOCX
- 页数:90
- 大小:94.65KB
java总结下.docx
《java总结下.docx》由会员分享,可在线阅读,更多相关《java总结下.docx(90页珍藏版)》请在冰点文库上搜索。
java总结下
10.5线程安全
10.5.1同步代码块
线程安全的概念不容易定义,在《Java并发编程实践》中,作者做出了如此定义:
多个线程访问一个类对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,并且不需要额外的同步及在调用方法代码不必作其他的协调,这个类的行为仍然是正确的,那么这个类是线程安全的。
这样说,可能有些朋友没有看明白,那么我们先来看一个示例,来演示一下什么是现成不安全的情况。
packagecom.hzgg.threadtest;
packagecom.hzgg.test2;
publicclassSafeThreadTest{
publicstaticvoidmain(String[]args){
SafeThreadTeststt=newSafeThreadTest();
stt.init();
}
Vv=newV();
privatevoidinit(){
newThread(newRunnable(){
@Override
publicvoidrun(){
//TODOAuto-generatedmethodstub
while(true){
v.showString("AAAAAAAAAAAAAAAAAAAAAAAAAAA");
}
}
}).start();
newThread(newRunnable(){
@Override
publicvoidrun(){
//TODOAuto-generatedmethodstub
while(true){
v.showString("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBB");}
}
}).start();
}
classV{
publicsynchronizedvoidshowString(Strings){
for(inti=0;i System.out.print(s.charAt(i)); } System.out.println(); } } } 同步代码块,就是定义了一组原子性代码。 所谓原子性代码,就是说在这一组代码块中,只允许一个线程进入。 直到这个线程运行完毕,下一个线程才可以进入。 同步代码块的格式如下: synchronized(obj){ } 这里的obj,我们可以理解为一把锁,叫做同步监视器。 打印字符串的这一块代码,如果我们要将其保护起来,只允许同一时间点只有一个线程调用,那么可以使用同步代码块。 classV{ publicvoidshowString(Strings){ synchronized(this){ for(inti=0;i System.out.print(s.charAt(i)); } System.out.println(); } } } 两个线程同运行最后运行结果2000 publicclassSafeThreadTest2{ inti=0; publicstaticvoidmain(String[]args){ SafeThreadTest2stt2=newSafeThreadTest2(); stt2.init(); } Vv=newV(); privatevoidinit(){ //TODOAuto-generatedmethodstub newThread(newRunnable(){ @Override publicvoidrun(){ //TODOAuto-generatedmethodstub for(inti=0;i<100000;i++){ v.increase(); } } }).start(); newThread(newRunnable(){ @Override publicvoidrun(){ //TODOAuto-generatedmethodstub for(inti=0;i<100000;i++){ v.increase(); } } }).start(); //System.out.println(Thread.activeCount()); while(Thread.activeCount()! =1){ } System.out.println(i); } classV{ publicsynchronizedvoidincrease(){ i++; } } } 运行结果 200000 模拟银行取钱 packagecom.hzgg.test2; publicclassUnsafeThreadTest3{ privateintcount=100; publicstaticvoidmain(String[]args){ UnsafeThreadTest3test=newUnsafeThreadTest3(); test.test(); } publicvoidtest(){ newMyThread(90).start(); newMyThread(80).start(); } classMyThreadextendsThread{ privateintgetInt; publicMyThread(intgetInt){ this.getInt=getInt; } @Override publicvoidrun(){ if(getInt System.out.println(getName()+"减去"+getInt); try{ Thread.sleep (1); }catch(InterruptedExceptione){ e.printStackTrace(); } count-=getInt; System.out.println(getName()+"减去"+getInt+"后,count还剩下: "+count); }else{ System.out.println(getName()+"没有足够的count能够被减掉"); } } } } 多运行几次观察输出结果 Thread-0减去90 Thread-1减去80 Thread-0减去90后,count还剩下: -70 Thread-1减去80后,count还剩下: -70 这很明显不是我们想要的结果。 UnsafeThreadTest3类中有一个全局变量count,在MyThread中,接收一个getInt整数,在run方法中,如果这个getInt小于count,我们就用count减去getInt。 如果getInt大于count,那么我们打印“没有足够的count能够被减掉”。 但就在第一个线程执行的时候,传入90。 这个时候,count的值是100,所以能够减去90(但还没有减)。 但就在这个时候,另外一个线程也过来了,传入80,也小于100,所以两边都满足,最后一起减掉了。 所以程序出现了负数。 这样显然是不可以的。 这个案例可以延伸到银行取款中来。 packagecom.hzgg.test2; publicclassAccount{ privateStringcardNo; privatedoublemoney; publicAccount(StringcardNo,doublemoney){ this.cardNo=cardNo; this.money=money; } publicStringgetCardNo(){ returncardNo; } publicvoidsetCardNo(StringcardNo){ this.cardNo=cardNo; } publicdoublegetMoney(){ returnmoney; } publicvoidsetMoney(doublemoney){ this.money=money; } } packagecom.hzgg.test2; publicclassATMextendsThread{ //模拟账户 privateAccountaccount; //取多少钱 privatedoublecaMoney; publicATM(Stringname,Accountaccount,doublecaMoney){ super(name); this.account=account; this.caMoney=caMoney; } @Override publicvoidrun(){ //如果账户余额大于取钱的数目 if(account.getMoney()>caMoney){ System.out.println(getName()+"取钱成功! 共取: "+caMoney); try{ Thread.sleep (2); }catch(InterruptedExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } account.setMoney(account.getMoney()-caMoney); System.out.println("余额是: "+account.getMoney()); }else{ System.out.println(getName()+"余额不足! 取款失败"); } } publicstaticvoidmain(String[]args){ Accounta=newAccount("1111222233334444",6000); newATM("云飞日月",a,5000).start(); newATM("刘欢",a,5000).start(); } } 这几个案例让我们见到了线程存在的问题,如果不加以处理,1万次中出现1次,那也是错误的。 10.5.2同步方法 synchronized关键字也可以放到方法上,这样整个方法就是同步的。 同步监视器就是类对象本身this。 注意: synchronized关键字可以修饰方法、代码块,但是不能修饰构造方法和属性。 线程要进入同步代码块或同步方法中,必须先获得同步监视器的锁定。 也就是说必须先拿到锁,然后进入方法。 那么什么时候释放锁呢? 1.方法执行结束 2.在方法中遇到Exception,导致异常 3.程序中遇到了退出程序的代码,比如return 4.程序执行了同步监视器对象的wait()方法 10.6waitnotify 当线程A运行过程中遇到不满足的条件需要等待,等待另外的线程B去更改系统状态。 当B更改系统状态后,唤醒等待线程A。 等待线程A查看是否满足条件,如果满足则继续执行,不满足则继续等待。 举个例子。 在ATM中,有两个线程。 一个用来存款,一个用来提款。 当提款的线程提款时,发现余额不足,那么它等待有线程过来存款。 一旦有线程存款,那么马上唤醒这个提款的线程。 提款的线程继续查看是否余额大于提款额度,如果大于,则提款,否则继续等待。 这个功能的实现可以借助于Object类的waitnotify和notifyAll方法,注意,这三个方法不是Thread类的方法,而是Object类方法。 另外,要使用这些方法,必须获得同步监视器对象。 这两个线程是交替执行;wait方法只能在获取同步监视锁的方法中或代码块中才能调用同步监视锁既是synchronized packagecom.hzgg.test2; publicclassTestThreadWait{ publicstaticvoidmain(String[]args){ TestThreadWaitttw=newTestThreadWait(); ttw.init(); } Vv=newV(); privatevoidinit(){ //TODOAuto-generatedmethodstub newThread(newRunnable(){ @Override publicvoidrun(){ //TODOAuto-generatedmethodstub while(true){ v.f1(); } } }).start(); newThread(newRunnable(){ @Override publicvoidrun(){ //TODOAuto-generatedmethodstub while(true){ v.f2(); } } }).start(); } classV{ booleanflag=true; publicsynchronizedvoidf1(){ while(flag){ try{ wait(); }catch(InterruptedExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } } flag=true; notifyAll(); System.out.println("我是线程1AAAAAAAAAA"); } publicsynchronizedvoidf2(){ while(! flag){ try{ wait(); }catch(InterruptedExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } } flag=false; notifyAll(); System.out.println("我是线程2BBBBBBBBBB"); } } } 运行结果部分: 我是线程1AAAAAAAAAA 我是线程2BBBBBBBBBB 我是线程1AAAAAAAAAA 我是线程2BBBBBBBBBB 我是线程1AAAAAAAAAA 我是线程2BBBBBBBBBB 在这段代码中,我们开启了两个线程,线程中的run方法调用了V类对象的f1方法。 在f1方法中,首先必须获得同步监视器对象,也就是必须有synchronized才可以使用wait和notifyAll。 第一个线程进入f1方法,发现flag为false,没有进入if代码块,将flag设置为true(这时候另外一个线程可能已经进入f1方法,发现flag为false,于是进入wait状态),同时唤醒正在wait状态的所有线程(这里使用的notifyAll)。 这时候,进入wait状态的线程会被唤醒,继续检查是否满足条件,如果满足条件则运行,否则继续等待。 假如是三个线程没人一下的运行,代码如下 packagecom.hzgg.test2; publicclassTestThreadWait2{ publicstaticvoidmain(String[]args){ TestThreadWait2tt=newTestThreadWait2(); tt.go(); } Vv=newV(); privatevoidgo(){ //TODOAuto-generatedmethodstub newThread(newRunnable(){ @Override publicvoidrun(){ while(true){ v.f1(); } } }).start(); newThread(newRunnable(){ @Override publicvoidrun(){ //TODOAuto-generatedmethodstub while(true){ v.f2(); } } }).start(); newThread(newRunnable(){ @Override publicvoidrun(){ //TODOAuto-generatedmethodstub while(true){ v.f3(); } } }).start(); } classV{ publicinti=1; publicsynchronizedvoidf1(){ while(i! =1){ try{ wait(); }catch(InterruptedExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } } i=2; notifyAll(); System.out.println("AAAAAAAAAAAAAAAAAAAA"); } publicsynchronizedvoidf2(){ while(i! =2){ try{ wait(); }catch(InterruptedExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } } i=3; notifyAll(); System.out.println("BBBBBBBBBBBBBB"); } publicsynchronizedvoidf3(){ while(i! =3){ try{ wait(); }catch(InterruptedExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } } i=1; notifyAll(); System.out.println("CCCCCCCCCCCCCCCCCCc"); } } } AAAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBBB CCCCCCCCCCCCCCCCCCc AAAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBBB CCCCCCCCCCCCCCCCCCc 10.6.1生产者消费者问题 很多情况下,我们需要这样的模型。 大家可以想象一下吃自助餐。 在自助餐的公共区域有很多食物,我们(消费者)可以去挑选食物。 然而,这时候食物被我们选没了,于是大家伙都等待。 在等待什么呢? 等待厨师做出新的一批食物放置上来,我们就可以继续选择我们喜爱的食物。 同样,如果反过来理解的话也可以。 我们可以制造一些请求,这些请求放到一个队列中。 队列的另一端会处理请求。 如果队列中有请求,那么处理请求,没有的话等待请求的到来。 我们在队列中放入请求的时候,如果队列满了,我们就等待处理请求处理完一个请求,于是我们可以放入新的请求。 我们用代码来模拟第二种情况: Request: 请求对象 RequestContainer: 存放请求的容器 ProcessRequestThread: 处理请求的线程 MakeRequestThread: 制造请求的线程 代码 Request: 请求对象 产品类 packagecom.hzgg.threadSafe; publicclassProduct{ privateintid; privateStringname; publicProduct(intid,Stringname){ this.id=id; this.name=name; } publicintgetId(){ returnid; } publicvoidsetId(intid){ this.id=id; } publicStringgetName(){ returnname; } publicvoidsetName(Stringname){ this.name=name; } } RequestContainer: 存放请求的容器 桌子类 packagecom.hzgg.threadSafe; importjava.util.LinkedList; publicclassTable{ //容器 LinkedList publicintmax=10; /** *往桌子上方产品 *@paramp */ publicsynchronizedvoidput(Productp){ while(size()==max){ try{ wait(); }catch(InterruptedExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } } list.add(p); n
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- java 总结