黑马程序员java学习—多线程2:等待唤醒机制

来源:互联网 发布:2016it企业排名 编辑:程序博客网 时间:2024/04/28 12:01

同步函数:

只有当同步代码块和同步函数封装的内容是一样的时候,才可以直接将同步关键字作为修饰符修饰函数即可。

这样函数就具备了同步性。

这就是同步函数。同步的另一种表现形式。

这种表现形式较为简单。

同步函数使用锁是this 。是调用同步函数的对象。

同步函数和同步代码块有什么区别呢?

1,同步函数比同步代码块写法简单。

2,同步函数使用的锁是this。同步代码块使用的锁是任意指定的对象。

建议开发时,使用同步代码块。尤其是需要不同锁时。

静态同步函数使用的锁是什么?

静态随着类的加载而加载的,这时内存中存储的对象至少有一个

就是该类字节码文件对象。

这个对象的表示方式 类名.class 它就表示字节码文件对象。 

 

饿汉式。

class Single

{

     private static final Single s = new Single();

     private Single(){}

     public static Single getInstance()

    {

        return s;

    }

}

 

懒汉式

在被多线程并发访问时,会出现多线程安全问题。

为了解决,加入了同步,虽然安全问题解决了,

但是性能降低了。

有没有办法将安全问题解决的同时,还可以提高性能。

解决改进方法:改成同步代码块。 

可以通过双重判断的形式来完成这个过程。 

class Single

{

      private static Single s = null;

      private Single(){}

      public static  Single getInstance()

     {

         if(s==null)// -->0 -->1

        {

           synchronized(Single.class)

           {

                if(s==null)// -->0 -->1

                s = new Single();

           }

          }

          return s;

    }

}

同步的另一个弊端:死锁。

最常见的死锁情况:同步嵌套。

同步中还有同步,两个同步用的不是一个锁。 

记住尽量避免同步嵌套的情况。

在0和1之间不断变换

if(x==1){}

else{}

x=(x+1)%2;

线程间通信:多个线程在处理同一个资源。 但是处理的动作(线程的任务)却不相同。

等待唤醒机制。

涉及到的方法:

wait():等待,将正在执行的线程释放其执行资格和执行权,并存储到线程池中。

notify():唤醒,唤醒线程池中被wait的线程,一次唤醒一个,而且是任意的。

notifyAll():唤醒全部,可以将线程池中的所有wait线程都唤醒,

唤醒的意思就是让线程池中的线程具备执行资格。

这些方法都要使用在同步中才有效。

这些方法在使用时必须标明所属锁,这样才可以明确出这些放操作的到底是哪个锁上的线程。

为什么这些操作线程的方法定义在Object类中?

因为这些方法在使用时,必须要标明所属的锁,而锁又可以是任意对象。

能被任意对象调用的方法一定定义在Object类中。

代码演示

synchronized(r)

{

 if(r.flag){

             try{r.wait();}

             catch(InterruptedException e){}

             }

if(x==0)

{

         r.name = "Mike";

         r.sex = "man";

}

else

{

         r.name = "小红";

         r.sex = "女";

}

r.flag = true;

r.notify();

}

synchronized(r)

{

      if(!r.flag)

{

      try{r.wait();}

      catch(InterruptedException e){}

}

      System.out.println(r.name+"....."+r.sex);

      r.flag = false;

      r.notify();

}

多生产者多消费者问题。

发生的问题:

生产者生产的商品没有被消费就生产了新的商品。

问题在于两点:

1,本方唤醒了本方。

2,被唤醒的本方没有判断标记。只要将if判断该外while判断。 

将if改为while循环判断标记后,出现了死锁。

因为本方唤醒了本方,而被唤醒的本方一判断标记,就继续等待。这样所有的线程都等待了。

必须唤醒对方才行,但是没有直接唤醒对方的动作,所以就使用了notifyAll,唤醒全部。 


这个程序有一个bug。就是每次notifyAll。都会唤醒本方。
可不可以只唤醒对方呢?

JDK1.5版本提供了一些新的对象,优化了等待唤醒机制。

1,将synchronized 替换成了Lock接口。
  将隐式锁,升级成了显示锁。
   Lock
  获取锁:lock();
  释放锁:unlock();注意:释放的动作一定要执行,所以通常定义在finally中。
  获取Condition对象:newCondition();
2,将Object中的wait,notify,notifyAll方法都替换成了Condition的await,signal,signalAll。
 和以前不同是:一个同步代码块具备一个锁,该所以具备自己的独立wait和notify方法。
 现在是将wait,notify等方法,封装进一个特有的对象Condition,而一个Lock锁上可以有多个Condition对象。


 Lock lock = new ReentrantLock();

 Condition conA = lock.newCondition();
 Condition conB = lock.newCondition();

 con.await();//生产,,消费

 

 con.signal();生产


 set()
 {
    if(flag)
   conA.await();//生产者,
   code......;

  flag = true;
  conB.signal();
 }
 
 out()
 {
  if(!flag)
   conB.await();//消费者

  code....;
  flag = false;
  conA.signal();
 }

 

wait和sleep的区别:
wait:释放cpu执行权,释放同步中锁。
sleep:释放cpu执行权,不释放同步中锁。


synchronized(锁)
{
 wait();
}


停止线程:
 stop过时。
 原理:run方法结束。run方法中通常定义循环,指定控制住循环线程即可结束。

 1,定义结束标记。
 2,当线程处于了冻结状态,没有执行标记,程序一样无法结束。
  这时可以循环,正常退出冻结状态,或者强制结束冻结状态。
  强制结束冻结状态:interrupt();目的是线程强制从冻结状态恢复到运行状态。
  但是会发生InterruptedException异常。