黑马程序员_Java基础_多线程_12

                                            ------- android培训java培训、期待与您交流! ----------
 

导读:线程间通信-示例代码,等待唤醒,多线程的优化,生产者消费者,守护线程,join()方法,优先级&yield方法
 

1、  线程间通信-示例代码

l  什么叫线程操作的代码,赋值语句(第一个线程中)是,输出语句(第二个线程)也是。他们都在操作同一个资源,都是对于同一个资源的代码处理,因此都要放在同步当中。虽然在两个run()方法中,但是要处理同一个资源。

2、  线程通信-解决安全问题

3、  线程通信-等待唤醒机制(开发中常见)

l  一大片一大片男,一大片一大片女,是因为当输入一个名字和一个性别后,因为线程可能还是让input获得了执行权。将前一个内容给覆盖了。当某个时刻Output获得线程后,也可能输出多条语句。这由CPU的切换决定。为了输入一个,输出一个,可能加入一个标记flag,默认值设为false。输入之前先判断这里面有没有值啊,没有的话,再存入。如果有值的话,将false改为true,代表里面有数据了。当input再拿到时cpu的时候,一判断为true,就要等在那不要动了用sleep(),可是sleep的时间不确定啊。什么时候才能醒啊,当我把数据取走了之后,才能醒。最好的办法应该是wait();我叫你的时候你再动。当Output拿到执行权的时候要也做一下判断,如果为真,可取。打印完之后,设置flagfalseOutputwait()之前要将Inputnotify();

l  等待的进程在哪里呢?线程运行的时候,线程中会建立一个线程池,等待进程都存在这个线程池当中。当notify的时候唤醒的都是线程池中的线程。notify()一般唤醒第一个被等待的

例:小时候的游戏“冰棍化了

nogifyAll():唤醒线程池中的所有的。

l  wait()notify()notifyAll(),他们是操作线程的怎么跑到上帝(Object)里面去了呢?

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

Ø  锁可以是任意类型,任意对象能调用能的方法应该定义在哪里?上帝里面。使用wait()的时候抛异常了,InterruputedException。想用wait()只能try一下。这三种方法全用在同步里面。wait()notify()你必须标识出,一个线程要所持有的锁。如,try{r.wait();}catch(Exceptione){}因为同步会出现嵌套,两个锁。

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

class Res

{

       String name;

       String sex;

       boolean flag = false;

}

class Inputimplements Runnable

{

       private Res r ;

       Input(Res r)

       {

              this.r = r;

       }

       public void run()

       {

              int x = 0;

              while(true)

              {

                     synchronized(r)

                     {

                            if(r.flag)

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

                            if(x==0)

                            {

                                   r.name="mike";

                                   r.sex="man";

                            }

                            else

                            {

                                   r.name="丽丽";

                                   r.sex ="女女女女女";

                            }

                            x= (x+1)%2;

                            r.flag = true;

                            r.notify();

                     }

              }

       }

}

class Outputimplements Runnable

{

       private Res r ;

       Output(Res r)

       {

              this.r = r;

       }

       public void run()

       {

              while(true)

              {

                     synchronized(r)

                     {

                            if(!r.flag)

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

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

                            r.flag = false;

                            r.notify();

                     }

              }

       }

}

class  InputOutputDemo

{

       public static void main(String[] args)

       {

              Res r = new Res();

              Input in = new Input(r);

              Output out = new Output(r);

              Thread t1 = new Thread(in);

              Thread t2 = new Thread(out);

              t1.start();

              t2.start();

       }

}

 

4、  多线程的优化

class Res

{

       private String name;   //设置为私有的变量

       private String sex;

       private boolean flag = false;

       public synchronized void set(Stringname,String sex) //将同步放在了这里

       {

              if(flag)

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

              this.name = name;

              this.sex = sex;

              flag = true;

              this.notify();

       }

       public synchronized void out()      //将同步放在了这里

       {

              if(!flag)

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

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

              flag = false;

              this.notify();

       }

}

class Inputimplements Runnable

{

       private Res r ;

       Input(Res r)

       {

              this.r = r;

       }

       public void run()

       {

              int x = 0;

              while(true)

              {

                     if(x==0)                         //将这里的同步移到了Res中去了。

                            r.set("mike","man");                        

                     else

                            r.set("丽丽","女女女女女");                        

                     x = (x+1)%2;

              }

       }

}

class Outputimplements 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)

       {

              Res r = new Res();

              new Thread(new Input(r)).start();  //6条语句,变以2条语句

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

       }

}

 

5、线程间通信,生产者消费者。

l  t1把本方的给换醒了。因为线程池中顺序中t1t2t3t4t4唤醒t1后,t1执行完成后,把t2(本方,生产方)给唤醒了。当t2醒的时候,它没有去看这个标记。而这个标记已经被替之为真了,t1已经往里面存了数据了,t2却不知道。t2又生产了了个,这就导致了生产了2个。我们怎样让它每次醒的时候都回去判断标记呢。If只判断一次,可是如果变为while的时候就可以判断多次。

l  死锁和全部等待的区别是:

死锁:我在问你要,你在问我要,互相不放。等待是哥几个谁都动不了,都不是活着的,都冻结了。要怎么办呢,用notifyAll()即唤醒本方,也把对方唤醒。唤醒本方,一循环,它继续等。可是对方t3t4全醒了。

l  当出现多个生产者消费者都在做这件事的时候。要写while循环,要写notifyAll(),这种方式是一种比较通用的方式。

/*

对于多个生产者和消费者。为什么要定义while判断标记?原因:让被唤醒的线程再一次判断标记。

为什么定义notifyAll?因为需要唤醒对方线程。因为只用notify,容易出现只唤醒本方线程的情况。导致程序中的所有线程都等待。

*/

classProducerConsumerDemo

{

       public static void main(String[] args)

       {

              Resource r = new Resource();

              Producer pro = new Producer(r);

              Consumer con = new Consumer(r);

              Thread t1 = new Thread(pro);

              Thread t2 = new Thread(pro);

              Thread t3 = new Thread(con);

              Thread t4 = new Thread(con);

              t1.start();

              t2.start();

              t3.start();

              t4.start();

       }

}

class Resource

{

       private String name;

       private int count = 1;

       private boolean flag = false;

                     //  t1   t2

       public synchronized void set(String name)

       {

              while(flag)

                     try{this.wait();}catch(Exceptione){}//t1(放弃资格)  t2(获取资格)

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

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

              flag = true;

              this.notifyAll();

       }

       // t3   t4 

       public synchronized void out()

       {

              while(!flag)

                     try{wait();}catch(Exceptione){}//t3(放弃资格) t4(放弃资格)

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

              flag = false;

              this.notifyAll();

       }

}

class Producerimplements Runnable

{

       private Resource res;

       Producer(Resource res)

       {

              this.res = res;

       }

       public void run()

       {

              while(true)

              {

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

              }

       }

}

class Consumerimplements Runnable

{

       private Resource res;

       Consumer(Resource res)

       {

              this.res = res;

       }

       public void run()

       {

              while(true)

              {

                     res.out();

              }

       }

}

 

6、JDK5.0升级后的新特性

l  只唤醒对方不唤醒本方要怎么去做呢?

Java.util.concurrent.locks

接口Lock ,实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的Condition 对象。其中有方法lock():获取锁,unlock释放锁。程序中的锁的隐式的,可以如果用lock()unlock()的话,就变成了显式的。

l  接口Condition,将 Object 监视器方法(waitnotify  notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 setwait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。JDK1.5以后,synchronized挂了,被Lock给我替代了。Object中的waitnotifynotifyAll方法也挂了,被condition给替代了。它中的方法await()signal()唤醒一个等待线程,signalAll()唤醒所有等待线程。

l  Wait()notify()这样的语句都要放在同步语句块当中。每个wait(), notify()都要标识出自己所属的锁。Condition可以帮我们建立一个锁。用Lock中的newCondition()方法创建Condition。通过锁产生一个wait()notify()这们的实例对象。

l  还有一个问题,如果在condition.await()这里,真的抛出异常了,那么放锁的没有?没有。线程还在,我还拿着锁,但是锁我没有放,这时候别人进不来。Lock.unlock()一定要执行,所以放在finally中,释放锁(资源)。

l  JDK的新技术特性,可以建立多个锁。并只唤醒其中的某一个锁。如,在out()方法中t3t4等待,只唤醒t1t2中的一个。condition_pro.wait();只能被condition_pro.signal();唤醒,condition_con.wait();只能被condition_con.signal();唤醒。

l  其实程序中就多了三句,牛就牛在哪呢,一个锁中可以绑定多个Condition对象,同步中只能绑定一个,锁如果一嵌套就发生了死锁。

l  生产者消费者有什么替代方案呢,1.5以后它提供了显式的锁机制。以及显式的锁对象上的等待唤醒操作机制,同时把等待唤醒里进行封装,封装完后一个锁对应多个Condition。没有之前一个锁对应几个wait()notify()?一个。再建锁就再需要进行同步。两个形成嵌套,容易形成死锁。

l  /*

JDK1.5 中提供了多线程升级解决方案。

将同步Synchronized替换成现实Lock操作。将Object中的waitnotify notifyAll,替换了Condition对象。该对象可以Lock锁进行获取。该示例中,实现了本方只唤醒对方操作。

Lock:替代了Synchronized

       lock

       unlock

       newCondition()

Condition:替代了Object waitnotify notifyAll

       await();

       signal();

       signalAll();

*/

importjava.util.concurrent.locks.*;   //要导入包

classProducerConsumerDemo2

{

       public static void main(String[] args)

       {

              Resource r = new Resource();

              Producer pro = new Producer(r);

              Consumer con = new Consumer(r);

              Thread t1 = new Thread(pro);

              Thread t2 = new Thread(pro);

              Thread t3 = new Thread(con);

              Thread t4 = new Thread(con);

              t1.start();

              t2.start();

              t3.start();

              t4.start();

       }

}

class Resource

{

       private String name;

       private int count = 1;

       private boolean flag = false;

                     //  t1   t2

       private Lock lock = newReentrantLock();  //只多了这三条语句

       private Condition condition_pro =lock.newCondition();

       private Condition condition_con =lock.newCondition();

       public void set(String name)throws InterruptedException

       {

              lock.lock();

              try

              {

                     while(flag)

                            condition_pro.await();//t1,t2

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

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

                     flag = true;

                     condition_con.signal();

              }

              finally

              {

                     lock.unlock();//释放锁的动作一定要执行。

              }

       }

       // t3   t4 

       public void out()throws InterruptedException //会抛出异常

       {

              lock.lock();

              try

              {

                     while(!flag)

                            condition_con.await();

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

                     flag = false;

                     condition_pro.signal();

              }

              finally

              {

                     lock.unlock();

              }

             

       }

}

class Producerimplements Runnable

{

       private Resource res;

       Producer(Resource res)

       {

              this.res = res;

       }

       public void run()

       {

              while(true)

              {

                     Try     //这里对于捕获的异常进行捕获。

                     {

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

                     }

                     catch (InterruptedExceptione)

                     {

                     }

                    

              }

       }

}

class Consumerimplements Runnable

{

       private Resource res;

       Consumer(Resource res)

       {

              this.res = res;

       }

       public void run()

       {

              while(true)

              {

                     try

                     {

                            res.out();

                     }

                     catch (InterruptedExceptione)

                     {

                     }

              }

       }

}

 

7、多线程中停止线程

l  Thread()中有一个方法,stop(),它已经过时,但不能清除了,才程序员还用过,老程序中还有。它有一个bug,可以让程序强行的停止,无论程序是在什么状态。

l  /*

Ø  如何停止线程?

stop方法已经过时。因此只有一种,run方法结束。开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束,也就是线程结束。

Ø  特殊情况:

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

*/

class StopThreadimplements Runnable

{

       private boolean flag =true;

       public synchronized void run()

       {

              while(flag)

              {

                     try

                     {

                            wait();

}

cathch(InterruptedException e)

{

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

}

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

              }

       }

       public void changeFlag()

       {

              flag = false;

       }

}

class  StopThreadDemo

{

       public static void main(String[] args)

       {

              StopThread st = new StopThread();

              Thread t1 = new Thread(st);

              Thread t2 = new Thread(st);

              t1.setDaemon(true);

              t2.setDaemon(true);

              t1.start();

              t2.start();

 

              int num = 0;

              while(true)

              {

                     if(num++ == 60)

                     {

                            //st.changeFlag();

                            //t1.interrupt();

                            //t2.interrupt();

                            break;

                     }

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

              }

              System.out.println("over");

       }

}

l  有一种特殊的情况程序也停不下来:

两个线程开启的时候,无论什么时候抢到cpu执行权,都到publicsynchronized void run()中去运行,线程0一进来,wait()了,放弃了资格,线程1一进来,也释放的资格。主线程已经执行完了。我改变了标记但是没有结束线程。

l  Tread类中提供了一种方法,叫做interrupt()中断线程。它将处于冻结状态的线程,强制的恢复到运行中来。只有运行才能再读标记。(wait()notify()sleep()都是正常回到运行状态。)例子:催眠师,wait(),wait()给你催眠了,可是他出国了。我来了,也不会唤醒,一砖头下去,哦了。但有一个问题受伤了(出异常了)。

l  interrupt()方法:线程在调用 Object 类的 wait()wait(long)  wait(long, int) 方法,或者该类的 join()join(long)join(long, int)sleep(long)  sleep(long, int)方法过程中受阻,则其中断状态将被清除,它还将收到一个InterruptedException。(中断状态决不是中断线程,stop()方法才是中继线程。强制结束你的挂起状态,让你回到运行中来)。

 

8、守护线程

l  Thread的方法。public final void setDaemon(booleanon)将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。 该方法必须在启动线程前调用。

l  守护线程(用户线程):你所看到的线程都是前台线程,当你把某些线程标记成后台线程后,它就有了一个特殊的含义。后台线程开启后 ,和前台线程,共同抢占CPU的执行权,运行。开启运行都没有区别,就结束后有区别。当所有的前台线程结束后,后台线程会自动结束有后台依赖前台的意思。我在你在,我不在,你自动结束不用刻意结束。这就是后台线程的特点。主线程是前台线程,前台线程一结束,后台线束自动结束。

l  举例:《圣战士星矢》,雅典娜是前台线程,他们兄弟五个就是后台线程。雅典娜一挂,他们五个就失业了。

 

9、多线程Join方法

l  public final voidjoin() throws InterruptedException等待该线程终止。

主线程在往下走的时候,当读到t1.join()的话,表示t1要申请加入到运行中来,更具体一些是t1CPU执行权。Join的意思就是要抢夺CPU的执行权。这时候执行权是CPU的,CPU把执行权放在那了,这时主线程处于冻结状态了。主线程不会再跟t1CPU执行权了。当t1执行完成后,主函数才回到运行状态中来。

l  作用:当我们在进行一个多线程运算的时候,一个线程可以在运行的过程中,加入一个线程(如果满足条件的话),这个加入的线程运行完成后,另外一个线程再继续运行。

l  主线程遇到谁的Join它就等谁。如,t1.start();t1.join(); t2.start;主线程执行àt1执行à主线程和t2交替执行。t1.start();t2.start;t1.join();主线程执行àt1t2交替执行à主线程和t2交替执行。

l  如果t1用了join方法,但是t1wait()在那里了,可以用interruput强行使主线程活过来,这时候会抛出异常。

 

10、优先级&yield方法

l  toString()方法,返回该线程的字符串表示形式,包括线程名称、优先级和线程组。System.out.println(Thread.curentThread().toString());会打印出Thread[Thread-1,5,main]表示Thread1,优先级是5,所属的组是main这个组。

Ø  线程组:一般的谁开启你的你就属于哪一个组。主线程开启了t1t2。你不想属于这个组,你就new一个ThreadGroup对象,把你想封装的线程封装到这个组中就可以了。线程组,几乎用不上,用起来还挺麻烦的。

l  setPriority(int newPriority),更改线程的优先级。所有线程,包括主线程,默认优先级是5。优先级差别小的话也看不出来,在这10个优先级跨度最大的是1510。所以将它们起了一个值是MAX_PRIORITY,MIN_PRIORITY, NORM_PRIORITY。这三个名字是static常量值。直接用数字的话,可读性不好。

l  yield(),暂停当前正在执行的线程对象,并执行其他线程。(临时停止)线程在读到yield()后就释放了执行权了。稍微间缓线程执行的频率。如果不释放执行权为出现什么状况,t1线程会连续输出好几次。达到所有线程都能达到平均运行的效果。

l  多线程匿名类写法示例:

class ThreadTest

{

       public static void main(String[] args)

       {

              new Thread()       //用匿名类创建一个线程。技巧型的写法。

              {

                     public void run()

                     {

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

                            {

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

                            }

                     }

              }.start();

              for(int x=0; x<100; x++)   //for在主线程中运行。

              {

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

              }

              Runnable r  = new Runnable()  //创建另一个线程,另一种写法。

              {

                     public void run()

                     {

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

                            {

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

                            }

                     }

              };

              new Thread(r).start();

       }

}

有了多线程后 ,什么时候用多线程?

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

 

                                          ------- android培训java培训、期待与您交流! ----------  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值