多线程(4)

多线程中最为常见的应用案例

生产者消费者问题


生产和消费同时执行,需要多线程。
但是执行的任务却不相同,处理的资源确实相同的:线程间的通信

1,描述一下资源。
2,描述生产者,因为具备着自己的任务。
3,描述消费者,因为具备着自己的任务。

问题1:数据错误:已经被生产很早期的商品,才被消费到。
出现线程安全问题,加入了同步解决。使用同步函数。
问题已解决:不会在消费到之前很早期的商品。

//1,描述资源。属性:商品名称和编号,  行为:对商品名称赋值,获取商品。
class Resource
{
    private String name;
    private int count = 1;

    //1,提供设置的方法。
    public synchronized void set(String name)
    {
        //给成员变量赋值并加上编号。
        this.name = name + count;
        //编号自增。
        count++;
        //打印生产了哪个商品。
        System.out.println(Thread.currentThread().getName()+"......生产者...."+this.name);
    }
    public synchronized void out()
    {
        System.out.println(Thread.currentThread().getName()+"....消费者...."+this.name);
    }
}

//2,描述生产者。
class Producer implements Runnable
{
    private Resource r ;
    // 生产者一初始化就要有资源,需要将资源传递到构造函数中。
    Producer(Resource r)
    {
        this.r = r;
    }
    public void run()
    {
        while(true)
        {
            r.set("面包");
        }
    }
}

//3,描述消费者。
class Consumer implements Runnable
{
    private Resource r ;
    // 消费者一初始化就要有资源,需要将资源传递到构造函数中。
    Consumer(Resource r)
    {
        this.r = r;
    }
    public void run()
    {
        while(true)
        {
            r.out();
        }
    }
}




class ThreadDemo8
{
    public static void main(String[] args) 
    {
        //1,创建资源对象。
        Resource r = new Resource();

        //2,创建线程任务。
        Producer pro = new Producer(r);
        Consumer con = new Consumer(r);

        //3,创建线程。
        Thread t1 = new Thread(pro);
        Thread t2 = new Thread(con);

        t1.start();
        t2.start();
    }
}

问题2:发现了连续生产却没有消费,同时对同一个商品进行多次消费。
希望的结果应该是生产一个商品,就被消费掉。生产下一个商品。
搞清楚几个问题?
生产者什么时候生产呢?消费者什么时候应该消费呢?
当盘子中没有面包时,就生产,如果有了面包,就不要生产。
当盘子中已有面包时,就消费,如果没有面包,就不要消费。

生产者生产了商品后应该告诉消费者来消费。这时的生产者应该处于等待状态。
消费者消费了商品后,应该告诉生产者,这时消费者处于等待状态。

等待:wait();
告诉:notify();//唤醒

问题解决:实现生产一个消费一个

=====================
等待/唤醒机制

wait()

: 会让线程处于等待状态,其实就是将线程临时存储到了线程池中。

notify()

:会唤醒线程池中任意一个等待的线程。

notifyAll()

:会唤醒线程池中所有的等待线程。

记住:这些方法必须使用在同步中,因为必须要标识wait,notify等方法所属的锁。
同一个锁上的notify,只能唤醒该锁上的被wait的线程。

为什么这些方法定义在Object类中呢?
因为这些方法必须标识所属的锁,而锁可以是任意对象,任意对象可以调用的方法必然时Object类中的方法。

举例:小朋友抓人游戏。

//1,描述资源。属性:商品名称和编号,  行为:对商品名称赋值,获取商品。
class Resource
{
    private String name;
    private int count = 1;

    //定义标记。
    private boolean flag = false;

    //1,提供设置的方法。
    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);

        //将标记改为true。
        flag = true;
        //唤醒消费者。
        this.notify();
    }
    public synchronized void out()
    {
        if(!flag)
            try{this.wait();}catch(InterruptedException e){}
        System.out.println(Thread.currentThread().getName()+"....消费者...."+this.name);
        //将标记该为false。
        flag = false;
        //唤醒生产者。
        this.notify();
    }
}

//2,描述生产者。
class Producer implements Runnable
{
    private Resource r ;
    // 生产者一初始化就要有资源,需要将资源传递到构造函数中。
    Producer(Resource r)
    {
        this.r = r;
    }
    public void run()
    {
        while(true)
        {
            r.set("面包");
        }
    }
}

//3,描述消费者。
class Consumer implements Runnable
{
    private Resource r ;
    // 消费者一初始化就要有资源,需要将资源传递到构造函数中。
    Consumer(Resource r)
    {
        this.r = r;
    }
    public void run()
    {
        while(true)
        {
            r.out();
        }
    }
}




class ThreadDemo9
{
    public static void main(String[] args) 
    {
        //1,创建资源对象。
        Resource r = new Resource();

        //2,创建线程任务。
        Producer pro = new Producer(r);
        Consumer con = new Consumer(r);

        //3,创建线程。
        Thread t1 = new Thread(pro);
        Thread t2 = new Thread(con);

        t1.start();
        t2.start();
    }
}

多生产多消费


问题1;生产了商品没有被消费,同一个商品被消费多次。
Thread-0……生产者….面包2499//没有被消费。
Thread-1……生产者….面包2500
Thread-3….消费者….面包2500

被唤醒的线程没有判断标记,造成问题1的产生。
解决:只要让被唤醒的线程必须判断标记就可以了。将if判断标记的方式改为while判断标记。记住:多生产多消费,必须时while判断条件。

问题2:发现while判断后,死锁了。
原因:生产方唤醒了线程池中生产方的线程。本方唤醒了本方。
解决:希望本方要唤醒对方,没有对应的方法,所以只能唤醒所有。

其实还有一些遗憾的,效率低了。

class Resource
{
    private String name;
    private int count = 1;

    //定义标记。
    private boolean flag = false;

    //1,提供设置的方法。
    public synchronized void set(String name)//   
    {

        while(flag)
            try{this.wait();}catch(InterruptedException e){}// t1等  t2等
        //给成员变量赋值并加上编号。
        this.name = name + count;//商品1  商品2  商品3
        //编号自增。
        count++;//2 3  4
        //打印生产了哪个商品。
        System.out.println(Thread.currentThread().getName()+"......生产者...."+this.name);//生产 商品1  生产商品2  生产商品3

        //将标记改为true。
        flag = true;
        //唤醒消费者。
        this.notifyAll();
    }
    public synchronized void out()// 
    {
        while(!flag)
            try{this.wait();}catch(InterruptedException e){}//t3等  //t4等
        System.out.println(Thread.currentThread().getName()+"....消费者...."+this.name);//消费 商品1
        //将标记该为false。
        flag = false;
        //唤醒生产者。
        this.notifyAll();
    }
}

//2,描述生产者。
class Producer implements Runnable
{
    private Resource r ;
    // 生产者一初始化就要有资源,需要将资源传递到构造函数中。
    Producer(Resource r)
    {
        this.r = r;
    }
    public void run()
    {
        while(true)
        {
            r.set("面包");
        }
    }
}

//3,描述消费者。
class Consumer implements Runnable
{
    private Resource r ;
    // 消费者一初始化就要有资源,需要将资源传递到构造函数中。
    Consumer(Resource r)
    {
        this.r = r;
    }
    public void run()
    {
        while(true)
        {
            r.out();
        }
    }
}




class ThreadDemo10
{
    public static void main(String[] args) 
    {
        //1,创建资源对象。
        Resource r = new Resource();

        //2,创建线程任务。
        Producer pro = new Producer(r);
        Consumer con = new Consumer(r);

        //3,创建线程。
        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();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值