A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 善良的禽兽 中级黑马   /  2015-9-30 12:37  /  516 人查看  /  3 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文


线程之间的通讯:
其实就是多个线程在操作同一个资源。
但是操作的动作不同。


wait:
notify;
notifyAll;


都使用在同步中, 因为要对只有监视器(锁)的线程操作。
所以要使用在同步中, 因为只有同步才具有锁;


为什么这些操作线程的方法要定义Object类中呢?
因为这些方法在操作同步中线程时, 都必须要标识他们所操作线程只有锁。
只有同一个锁上的被等待线程, 可以被同一个锁上的notify唤醒;
不可以对不同锁中的线程进行唤醒;


也就是说, 等待和唤醒必须是同一个锁;
而锁可以是任意对象, 所以可以被任意对象调用的方法定义在Object类中;






    我们用Synchronized进行类对象的成员函数进行同步操作的时候,该Syschronized默认的锁就是this,  当某一个线程放弃资源(this.wait())之后, 该线程就进去到线程池中, 当执行this.notify的时候线程池中的第一条线程获得资源(实际上这是一个队列的数据结构, 先进先出), 这时候就会发生一个问题, 因为在线程池中, 所有的线程都是一样的, 每次都是取出线程池中的第一条线程, 此时我们如果是解决生产消费者问题的时候, 就会发生生产线程又唤醒了另一条生产线程...
   为了解决这个问题, 在JDK1.5中JAVA对多线程提供了升级解决方案, 将同步Synchronized替换成了现实的Lock操作, 将Object对象中的wait(), notify(), notifyAll()替换成了Condition对象对应的await(), signal(), signalAll().该对象可以使用语句 Lock对象.newCondition()进行获取, 这时候我们就可以将线程进行整理分类, 然后在程序运行的时候唤醒指定类型的线程;


   当我们使用Lock对象的时候, 不要忘记导入:
      import java.util.concurrent.locks.*;
   
   定义锁:
      private Lock lock = new ReentrantLock();


   
   Condition对象的定义方法;
      Condition condition = lock.newCondition();






   
   看了几遍生产者消费者问题的视频之后总算搞懂了, 这个ProducerConsumer程序为什么会产生全部等待的状态;
   首先, 如果我们只使用两条线程来解决这个问题(一条线程用于生产产品, 另外一条线程用来消费产品), 是不会出现任何问题的, 之后自己又增加了两条线程, 就出现了,程序运行到一半卡死的状态;
   
我们这里总共有五条线程
main线程......也就是主函数线程;
Thread0 和 Thread1  这两条线程是生产者线程
Thread2 和 Thread3  这两条线程是消费者线程


首先Thread0生产一个商品, 然后flag被赋值为true, 并进入了线程池中,
此时Thread1线程开启, 因为flag为true,Thread1也进入了线程池,


Thread2线程开启, 消费了Thread0生产的商品后, 将flag赋值为false, 并进入线程池,
Thread3经过判断之后同样进入线程池...


到目前为止线程池中的线程队列为:


Thread0,   Thread1,   Thread2,  Thread3;


当然最先唤醒的是Thread0,此时flag的值为false, Thread0便跳出while循环;
生产完商品之后, 将flag赋值为true, 并将线程池中的第一个线程唤醒(也就是Thread1, Thread1仅仅是获取资格, 并没有执行权)
然后Thread0在执行flag==true判断的时候, 又被扔进了线程池, 此时把控制权交给了Thread1, 很不幸, 此时Thread1这条线程,
执行阶段正好在while(flag == true)这个循环中, 更不幸的是, flag正好为true;  此时就是一个死循环了, 而且Thread1这条线程
也没法notify Thread2或者Thread3来消费商品, 于是, 大家都睡了.....






总结:   当然, 不同的机器不同的状态下, 所执行的线程顺序可能不一样,
但是都有可能遇到这种全部等待的情况,
造成这种情况的原因就是某一线程又唤醒了本类线程,
而且本类线程不满足条件,导致刚唤醒的线程又直接wait()了,
没法唤醒对方线程,所以程序就卡住不动了, 这应该不叫死锁, 而是全部等待....  

   实际上, 在写多线程的程序中我们经常会遇到线程wait()之后没有其他线程将其唤醒的时候, 此时我们只能”自己把自己唤醒了”;
      在Thread类中, 提供了interrupt()方法, 可以强制将某线程唤醒, 不管该线程是处于wait()状态, 还是sleep()状态, 因为是强制唤醒, 所以Interrupt()方法还是会抛出错误..




       此外, JAVA还提供了守护线程机制; 将线程标记为守护线程或者用户线程, 当正在运行的都是守护线程时, JAVA虚拟机退出; 该方法必须在线程启动先调用...
       所谓的守护线程我们可以这样理解, 多条守护线程守护着程序中的非守护线程, 当这些非守护线程都挂了以后, 这些守护线程便没有存在的意义了, 自然也挂.....
       如何将线程标记为守护线程:
               Thread t1 = new Thread();
               t1.setDaemon(true);



      在Java中线程可以调用toString()方法来显示自身线程的信息, 该方法返回线程的字符串表现形式,包括线程名称, 优先级和线程组;
      在系统中每一条线程都有自己的优先级, 系统中线程优先级的范围是1~10, 但是所有线程默认的优先级是5,  这些优先级中跨度最大的是1, 5, 10, 所以Java定义了三个常量来表示这三种优先级,  分别对应, Thread.MIN_PRIORITY, Thread.NORM_PRIORITY, Thread.MAX_PRIORITY;
      当然我们可以调用某一线程的某个方法来设置该线程的优先级:
               t1.setPriority(Thread.MAX_PRIORITY);    这里就像t1这条线程的优先级设置为最高了





    我们知道在系统中每条线程是交替运行的, 然后每条线程的运行时间是由CPU决定的, 有可能一条线程占用CPU很久的时间, 这时候我们可以在这条线程的代码块中加入Thread.yield(); 这句程序, 这句程序的作用是,  该线程自动让出执行权给其他线程(不过并不是该条线程就死了, 之后控制权还是会回到他手里)


3 个回复

倒序浏览
等待唤醒机制啊
回复 使用道具 举报
思路明确,比较简洁地说明了线程间通讯的原则,已收藏
回复 使用道具 举报
不错。。。继续加油                              
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马