java学习笔记多线程学习总结(下)

来源:互联网 发布:java中求最大公约数 编辑:程序博客网 时间:2024/05/22 00:27


八、线程间的通信

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

线程间通信例子:

eg:一个存一个输出。两个不同操作一个共享数据,形象显示线程间的通信。

 class Res                     //  定义资源类

 {   String name;    String sex;   }

 class Input implements Runnable        // 定义一个线程用于输入

  {

     private Res r;

     Input(Res r){   this.r = r;   }

     public void run()          // 线程执行语句覆写run,用于存入

     { 

        int x=0;

        while(true)

        {

           Synchronized(r)/(Input.class)/(output.class)   // 线程同步

           {

               if(x==0)

               {   r.name=”mike”;   r.sex=”man”;   }

               else

               {   r.name=”丽丽”;   r.sex=”女女女女”;   }

               x=(x+1)%2;

           }

        }

     }

  }

 class output implements Runnable   // 定义一个线程类实现Runnable接口用于输出

  {

     private Res r;

     output(Res r){   this.r=r;   }

     public void run()          // 线程执行语句覆写run,用于输入

     {

         while(true)

         {

             Synchronized(r)         // 线程同步前提多线程,一个锁

             {   System.out.println(r.name+”…”+r.sex);   }

         }

     }

  }

class InputOutputDemo    // 主函数

{

  public static void main(String[] args)

   {

     Res r = new Res();   // 实例一个共享资源传参给2个线程执行。

     Input in = new Input(r);

     Output out = new Output(r);

     Thread t1 = new Thread(in);     // 实例化两个线程

     Thread t2 = new Thread(out);

     t1.start();             // 启动两个线程

     t2.start();

   }

}


二、线程操作方法

wait()、notify()、notifyAll()必须用在同步中(因为要锁)并用锁对象调用方法。此三种方法定义在Object类中,为了使任意对象都可以调用。注意到实现一进一出的情况,故引入wait()和notify()方法。

wait()方式继承在Object,需要抛出异常。sleep方法是Thread类都抛出异常interrupt异常。

notify()方法用于唤醒全部线程(等待中的)

说明:wait、notify、notifyAll都使用在同步中,因为要对持有监视器(锁)的线程操作,所以要使用同步中,因为只             有同步才具有锁。

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

这是因为这些方法在操作同步中线程时,都必须要标识他们所操作的线程持有的锁,又有同一个锁上的被等待线程,可以被同一个锁上notify唤醒,不可以对不同锁中的线程进行唤醒。也就是说,等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中。

存入取出程序的简化示例:

class Res

{

  private String name;

  private String sex;

  private Boolean flag = false;

  public Synchronized void set(String name,String sex)

   {

     if(flag)

       try{   this.wait();  }catch(Exception e){   }

     this.name = name;

     this.sex =sex;

     flag = true;

     this.notify();

   }

  public Synchronized void out()

  {   System.out.println(name+”,,,”+sex);   }

}

class Input implements Runnable

{

  private Res r;

  Input(Res r)

  {   this.r = r;   }

  public void run()

  {  

      int x=0;

      while(true)

      {  

          if(x==0)

            r.set(“mike”,”man”);

          else

            r.set(“丽丽”,”女女女女”);

          r=(x+1)%2;

      }

   }

}

class Output implements Runnable

{

  private Res r;

   Output(Res r){   this.r=r;   }

  public void run()

   {

     while(true)

     {

        r.out();

     }

   }

}

class InputOutputDemo2

{

  public static void main(String[] args)

   {

  Resr = new Res();

  newThread(new Input(r)).start();

  newThread(new Output(r)).start();

   }

}


三、线程等待

当多个线程操作同一数据时,线程需要等待执行。

线程等待示例程序:

线程间通信——生产者消费者

class Resource

{

         privateString name;

         privateint count=1;

         privateboolean flag = false;

         publicsynchronized void set(String name)

         {

                   if(flag)

                            try{this.wait();}catch(Exceptione){}

                   this.name=name+"--"+count++;

                   System.out.println(Thread.currentThread().getName()+"......生产者。。。"+this.name);

                   flag=true;

                   this.notify();

         }

         publicsynchronized void out()

         {

                   if(!flag)

                            try{wait();}catch(Exceptione){}

                   System.out.println(Thread.currentThread().getName()+"...消费者。。。"+this.name);

                   flag=false;

                   this.notify();

         }

}

class Producer implements Runnable

{

         privateResource res;   

         Producer(Resourceres)

         {

                   this.res=res;

         }

         publicvoid run()

         {

                   while(true)

                   {

                            res.set("+商品+");

                   }

         }      

}

class Consumer implements Runnable

{

         privateResource res;

         Consumer(Resourceres)

         {

                   this.res=res;

         }

         publicvoid run()

         {

                   while(true)

                   {

                            res.set("+商品+");

                   }

         }

}

public class ProducerConsumerDemo {

 

         publicstatic void main(String[] args) {

                   //TODO 自动生成的方法存根

                   Resourcer = new Resource();

                   Producerp = new Producer(r);

                   Consumerc = new Consumer(r);

                   Threadt1 = new Thread(p);

                   Threadt2 = new Thread(c);

                   Threadt3 = new Thread(p);

                   Threadt4 = new Thread(c);

                   t3.start();

                   t4.start();

                   t1.start();

                   t2.start();

         }

}

多加几个线程后即多几个生产者和消费者后:发现出现生产两次,消费一次。或生产一次消费两次的情况。产生这种情况的原因是唤醒后没有判断标记。将if(flag)改为while(flag)。但这样会产生全部等待的情况,又不同于死锁。此时将notify改为notifyAll唤醒。if(flag)和notify只应用于,生产者和消费者都是一个的情况。


四、Java1.5新特性,Lock对象及Condition

JDK1.5中提供了多线程升级解决方案。将同步Synchronized替换成现实Lock操作。将Object中的wait,notify,notifyAll,替换了Condition对象。该对象可以通过Lock锁进行获取。该示例中实现了本方只唤醒对方的操作。一个锁可以对应对个condition,之前一个锁只对应一组wait..notify。且,释放锁的动作在finally中,一定会执行。

Condition将Object监视器方法(wait、notify、notifyAll)分解成截然不同的对象,以便通过这些对象与任意Lock实现组合使用,为每个对象提供多个等待set(wait-set  )。其中,Lock替代了synchronized方法和语句的使用,Condition替代了Object监视器方法的使用(wait、notify、notifyAll)。

线程间通信——生产者消费者2(使用新特性替换synchronized)

import java.util.concurrent.locks.*;

class Resource

{

         privateString name;

         privateint count=1;

         privateboolean flag = false;

         privateLock lock = new ReentrantLock();    // 定义Lock

         privateCondition condition_pro = lock.newCondition();     //  通过Lock定义Condition

         privateCondition condition_con = lock.newCondition();     //  通过Lock定义Condition

         publicvoid set(String name)throws InterruptedException    await方法会抛出异常

         {

                   lock.lock();    // 锁

                   try

                   {

                            while(flag)

                                     condition_pro.await();    // 调用await方法等待。

                            this.name=name+"--"+count++;

                            System.out.println(Thread.currentThread().getName()+"......生产者。。。"+this.name);

                            flag=true;

                            condition_con.signalAll();    // 唤醒线程

                   }

                   finally

                   {

                            lock.unlock();   // 解锁,必须执行,释放资源

                   }      

         }

         publicvoid out()throws InterruptedException

         {

                   lock.lock();

                   try

                   {

                            while(!flag)

                                     condition_con.await();

                            System.out.println(Thread.currentThread().getName()+"...消费者。。。"+this.name);

                            flag=false;

                            condition_pro.signalAll();

                   }

                   finally

                   {

                            lock.unlock();

                   }

}

}

class Producer implements Runnable

{

         privateResource res;   

         Producer(Resourceres)

         {

                   this.res=res;

         }

         publicvoid run()

         {

                   while(true)

                   {

                            try{

                                     res.set("+商品+");

                            }catch (InterruptedException e) {

                                     //TODO 自动生成的 catch 块

                                     e.printStackTrace();

                            }

                   }

         }      

}

class Consumer implements Runnable

{

         privateResource res;

         Consumer(Resourceres)

         {

                   this.res=res;

         }

         publicvoid run()

         {

                   while(true)

                   {

                            try{

                                     res.out();

                            }catch (InterruptedException e) {

                                     //TODO 自动生成的 catch 块

                                     e.printStackTrace();

                            }

                   }

         }

}

public classProducerConsumerDemoLockCondition {

 

         publicstatic void main(String[] args) {

                   //TODO 自动生成的方法存根

                   Resourcer = new Resource();

                   Producerp = new Producer(r);

                   Consumerc = new Consumer(r);

                   Threadt1 = new Thread(p);

                   Threadt2 = new Thread(c);

                   Threadt3 = new Thread(p);

                   Threadt4 = new Thread(c);

                   t1.start();

                   t2.start();

                   t3.start();

                   t4.start();

         }

}


五、线程的停止

1、定义循环结束标记

     因为线程运行代码一般都是循环,只要控制了循环即可。

2、使用interrupt(中断)

     该方法是结束线程的冻结状态,使线程回到运行状态中来。

   (不用stop方法是因为他有bug,强制停止。)

   (suspend方法会发生死锁,也停用)

stop方法已经过时如何停止线程? 只有一种,run方法结束。

开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束。也就是线程结束。

停止循环的一种特殊情况:

当线程处于冻结状态,就不会读取到标记,那么线程就不会结束.当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结状态进行清除.强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束.Thread类中提供了该方法,interrupted方法,清除线程的冻结状态

class StopThread implements Runnable   //定义一个线程类

{

         private boolean flag = true;    

         public synchronized void run()   //线程执行的语句

         {

                   while(flag)

                   {

                            try

                            {

                                     wait();//为真时等待

                            }

                            catch(InterruptedExceptione)

                            {

                                     System.out.println(Thread.currentThread().getName()+"....Exception"); 

                                     flag= false;   //出现异常说明强制中止,改变标记使结束.

                            }

                            System.out.println(Thread.currentThread().getName()+"....run");  //为假时执行

                   }

         }      

         publicvoid changeFlag()   // 定义改变标记方法

         {

                   flag= false;

         }

}

class StopThreadDemo

{

         publicstatic void main(String[] args)   //主函

         {

                   StopThreadst = new StopThread();   //实例一个实现Runnable接口的线程类

                   Threadt1 = new Thread(st);

                   Threadt1 = new Thread(st);         //定义两个Thread类对像

                   t1.start();

                   t2.start();

                   intnum = 0;

                   while(true)            // 主线程执行语句

                   {

                            if(num++== 60)

                            {

                                     //st.changeFlag();

                                     t1.interrupt();       // num等于60时中止线程

                                     t2.interrupt();

                                     break;

                            }

                            System.out.println(Thread.currentThread().getName()+".......num");    //主线程输出

                   }

                   System.out.println("over");

         }

}


六、守护线程和用户线程
守护线程和用户线程(后台线程依赖主线程),正在运行的线程都是守护线程时,JAVA虚拟机退出。该方法必须在启动线程前调用。

class StopThreadimplements Runnable   //定义一个线程类

{

         private boolean flag = true;

         public void run()   //线程执行的语句

         {

                   while(flag)

                   {      

                            System.out.println(Thread.currentThread().getName()+"....run");  //为假时执行

                   }

         }      

         public void changeFlag()   // 定义改变标记方法

         {

                   flag = false;

         }

}

classStopThreadDemo

{

         public static void main(String[]args)   //主函

         {

                   StopThread st = newStopThread();   //实例一个实现Runnable接口的线程类

                   Thread t1 = new Thread(st);

                   Thread t1 = newThread(st);         //定义两个Thread类对像

                   t1.setDaemon(true);

t2.setDaemon(true);  //设置t1,t2为守护线程主线程结束后,他们自动结束。

t1.start();

                   t2.start();

                   int num = 0;

                   while(true)            // 主线程执行语句

                   {

                            if(num++ == 60)

                            {

                                     break;

                            }

                            System.out.println(Thread.currentThread().getName()+".......num");    //主线程输出

                   }

                   System.out.println("over");

         }

}


七、Join方法抢夺CPU的执行权

Join:当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完A才会执行。

Join可以用来临时加入线程执行。

Join:当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完A才会执行。

Join可以用来临时加入线程执行。

class Demo imlements Runnable

{

         publicvoid run()   // 线程的执行内容

         {

         for(int x=0;x<70;x++)

         {

         System.out.println(Thread.currentThread().getName()+”…..”+x);

}

}

}

class JoinDemo

{

         publicstatic void main(String[] args)  //  主函

         {

                   Demod = new Demo();

                   Threadt1 = new Thread(d);     //  两个线程

                   Threadt2 = new Thread(d);

                   t1.start();

                   t1.join();     // 抢夺CPU的执行权,主线程停止等待t1执行完成后主线程恢复。

                   t2.start();

                   for(intx=0;x<80;x++)      // 主线程执行内容

                   {

         System.out.println(“main…..”+x);

}

System.out.println(“over”);

}

}


八、线程中的ToString()方法

ToString()方法:返回该线程的字符串表示形式,包括线程名称、优先级和线程组。

线程组:

谁开启的就属于哪个组,例如main。线程组是一个类可以自定义组ThreadGroup类。

线程优先级

线程优先级代表着抢资源的频率。注所有的线程包括主线程默认优先级是5。

更改线程的优先级:

setPriority(int newPriority)方法。参数:(MAX/MIN/NORM_PRIORITY)

eg: setPriority(Thread. MAX _PRIORITY)   设置为最大优先级


九、yield方法

yield方法为静态方法。用Thread调用。暂停当前正在执行的线程对象,并执行其它线程。强制临时释放执行权,减缓线程运行,可以达到线程都有机会平均运行效果。


十、开发中创建线程

什么时候用到多线程?

当某些代码需要同时被执行时,就用单独的线程进行封装。




0 0