Java学习个人备忘录之线程间的通信

线程间通讯
多个线程在处理同一资源,但是任务却不同. 

class Resource  
{  
    String name;  
    String sex;  
}  
  
//输入  
class Input implements Runnable  
{  
    Resource r;  
    Input(Resource r)  
    {  
        this.r = r;  
    }  
    public void run()  
    {  
        int i = 0;  
        while(true)  
        {  
            synchronized(r)  //保证两个线程用同一个锁  
            {  
                if (i==0)  
                {  
                    r.name = "mike";  
                    r.sex = "nan";  
                }  
                else  
                {  
                    r.name = "丽丽";  
                    r.name = "女女女女女女女女女";  
                }  
                x = (x+1)%2;  
            }  
        }  
    }  
}  
  
//输出  
class Output implements Runnable  
{  
    Resource r;  
    Output(Resource r)  
    {  
        this.r = r;  
    }  
    public void run()  
    {  
        while(true)  
        {  
            synchronized(r)  //保证两个线程用同一个锁  
            {  
                System.out.println(r.name+"....."+r.sex);  
            }  
        }  
    }  
}  
  
class ResourceDemo  
{  
    public static void main(String[] args)  
    {  
        //创建资源  
        Resource r = new Resource();  
        //创建任务  
        Input in = new Input(r);  
        Output out = new Output(r);  
        //创建线程  
        Thread t1 = new Thread(in);  
        Thread t2 = new Thread(out);  
        //开启线程  
        t1.start();  
        t2.start();  
    }  
}

但是这样会造成大量的才重复, 没有交替性。

 

等待唤醒机制

涉及的方法:
1. wait(): 让线程处于冻结状态, 被wait的线程会被存储到线程池中.
2. notify(): 唤醒线程池中一个线程(任意)
3. notifyAll(): 唤醒线程池中的所有线程.


这些方法都必须定义在同步中,
因为这些方法都是用于操做线程状态的方法.
必须要明确到底操做的是哪个锁上的线程.


为什么操做线程的方法wait notify notifyAll定义在了Object类中.

因为这些方法时监视器的方法, 坚持其其实就是锁.
锁可以是任意的对象,任意的对象调用的方式一定定义在Object类中的. 

class Resource  
{  
    String name;  
    String sex;  
    boolean flag = false;  
}  
  
//输入  
class Input implements Runnable  
{  
    Resource r;  
    Input(Resource r)  
    {  
        this.r = r;  
    }  
    public void run()  
    {  
        int i = 0;  
        while(true)  
        {  
            synchronized(r)  //保证两个线程用同一个锁  
            {  
                if (r.flag)  
                {  
                    r.wait();  
                }  
                if (i==0)  
                {  
                    r.name = "mike";  
                    r.sex = "nan";  
                }  
                else  
                {  
                    r.name = "丽丽";  
                    r.name = "女女女女女女女女女";  
                }  
                r.flag = true;  
                r.notify();  //唤醒对方  
                x = (x+1)%2;  
            }  
        }  
    }  
}  
  
//输出  
class Output implements Runnable  
{  
    Resource r;  
    Output(Resource r)  
    {  
        this.r = r;  
    }  
    public void run()  
    {  
        while(true)  
        {  
            synchronized(r)  //保证两个线程用同一个锁  
            {  
                if (!r.flag)  
                {  
                    r.wait();  
                }  
                System.out.println(r.name+"....."+r.sex);  
                r.flag = false;  
                r.notify();  //唤醒对方  
            }  
        }  
    }  
}  
  
class ResourceDemo2  
{  
    public static void main(String[] args)  
    {  
        //创建资源  
        Resource r = new Resource();  
        //创建任务  
        Input in = new Input(r);  
        Output out = new Output(r);  
        //创建线程  
        Thread t1 = new Thread(in);  
        Thread t2 = new Thread(out);  
        //开启线程  
        t1.start();  
        t2.start();  
    }  
}

上面代码的优化

class Resource  
{  
    private String name;  //这里要私有化  
    private String sex;  
    boolean flag = false;  
    public synchronized void set(String name,String sex)  //对数据要可控化  
    {  
        if (this.flag)  
            try{this.wait();}catch(InterruptedException e){}  
        this.name = name;  
        this.sex = sex;  
        flag = true;  
        this.notify();  
    }  
    public synchronized void out()  
    {  
        if (this.flag)  
            try{this.wait();}catch(InterruptedException e){}  
        System.out.println(name+"....."+sex);  
        flag = false;  
        this.notify();  
    }  
}  
  
//输入  
class Input implements Runnable  
{  
    Resource r;  
    Input(Resource r)  
    {  
        this.r = r;  
    }  
    public void run()  
    {  
        int i = 0;  
        while(true)  
        {  
            if (i==0)  
            {  
                r.set("mike","nan");  
            }  
            else  
            {  
                r.set"丽丽","女女女女女女女女女");  
            }  
            x = (x+1)%2;  
        }  
    }  
}  
  
//输出  
class Output implements Runnable  
{  
    Resource r;  
    Output(Resource r)  
    {  
        this.r = r;  
    }  
    public void run()  
    {  
        while(true)  
        {  
            r.out();  
        }  
    }  
}  
  
class ResourceDemo3  
{  
    public static void main(String[] args)  
    {  
        //创建资源  
        Resource r = new Resource();  
        //创建任务  
        Input in = new Input(r);  
        Output out = new Output(r);  
        //创建线程  
        Thread t1 = new Thread(in);  
        Thread t2 = new Thread(out);  
        //开启线程  
        t1.start();  
        t2.start();  
    }  
}

 

多生产者多消费者问题

class Resource  
{  
    private String name;  
    private int count = 1;  
    private boolean flag = false;  
    public synchronized void set(String name)  
    {  
        if (flag)  
            try{this.wait();}catch(InterruptedException e){}  
        this.name = name + count;  
        count++;  
        System.out.println(Thread.currentThread().getName()+".....生产者....."+this.name);  
        flag = true;  
        notify();  
    }  
    public synchronized void out()  
    {  
        if (!flag)  
            try{this.wait();}catch(InterruptedException e){}  
        System.out.println(Thread.currentThread().getName()+".....消费者....."+this.name);  
        flag = false;  
        notify();  
    }  
}  
  
class Producer implements Runnable  
{  
    private Resource r;  
    Producer(Resource r)  
    {  
        this.r = r;  
    }  
    public void run()  
    {  
        while (true)  
        {  
            r.set("烤鸭");  
        }  
    }  
}  
  
class Consumer implements Runnable  
{  
    private Resource r;  
    Consumer(Resource r)  
    {  
        this.r = r;  
    }  
    public void run()  
    {  
        while(true)  
        {  
            r.out();  
        }  
    }  
}  
  
class ProducerConsumerDemo  
{  
    public static void main(String[] args)  
    {  
        Resource r = new Resource();  
        Producer pro = new Producer(r);  
        Consumer con = new Consumer(r);  
        Thread t0 = new Thread(pro);  
        Thread t1 = new Thread(pro);  
        Thread t2 = new Thread(con);  
        Thread t3 = new Thread(con);  
        t0.start();  
        t1.start();  
        t2.start();  
        t3.start();  
    }  
}

但是这样会出现安全隐患, 从这4个线程上看, 一共分了两组, t0和t1一组, t2和t3一组, 当t1 t2 t3 睡眠时, t0出来后再次唤醒t1, 这时t1是不用判断的if条件的,直接向下继续执行. 这样就又进行了"生产烤鸭", 所以出现了安全隐患. 解决办法: 将两个if 换成 while, 这样在t1醒来的时候会继续判断flag是否为真. 但是这样又会出现死锁现象, 因为t1判断flag时, flag为真, 这时t1会再次等待,这时4个线程都进入等待状态---死锁!!

 

解决办法1
将notify换成notifyAll, 这样就一定会唤醒对方的线程,同时自己方的线程因为while循环出不去. 

class Resource  
{  
    private String name;  
    private int count = 1;  
    private boolean flag = false;  
    public synchronized void set(String name)  
    {  
        while (flag)  
            try{this.wait();}catch(InterruptedException e){}  
        this.name = name + count;  
        count++;  
        System.out.println(Thread.currentThread().getName()+".....生产者....."+this.name);  
        flag = true;  
        notifyAll();  
    }  
    public synchronized void out()  
    {  
        while (!flag)  
            try{this.wait();}catch(InterruptedException e){}  
        System.out.println(Thread.currentThread().getName()+".....消费者....."+this.name);  
        flag = false;  
        notifyAll();  
    }  
}  
  
class Producer implements Runnable  
{  
    private Resource r;  
    Producer(Resource r)  
    {  
        this.r = r;  
    }  
    public void run()  
    {  
        while (true)  
        {  
            r.set("烤鸭");  
        }  
    }  
}  
  
class Consumer implements Runnable  
{  
    private Resource r;  
    Consumer(Resource r)  
    {  
        this.r = r;  
    }  
    public void run()  
    {  
        while(true)  
        {  
            r.out();  
        }  
    }  
}  
  
class ProducerConsumerDemo  
{  
    public static void main(String[] args)  
    {  
        Resource r = new Resource();  
        Producer pro = new Producer(r);  
        Consumer con = new Consumer(r);  
        Thread t0 = new Thread(pro);  
        Thread t1 = new Thread(pro);  
        Thread t2 = new Thread(con);  
        Thread t3 = new Thread(con);  
        t0.start();  
        t1.start();  
        t2.start();  
        t3.start();  
    }  
}

if判断标记只有一次, 会导致不该运行的线程运行了, 出现了数据错误的情况. while判断标记, 解决了线程获取执行权后, 是否要运行。
notify: 只能唤醒一个线程, 如果本方唤醒了本方, 就没有意义, 而且while判断标记notify会导致死锁. notifyAll解决了, 本方线程一定会唤醒对方线程. 

解决办法2:
JDK1.5新特征的解决办法--Lock

可以看出来, 上面的解决方法会造成多次无用的判断, 这会降低效率,可以用这面的方法解决. 

Lock l = new ReentrantLock(); 
void show() 
{ 
    l.lock(); //获取锁 
    code... 
    l.unlock(); //释放锁 
}

jdk1.5以后将同步和锁封装成了对象.
并将操作锁的隐式方法定义到了该对象中,
将隐式动作变成了显示动作.

但是如果执行的代码抛出了异常, 这样代码就会一直持有锁,不释放,所以要如下

Lock l = new ReentrantLock(); 
void show() 
{ 
    l.lock(); //获取锁 
    try 
    { 
        code... 
    } 
    finally 
    { 
        l.unlock(); //释放锁 
    } 
}

 

import java.util.concurrent.locks.*  
class Resource  
{  
    private String name;  
    private int count = 1;  
    private boolean flag = false;  
      
    Lock l = new ReentrantLock();//因为Lock是java.util.concurrent.locks包中的类, 所以要先导入包.  
  
    public void set(String name) //这里的同步就可以去掉了  
    {  
        l.lock();  //在这里加上锁  
        try  
        {  
            while (flag)  
                try{this.wait();}catch(InterruptedException e){}  
            this.name = name + count;  
            count++;  
            System.out.println(Thread.currentThread().getName()+".....生产者....."+this.name);  
            flag = true;  
            notifyAll();  
        }  
        finally  
        {  
            l.unlock();  
        }  
    }  
    public void out()  
    {  
        l.lock();  
        try  
        {  
            while (!flag)  
                try{this.wait();}catch(InterruptedException e){}  
            System.out.println(Thread.currentThread().getName()+".....消费者....."+this.name);  
            flag = false;  
            notifyAll();  
        }  
        finally  
        {  
            l.unlock();  
        }  
    }  
}  
  
class Producer implements Runnable  
{  
    private Resource r;  
    Producer(Resource r)  
    {  
        this.r = r;  
    }  
    public void run()  
    {  
        while (true)  
        {  
            r.set("烤鸭");  
        }  
    }  
}  
  
class Consumer implements Runnable  
{  
    private Resource r;  
    Consumer(Resource r)  
    {  
        this.r = r;  
    }  
    public void run()  
    {  
        while(true)  
        {  
            r.out();  
        }  
    }  
}  
  
class ProducerConsumerDemo  
{  
    public static void main(String[] args)  
    {  
        Resource r = new Resource();  
        Producer pro = new Producer(r);  
        Consumer con = new Consumer(r);  
        Thread t0 = new Thread(pro);  
        Thread t1 = new Thread(pro);  
        Thread t2 = new Thread(con);  
        Thread t3 = new Thread(con);  
        t0.start();  
        t1.start();  
        t2.start();  
        t3.start();  
    }  
}

解决办法3:
JDK1.5新特征的解决办法--Condition

Condition在底层上是这样实现的:
interface Condition
{
  await();
  signal();
  signalAll();
}

所以要这样实现, 如下:
Lock l = new ReectrantLock();

Condition c1 = l.newCondition();

Condition c2 = l.newCondition();

import java.util.concurrent.locks.*  
class Resource  
{  
    private String name;  
    private int count = 1;  
    private boolean flag = false;  
      
    //创建一个锁对象.  
    Lock l = new ReentrantLock();//因为Lock是java.util.concurrent.locks包中的类, 所以要先导入包.  
  
    //通过已有的锁获取该锁上的监视器对象.  
    Condition con = l.newCondition();  
  
    public void set(String name) //这里的同步就可以去掉了  
    {  
        l.lock();  //在这里加上锁  
        try  
        {  
            while (flag)  
//              try{this.wait();}catch(InterruptedException e){}   //这里用con.await()替换  
                try{con.await();}catch(InterruptedException e){}  
            this.name = name + count;  
            count++;  
            System.out.println(Thread.currentThread().getName()+".....生产者....."+this.name);  
            flag = true;  
//          notifyAll();  //这里用con.signalAll() 替换  
            con.signalAll();  
        }  
        finally  
        {  
            l.unlock();  
        }  
    }  
    public void out()  
    {  
        l.lock();  
        try  
        {  
            while (!flag)  
//              try{this.wait();}catch(InterruptedException e){}   //这里用con.await()替换  
                try{con.await();}catch(InterruptedException e){}  
            System.out.println(Thread.currentThread().getName()+".....消费者....."+this.name);  
            flag = false;  
//          notifyAll();  //这里用con.signalAll() 替换  
            con.signalAll();  
        }  
        finally  
        {  
            l.unlock();  
        }  
    }  
}  
  
class Producer implements Runnable  
{  
    private Resource r;  
    Producer(Resource r)  
    {  
        this.r = r;  
    }  
    public void run()  
    {  
        while (true)  
        {  
            r.set("烤鸭");  
        }  
    }  
}  
  
class Consumer implements Runnable  
{  
    private Resource r;  
    Consumer(Resource r)  
    {  
        this.r = r;  
    }  
    public void run()  
    {  
        while(true)  
        {  
            r.out();  
        }  
    }  
}  
  
class ProducerConsumerDemo2  
{  
    public static void main(String[] args)  
    {  
        Resource r = new Resource();  
        Producer pro = new Producer(r);  
        Consumer con = new Consumer(r);  
        Thread t0 = new Thread(pro);  
        Thread t1 = new Thread(pro);  
        Thread t2 = new Thread(con);  
        Thread t3 = new Thread(con);  
        t0.start();  
        t1.start();  
        t2.start();  
        t3.start();  
    }  
}

其实解决办法3和解决办法2没有太大的区别.并没有真的运用了1.5的新特征。

解决办法4
这个解决办法才真正的运用到了1.5的新特征。

import java.util.concurrent.locks.*  
class Resource  
{  
    private String name;  
    private int count = 1;  
    private boolean flag = false;  
      
    //创建一个锁对象.  
    Lock l = new ReentrantLock();//因为Lock是java.util.concurrent.locks包中的类, 所以要先导入包.  
  
    //通过已有的锁获取该锁上的监视器对象.  
//  Condition con = l.newCondition();  
  
    //通过已有的锁获取两组监视器, 一组监视生产者, 一组监视消费者.  
    Condition producer_con = l.newCondition();  
    Condition consumer_con = l.newCondition();  
  
    public void set(String name) //这里的同步就可以去掉了  
    {  
        l.lock();  //在这里加上锁  
        try  
        {  
            while (flag)  
                try{producer_con.await();}catch(InterruptedException e){} //这里只让生产者等待  
            this.name = name + count;  
            count++;  
            System.out.println(Thread.currentThread().getName()+".....生产者....."+this.name);  
            flag = true;  
            consumer_con.signal(); //这里直接唤醒消费者  
        }  
        finally  
        {  
            l.unlock();  
        }  
    }  
    public void out()  
    {  
        l.lock();  
        try  
        {  
            while (!flag)  
                try{consumer_con.await();}catch(InterruptedException e){} //这里只让消费者等待  
            System.out.println(Thread.currentThread().getName()+".....消费者....."+this.name);  
            flag = false;  
            producer_con.signalAll(); //这里只唤醒生产者  
        }  
        finally  
        {  
            l.unlock();  
        }  
    }  
}  
  
class Producer implements Runnable  
{  
    private Resource r;  
    Producer(Resource r)  
    {  
        this.r = r;  
    }  
    public void run()  
    {  
        while (true)  
        {  
            r.set("烤鸭");  
        }  
    }  
}  
  
class Consumer implements Runnable  
{  
    private Resource r;  
    Consumer(Resource r)  
    {  
        this.r = r;  
    }  
    public void run()  
    {  
        while(true)  
        {  
            r.out();  
        }  
    }  
}  
  
class ProducerConsumerDemo2  
{  
    public static void main(String[] args)  
    {  
        Resource r = new Resource();  
        Producer pro = new Producer(r);  
        Consumer con = new Consumer(r);  
        Thread t0 = new Thread(pro);  
        Thread t1 = new Thread(pro);  
        Thread t2 = new Thread(con);  
        Thread t3 = new Thread(con);  
        t0.start();  
        t1.start();  
        t2.start();  
        t3.start();  
    }  
}

 

posted @ 2017-09-04 17:50  vector121  阅读(115)  评论(0编辑  收藏  举报