黑马程序员-Java多线程-day12

---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------

1、多线程(线程间通信)

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

wait();     notify();  notifyAll();都是用在同步中,因为要对持有监视器(锁)的线程操作。

所以要使用在同步中,因为只有同步才具有锁。

 

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

因为这些方法在操作同步中线程时,都必须要标识它们锁操作线程持有的锁,只有同一个锁上的被等待线程,可以被同一个锁上的notify唤醒。不可以对不同锁中的线程进行唤醒。

也就是说:等待和唤醒必须是同一个锁

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

class Res
{
       String name;
       String sex;
       boolean flag = false;
}
class Input implements Runnable
{
       private Res r;
       //Object obj = new Object();
       Input(Resr)
       {
              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 Output implements Runnable
{
       private Res r;
       //Object obj = new Object();
       Output(Resr)
       {
              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();
       }
}

小结:发现线程出现安全机制后首先就要反思其有没有满足同步代码块的两个前提条件,第一,是否是两个或两个以上的线程,咋一看发现是的,但实际上没有满足,因此这里需要将if else和Output中的输出语句均加上同步synchronized;第二,是否是同一个锁,通过运行发现,不是的,因此这里需要添加一个Class类对象,例如:Input.class,而又发现程序中的r也是唯一对象,因此在synchronized()中对象也可以是r.

 

2、线程间通信-代码优化

class Res
{
       private String name;
       private String sex;
       private boolean flag = false;
      
       public synchronized void set(String name,String sex)//同步函数,让锁变得唯一,其中这里的锁是this
       {
              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 Input implements Runnable
{
       private Res r;
       Input(Resr)
       {
              this.r = r;
       }
       public void run()
       {
              int x=0;
              while(true)
              {
                     if(x==0)
                            r.set("mike","man");
                     else
                            r.set("丽丽","女女女女女女");
                     x= (x+1)%2;
              }
       }
}
 
class Output implements Runnable
{
       private Res r;
       Output(Resr)
       {
              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();
              new Thread(new Output(r)).start();
       }
}

3、线程间通信-生产者消费者

当生产者与消费者均有多个的时候,需要循环判断flag标记,

因此这里就需要将if换成while语句进行循环判断;

而为了防止多个线程出现失去执行权的时候,这里就需要将全部线程唤醒,用notifyAll();

class ProducerConsumerDemo 
{
       public static void main(String[] args)
       {
              Resource res = new Resource();
              Producer pro = new Producer(res);
              Consumer con = new Consumer(res);
 
              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;
       public synchronized void set(String name)
       {
              while(flag)//while循环可进行多次判断
                     try{this.wait();}catch(Exceptione){}
              this.name = name+"---"+count++;
              System.out.println(Thread.currentThread().getName()+".....生产者....."+this.name);
              flag = true;
              this.notifyAll();//既唤醒本方又唤醒对方
       }
 
       public synchronized void out()
       {
              while(!flag)
                     try{this.wait();}catch(Exceptione){}
              System.out.println(Thread.currentThread().getName()+"........消费者........."+this.name);
              flag = false;
              this.notifyAll();
       }
}
 
class Producer implements Runnable
{
       private Resource res;
       Producer(Resourceres)
       {
              this.res = res;
       }
       public void run()
       {
              while(true)
              {
                     res.set("商品");
              }
       }
}
 
class Consumer implements Runnable
{
       private Resource res;
       Consumer(Resourceres)
       {
              this.res = res;
       }
       public void run()
       {
              while(true)
              {
                     res.out();
              }
       }
}

当生产者与消费者均有多个的时候,需要循环判断flag标记,因此这里就需要将if换成while语句进行循环判断;而为了防止多个线程出现失去执行权的时候,这里就需要将全部线程唤醒,用notifyAll();

对于多个生产者和消费者,为什么要定义while判断标记呢?

原因:让被唤醒的线程再一次判断标记。

为什么定义notifyAll?

因为需要唤醒对方线程,因为只用notify,容易出现只唤醒奔放线程的情况,导致程序中的所有线程都等待。

 

Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,

以便通过将这些对象与任意Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。

其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法

的使用。 Lock替代synchronized,Condition替代Object


4、JDK1.5中的多线程特性

JDK 1.5中提供了多线程升级解决方案。

将同步synchronized替换成现实lock操作。

将Object中的wait,notify,notifyAll替换成了Condition对象。

该对象可以通过Lock锁进行获取。

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

import java.util.concurrent.locks.*;//记得导入相应的包文件
class ProducerConsumerDemo2
{
       public static void main(String[] args)
       {
              Resource res = new Resource();
 
              Producer pro = new Producer(res);
              Consumer con = new Consumer(res);
 
              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;
       private Lock lock = new ReentrantLock();
       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();//await()会出现异常,需要在函数上抛出
                     this.name= name+"---"+count++;
                     System.out.println(Thread.currentThread().getName()+".....生产者....."+this.name);
                     flag= true;
                     condition_con.signal();
              }
              finally
              {
                     lock.unlock();//释放锁的动作一定要执行。
              }
       }
 
       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 Producer implements Runnable
{
       private Resource res;
       Producer(Resourceres)
       {
              this.res = res;
       }
       public void run()
       {
              while(true)
              {
                     try
                     {
                            res.set("商品");
                     }
                     catch(InterruptedException e)
                     {
                     }
              }
       }
}
 
class Consumer implements Runnable
{
       private Resource res;
       Consumer(Resourceres)
       {
              this.res = res;
       }
       public void run()
       {
              while(true)
              {
                     try
                     {
                            res.out();
                     }
                     catch(InterruptedException e)
                     {
                     }
              }
       }
}

5、停止线程

1.定义循环结束标记

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

2.使用interrupt(中断)方法。

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

注意:stop方法已经过时不再使用。

3.setDaemon :将该线程标记为守护线程或用户线程。

特点:该方法必须在启动线程钱调用;当正在运行的线程都是守护线程时,Java虚拟机退出。

4.stop方法已经过时,如何停止线程?

只有一种,run方法结束。开启多线程运行,运行代码通常是循环结构。

只要控制住循环体,就可以让run方法结束,也就是线程结束。

 

6、特殊情况:

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

当没有指定的方式让冻结的线程回复到运行状态时,这时就需要对冻结进行清除。强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束。

Thread类提供了该方法:interrupt();

代码演示:

class StopThread implements Runnable
{
       private boolean flag = true;
       public synchronized void run()
       {
              while(flag)
              {
                     try
                     {
                            wait();
                     }
                     catch(InterruptedException e)
                     {
                            System.out.println(Thread.currentThread().getName()+"....Exception");
                            flag = false;
                     }
                     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()+"...main...."+num);
              }
              System.out.println("over");
       }
}

7、多线程-守护线程

1、关键字: void Daemon(boolean on)     将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java虚拟机退出。且该方法必须在线程启动前调用

守护线程属于“后台线程”,随着前台线程的退出而退出。

 

2、join:等待该线程终止。

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

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

class Demo implements Runnable
{
       public void run()
       {
              for(int x=0; x<60; x++)
              {
                     System.out.println(Thread.currentThread().getName()+"...."+x);
              }
       }
}
class JoinDemo
{
       public static void main(String[] args) throws Exception
       {
              Demo d = new Demo();
              Thread t1 = new Thread(d);
              Thread t2 = new Thread(d);
 
              t1.start();
              t2.start();
              t1.join();//让t1申请线程,只有当t1线程执行完后,才会执行其余的。
              /*
              当t1.join();在t1.start();下面一行时,t1线程会首先执行完后,t2和主线程就会交替执行完;
              当t1.join();在t2.start();下面时,t1会与t2交替执行,直到t1线程执行完,没有执行完的t2线程就会与主线程交替执行。
              */
              for(int x=0;x<70;x++)
              {
                     System.out.println(Thread.currentThread().getName()+"....main");
              }
              System.out.println("over");
       }
}

8、多线程-优先级&yield方法

关键字:toString()、yield、setPriority

1.String toString()  返回该线程的字符串表现形式,包括线程名称、优先级和线程组。

2.static void yield() :暂停当前正在执行的线程对象,并执行其他线程。

3.void setPriority(int newPriority) :更改线程的优先级,其中,所有线程的默认优先级是5

小知识:数据固定的用常量(所有字母大写),数据共享有static静态,全局常量使用格式publicstatic final int MAX_PRIORITY

//真实开发中的多线程代码演示
class ThreadTest
{
       public static void main(String[] args)
       {
              new Thread()
              {
                     for(intx=0; x<100; x++)
                     {
                            System.out.println(Thread.currentThread().getName()+"...."+x);
                     }
              }.start();
 
              for(int x=0; x<100; x++)
              {
                     System.out.println(Thread.currentThread().getName()+"...."+x);
              }
 
              Runnable r = new Runnable()
              {
                     for(intx=0; x<100; x++)
                     {
                            System.out.println(Thread.currentThread().getName()+"...."+x);
                     }
              };
              new Thread(r).start();
       }
}

 小结:将函数进行一定的封装,达到三个函数能同时运行的目的。


---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------
详细请查看:http://edu.csdn.net
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值