代码改变世界

黑马程序员-线程间的通讯

2014-06-06 17:58  黑马程序员*  阅读(149)  评论(0)    收藏  举报

思考:wait(),notify(),notiyfAll()的特点,?

              1.这些方法存在与同步中,对持有监视器(锁)的线程操作。

              2.使用这些方法时必须要标示所属的同步的锁。

思考:为什么wait(),notify(),notiyfAll(),标示所属的同步的锁

              只有同一个锁上的等待的线程,才可以被同一个锁上notiyf唤醒,也就是说,等待和唤醒必须是同一个锁。

              所以,这些方法在操作同步中的线程时,都必须要标识它们所操作线程的锁。两伙小伙伴的例子。

思考:wait(),notify(),notiyfAll(),用来操作线程为什么定义在Object类中

              锁可以是任意对象,所以任意对象调用的方法应该定义在Object类中。

思考:wait(),sleep()有什么区别?

              wait():释放资源,释放锁。

              sleep():释放资源,不释放锁。

线程间的通讯:

其实就是多个线程在操作同一个资源,

但是操作的动作不同。

public class Demo23多线程 { 

       /**练习:存入和取出  mark,man 和丽丽 女(交替存入)

        * 出现的问题1:丽丽 男   mark 女

        * 原因:设置 丽丽 女时没有实现同步。

        * 解决;加同步函数或同步代码块

        * 出现问题2;无法实现存入一个,取出一个

        * 原因:其中一个线程可能会抢到多次执行权。

        * 解决办法;添加标记,wait(),notify()

        * 自身错误:if(){wait},后面不可以写else,要让它醒来后继续执行未完成的代码     

        */      

  

public static void main(String[] args) {
     Person23 p = new Person23();
     new Thread(new In(p)).start();
     new Thread(new Out(p)).start();
}
}
class Person23{
private String name;
private String sex;
boolean flag=false;
public synchronized void set(String name,String sex)  {
    if(flag){         
        try {
            this.wait();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
//    else{//犯过多次错。
        this.name=name;
        this.sex=sex;         
        flag=true;
        this.notify();         
//    }        
}
public synchronized void get()  {
    if(!flag){                 
        try {
            this.wait();
        } catch (InterruptedException e) {                 
            e.printStackTrace();
        }    
} 
    System.out.println(name+sex);
        flag=false;
        this.notify();             
}
}
class In implements Runnable{
private Person23 p;
public In(Person23 p) {
    super();
    this.p = p;
}
public void run() {
    int count = 1;
     while(true){                     
                if (count%2==0) {                        
                    p.set("丽丽", "女");                     
                } else {                         
                    p.set("mark", "man");                        
                }
                count++;                                   
         }        
}    
}
class Out implements Runnable{
private Person23 p;
public Out(Person23 p) {
    super();
    this.p = p;
}     
public void run() {
    while(true){             
            p.get();             
    }    
}    
}

public class ThreadTest8 {

       /**练习:消费者和生产者

          问题1:当线程超过两个后,会出现,生产一个,消费两个的情况。

          原因:if()语句中,wait后有两个线程等待。被唤醒后,导致其中一个没有判断条件,就执行了

                     所以出现了打印两次的情况。

              解决:使用while语句。但是while会导致所有的线程等待,使用notifyAll();

              即使唤醒了本方,也不会出现连续生产两个的情况。因为本方被唤醒,会再次判断标记,并等待。

              新问题:唤醒本方会导致本方去抢资源,即使抢到了也等待,但是消耗了资源。

              解决:1.5新特型Lock。

  `

public static void main(String[] args) {
     Resource8 r = new Resource8();
    Custom custom = new Custom(r);
    Product product = new Product(r);
    new Thread(custom).start();
    new Thread(custom).start();
    new Thread(product).start();
    new Thread(product).start();
}
}

//共享资源,生产和消费资源
class Resource8{
private String name;
private int num;    
private boolean flag=false;
public void set(String name,int num){
    this.name=name;
    this.num=num;
}
public int getNum(){
    return num;
}
public String getName(){
    return name;
}

public synchronized void pruduce(String str,int listnum) { 
     while(flag){//超过两个线程后,if判断易出现两个线程等待结束后,其中一个不会判断标记,导致多生产一次(连个线程只判断了一次)
 //但是用while会导致所有线程等待,所以notifyAll
                       //4个线程会全部等待。2个没事。因为线程唤醒了本方的线程,判断了一次标记后,也等待了。而没有去唤醒对方。
           try {
                   wait();
           } catch (InterruptedException e) {
                   e.printStackTrace();
                  System.out.println("线程等待出错");
           }
    }
    set(str,listnum);
    System.out.println(Thread.currentThread().getName()+getName()+"   生产...."+getNum());
    flag=true;
    notifyAll();
}
public synchronized void consume() {               
    while(!flag){
           try {
                  wait();
           } catch (InterruptedException e) {                      
                  e.printStackTrace();
                  System.out.println("线程等待出错");
           }
    }
    System.out.println(Thread.currentThread().getName()+getName()+"   消费....."+getNum());
    flag=false;
    notifyAll();           
}     }
class Product implements Runnable{
private Resource8 res;
private int num=1;
public Product(Resource8 r) {
    super();
    this.res = r;
} 
public void run() { 
    while(true){
           //传入生产的商品的名称和编号,编号从1开始
           res.pruduce("商品",num++);
           //生产超过5000个,停止生产。
    }     }}

class Custom implements Runnable{
private Resource8 res;
public Custom(Resource8 res) {
    super();
    this.res = res ;
} 
public void run() { 
//开始消费
    while(true){
                  res.consume();
    }     }}

 

jdk1.5中提供了多线程升级解决方案。java.util.concurrent.locks

接口 Lock

将同步synchroniaed替换成显示的lock操作

将object中的wait,notify,notifyall 替换成了condition对象中await(),signal,signalAll,

该对象可以lock锁,进行获取。 

Lock(锁),具有lock和unlock,newcondition方法,

condition包含singal和awiat方法。而且condition可以建立多个。

本实例中,实现了本方只唤醒对方的操作。

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class 多线程新特性 {
       /**生产者和消费者
        * Lock接口。conditon代替synchronized
        */
       public static void main(String[] args) {
               resource r = new resource();
               new Thread(new produce(r)).start();
               new Thread(new produce(r)).start();
               new Thread(new custom(r)).start();
               new Thread(new custom(r)).start();
       }
}
class resource{
        private int num;
        boolean flag=false;
        Lock lock = new ReentrantLock();
        Condition pro = lock.newCondition();
        Condition con = lock.newCondition();
        public void produce(){
               try {
                     lock.lock();
                     if(flag){
                            pro.await();
                     }
                     num++;
                     System.out.println(Thread.currentThread().getName() + "生产商品编号"
                                   + num + ".....");
                     flag = true;
                     con.signal();
              } catch (Exception e) {               
                     e.printStackTrace();
              } finally {
                     lock.unlock();
              }           
        }
        public void consume(){
              try {
                     lock.lock();
                     if(!flag){
                            con.await();
                     }
                     System.out.println(Thread.currentThread().getName() + "消费商品编号"
                                   + num + ".....");
                     flag = false;
                     pro.signal();
              } catch (Exception e) {               
                     e.printStackTrace();
              } finally {
                     lock.unlock();
              } }
}
class produce implements Runnable{
       private resource r;
       public produce(resource r) {
              super();
              this.r = r;
       }
       public void run() {
              while (true) {
                     r.produce();
              }}}
class custom implements Runnable{
       private resource r;
       public custom(resource r) {
              super();
              this.r = r;
       }
       public void run() {              
              while (true) {
                     r.consume();
              }    
       }
}

如何停止线程?

原理:只有一种,run方法结束。

怎样结束run方法呢?

线程运行代码一般都是循环,只要控制住循环,就可以控制run方法,也就是线程结束。

定义循环结束标记,

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

方法一:定义改变标记的方法,在调用者的函数中调用该方法。

特殊方法二:

在while存在wait(),导致当前线程全部冻结。所以没法改变标记,程序无法结束。

可以使用interrupt(),但是它会报异常。所以在异常处理时调用改变标记的方法。

方法二:更专业。

Thread类提供了interrupt方法,

interrupt:清除线程的终结状态。interrupt()方法。

使用interrupt(中断)方法,不是中断,而是让线程回到运行状态。

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

注;stop方法已经过时。不在使用。

class stopThread implements Runnable

{     private boolean flag= true;

       public synchronized void run(){

              while (flag){

                     try   {

                            wait();

                     }

                     catch (InterruptedException e)/InterruptedException的异常

                     {

             System.out.println("InterruptedException run");

                      changeFlag()//st.changeFlag()也可以结束线程,两者有什么区别//在catch里面处理更专业。                     

                     }                  

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

              }    

              System.out.println("over");

       }

       public void changeFlag(){

              flag= false;

       }

}

特殊情况;

当线程处于冻结状态。就不会读取标记,那么线程就不会结束

当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进清除。

强制让线程恢复到运行状态。这样就可以操作标记让线程结束。

Thread类提供了该方法,

interrupt:清除线程的终结状态。interrupt()方法。

使用interrupt(中断)方法。

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

注;stop方法已经过时。不在使用。

boolean isDaemon()

          测试该线程是否为守护线程。在启动线程前启用

前台线程和后台线程:

前台线程:可以看见的线程,mian方法是前台线程。

后台线程(守护线程):在前台结束后,自动结束。(守护雅典娜,她挂了,升斗士没意义)

守护线程:如果线程被标记为守护线程,它会正常的与前台抢夺cpu资源。区别是:前台线程结束,后台线程

自动结束,虚拟机退出。

 Thread t1 = new Thread(st);

                Thread t2 = new Thread(st);

                t1.setDaemon(true);//守护线程,启动前调用

                t2.setDaemon(true);

                t1.start();

                t2.start(); 

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

用处:join 可以用来临时加入线程执行。

Thread t1 = new Thread(d);

              Thread t2 = new Thread(d);

              t1.start();

       //    t2.join();//1*不可以放在此处,因为主线程执行到此行,主线程等待,t2还未开始。

              t1.join();//主线程冻结并交出执行权。t1结束才会执行。

              t2.start();

1.thread.currentThread.toString();获取线程组:

2.优先级:默认优先级5

setPriority(int newPriority) //0-10;

       更改线程的优先级。   

static int MAX_PRIORITY

          线程可以具有的最高优先级。

static int MIN_PRIORITY

          线程可以具有的最低优先级。

static int NORM_PRIORITY

          分配给线程的默认优先级。

setPriority(10);//别写10,setPriority(Thread.MAX_PRIOITY)

记住:数据固定用常量,数据共享用静态。

static void yield()

          暂停当前正在执行的线程对象,并执行其他线程。

先看看该方法的解释,Thread.yield()方法作用是:暂停当前正在执行的线程对象,并执行其他线程

暂停时什么状态?什么时候解除暂停?

其实yield方法无法实现机会绝对平等。yield()只是使当前线程重新回到【可执行状态】,

允许具有相同优先级的其他线程获得运行机会,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行,

即同一线程再次被执行。 

一些小问题:当多个循环在一起,只能一个循环结束才能执行下一个,效率低。

show(){

       for(int x=0;x++;x<1000){}

       for(int x=0;x++;x<1000){}

       for(int x=0;x++;x<1000){}

}

怎么解决:封装线程。同匿名内部类,技巧写法。

show(){

       new Thread(){

              public void run()   {

                     for(int x=0;x++;x<1000){}

              }

       }.start();

       for(int x=0;x++;x<1000){}; 

       Runnable r =new Runnable(){//Thread 继承了Runnalbe      

              public void run()  {

                     for(int x=0;x++;x<1000){}

              }  };

       new Thread(r).start();        

}

也可以用,Excutors线程工具类。