Java学习笔记14

23、遇到多线程安全问题,杀手锏是同步,但是如果加了同步还不解决问题,就要考虑同步的前提条件。

例如下图我们想做到输入一个姓名、性别就输出一个姓名、性别。


24、必须要注意wait()和notify(),是有所属的,属于某个锁(对象),他们都是监视某个锁管辖内的线程的状态。所以想wait()或者notify(),必须通过锁(对象)来调用。可以结合23中的图来理解,一个锁代表一个木头人游戏小组,里面的人就是线程,这个小组的成员冻结了,必然是这个小组的别人来唤醒notify,而不是别的小组(锁)的成员来唤醒,23上面图方框就代表一个锁,两根线就代表两个线程,一个输入线程,一个输出线程,两根线搭到了方框(锁)上,就代表有所属关系,正在执行的输入或者输出线程里有一句r.wait( )即锁说你这个线程wait一下,若是r.notify()即锁说归我所管的(搭在我这个框上的线)正在wait()的线程醒一醒。正是因为有所属关系,才使得可以使特定的线程wait()或者notify(),因为被锁管理的线程总是属于某个锁的。

锁就是监视器。不同的锁有不同的线程池,存放不同所属的被wait()的线程


wait()和sleep()一样都会往外抛InterruptionException异常,但是run()方法有没声明往外抛异常,所以wait()和sleep()也不能直接往外抛,只能将其捕捉try{}catch(InterruptionException e){ }




在输入和输出中不能new Resource,否则各自产生了一个对资源象,不是操作的一个资源。由于任务是处理操作资源,所以应该一初始化就把任务分配,所以在构造函数里接收资源对象即可。

25、线程间的通信--多生产多消费者的问题

一个线程在某句执行wait( ),当被notify()唤醒后就直接往下执行去了,注意:直接从wait()语句直接往下执行去了。notify是唤醒线程池中的任意一个,可能是输入的线程,也可能是输出的线程。

下面的程序可能出现死锁的情况:


当t2,t3,wait后,t0把t1 notify,之后以判断flag == true,t0 wait() ,轮到t1执行,t1一判断flag == true就也wait()了,此时4个线程都wait()了,所以就相当于线程都死锁了。

所以问题在于唤醒的是本方,而不是对方,改成notifyall()就可以保障唤醒对方(虽然也唤醒了本方,但是本方唤醒后一判断就又wait()了)。

我们在解决多生产,多消费问题时,会碰到2个问题:1、用if(flag)而不是whil(flag)导致生产了好的,但是没消费,而while的循环判断就解决了这个问题。2、死锁问题,就是随机唤醒了本方线程,而没有唤醒对方线程,用notifyall就可以解决这个问题。



28、java在1.5版本后用lock来替代synchronized


以前锁的获取和释放是由底层来完成的,基本上都是自动的。但是java5.0以后变成lock对象,获取锁(锁上)和释放锁都是手动,在执行代码时有可能发生异常,致使解锁失败,所以就要把释放锁写在finally里面,不管有什么异常,释放锁是一定会执行的。Reentranlock()是互斥锁。



wait()和notify()都是object类的方法。


一个condition包含一组wait,notify,notifyAll,lock锁可以有多个condition对象,也即多组那三个。之前一个object对象只能有一组


lock替代同步synchronized,而condition替代object里面的方法。

30、以前是一个object只有一组监视器,wait()全部,notify()全部(我们之前只需唤醒对方的线程,不需要唤醒全部,那样效率低),但是现在有了多组监视器后,可以一组监视生产者,一组监视消费者,这样就不存在全部唤醒的问题,效率就不在低下。

生成2组监视器,在set方法中放一个Condition对象(名字为producer_con)起到监视生产者的作用,在out方法中放一个Condition对象(名字为consumer_con)起到监视消费者的作用,监视就包括wait(),notify(),notifyAll()功能,但是在condition对象中不是这个叫法,而是await(),singal(),singalAll()。放在set中的代码,是线程0,1要执行的任务,所以一运行set中的监视器,监视的就是线程0和1,消费者那边也是同样的道理。

31、这次不再只生产一个消费一个,而是用一个数组容器来装烤鸭,示意图如下:


32、wait()和sleep()的区别

线程想能执行代码,就必须获得锁了才可以。同步里只有一个线程可以执行(谁拿锁谁执行),但是同步里活的线程可能不止一个。



t0执行进入show()里面,拿到锁,碰见wait(),让出执行权和锁,这时t1也进入了show(),也wait(),让出锁和执行权,同样t2也让出了执行权锁和,wait()那里,此时t4拿到锁和执行权执行method()方法,一句notifyAll唤醒show里面wait()的三个线程,但是处以临时阻塞状态,有执行资格但是没有执行权。此时t4在拿着锁,就算执行权切换到t1,t2或t3上,但是由于他们3个线程都没有拿到锁,所以只能无法执行,又把执行权切换到了t4上面。相当于唤醒了t1,t2,t3,但是等t4全部内容执行完才释放锁,轮到其他的线程执行,譬如轮到t1,那么t1拿到锁,t2,t3要等t1执行完才把执行权切给别的线程。

33、停止线程的方法。

为什么需要多线程?因为要执行的代码里面有很多(很大很长)的循环,主线程不能等循环执行完再执行循环下面的语句,所以才有多线程。一个线程单独去执行那个很长的循环。也就是说线程执行的run方法里如果没有循环,多线程是没有意义的。

34、interrupt()方法的作用就是清除线程的冻结状态,使其恢复到具有执行资格的(临时阻塞)状态。不用notify就能把线程从wait()弄醒,不用时间到就能把sleep的线程弄醒(活),这就是interrupt()的功能,但是会因为它而抛出interruptedEXception异常。



不再像以前一样使用while(true)使得线程一直运行下去,而是借助标志位来控制线程的结束与否。



35、setDaemon(true),设置线程为守护线程,也即后台线程,别的(前台)线程(手动设置标号)都结束后,后台线程立即结束,不管其处于什么状态。守护线程也叫用户线程,依附于系统线程。setDaemon(true)要在线程启动前写好,先设置,再t.start().

wait()会抛出interruptedException异常,但是t1.interrupt()中断方法并不会抛异常。

36、join也会往外抛异常。t1.join()意思就是当前运行的主线程要让出执行权和执行资格,让其先执行,执行完之后主线程再回复执行资格。优先级就是获取执行的几率,优先级1-10,越高,执行几率越大。1.5,10的优先级差距最大。默认优先级是5。


线程组类ThreadGroup,线程组表示一个线程的集合,之前t1.interrupt(),t2.interrupt()...t10.interrupt()是一个个唤醒的,线程组相当于打包,一个Interrupt就唤醒了线程组里的所有冻结状态线程,效率高。

Thread.yeild()相当于释放下执行权,让别人也执行一下,大家轮流执行的意思。

37、多线程面试题




上图中Runnable接口是抽象类,Test实现了Runnable接口就必须覆盖run(){ }方法。但是图中是run(ss) { },并不是覆盖,而是方法的重载,所以报错。

new Thread(new Runnable){ }相当于是匿名内部类,可以参考匿名内部类的构成方法(new + 父类的名字{里面代码覆盖父类的方法})那一章,也就是说这其实是Thread的一个子类,相当于class t extends Thread. 而Thread(new Runnable())则是传过来了一个任务也同样覆盖了Thread自己的run方法,线程子类和父类都具有run()方法,自然是子类的优先执行,从结果也可知优先执行的是子类的run方法。子类没有run方法时,才执行自己的run方法任务(也就是Runnable对象里的run方法)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值