-java多线程——生产者和消费者


1“线程的等待和唤醒”机制,实现两个线程,一个读取,一个写入的有序的情况!这里使用了while(true),无线死循环的情况,所以会一直不停的执行线程run()方法中的代码!

<span style="font-size:18px;"><span style="font-size:18px;">package com;

/**
 * 注意点:
 * <p/>
 * 线程的等待和唤醒,必须是使用同一个”锁“对象的不同的线程,才能实现相互间的”等待和唤醒“。
 * 必须使用”锁对象“.wait和”锁对象“.notify.显示指明,使用那个”锁对象“的线程”等待“或者”唤醒“.
 * <p/>
 * <p/>
 * User: OF895
 * Date: 2014/11/29
 * Time: 16:01
 */
public class Res {
    String name;
    String sex;
    boolean flag;
}

class Input implements Runnable {
    private Res r;

    public Input(Res r) {
        this.r = r;
    }

    @Override
    public void run() {
        int x = 0;
        while (true) {
            synchronized (r) {
                if (r.flag) {
                    try {
                        //如果flag为true的时候,也就是已经set了值的时候,那么就让这个“线程”wait下来
                        //程序在启动的时候,默认会有一个“线程池”,等待的线程都会存在于”线程池“中
                        r.wait(); //这里wait,必须指明是使用那个”锁“的线程
                    } catch (InterruptedException e) {
                        throw new RuntimeException("线程等待状态被打断了!");
                    }
                }
                if (x == 0) {
                    r.name = "mike";
                    r.sex = "man";
                } else {
                    r.name = "丽丽";
                    r.sex = "女";
                }

                x = (x + 1) % 2;
                //这个存放资源的类,在存放完资源后,需要将“标识置为true”,这样“取资源”的类,就可以去取了
                //这里flag为true后,当前“线程”如果还拥有CPU的执行权,那么会再次进入这个run执行“同步代码块”中的代码,此时flag为true了
                //那么当前的“线程”就“wait”了。
                r.flag = true;
                //在等待之前,将使用同一把“锁”对象的另一个线程,通知它启动!
                r.notify();
            }

        }
    }
}

class Output implements Runnable {
    private Res r;

    public Output(Res r) {
        this.r = r;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (r) {
                //当flag为false的时候,也就意味着Input尚未进行Set值的操作之前,让其wait
                if (!r.flag) {
                    try {
                        r.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(r.name + "....." + r.sex);
                //程序执行到这里的时候,说明Res中的name和sex已经被打印了,”资源“被取走了,那么我们就置一下标识
                r.flag = false;
                //唤醒  这里notify,必须指明是使用那个”锁“的线程
                r.notify();
            }

        }
    }
}


class Start {
    public static void main(String[] args) {
        Res r = new Res();
        Input in = new Input(r);
        Output output = new Output(r);

        //通过这样的操作就实现了,Input线程执行完,"通知"Output线程去执行,这样就实现了他们之间有序的执行动作!
        new Thread(in).start();
        new Thread(output).start();

    }
}
</span></span>



2在jdk1.5之前,我们程序员如何实现“生产者”和“消费者”的代码:

<span style="font-size:18px;">package com.ThreadDemo;

/**
 * User: OF895
 * Date: 2014/12/1
 * Time: 13:24
 */
public class ProducerConsumerDemo {
    public static void main(String[] args) {

        ResourceDemo res = new ResourceDemo();
        Producer producer = new Producer(res);
        Consumer consumer = new Consumer(res);

        Thread t1 = new Thread(producer);
        Thread t3 = new Thread(producer);
        Thread t2 = new Thread(consumer);
        Thread t4 = new Thread(consumer);

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

}


class ResourceDemo {
    private String name;
    private int count = 1;
    private boolean flag = false;

    public synchronized void set(String name) {
        //这里需要使用while循环判断这个flag,因为如果只使用if(flag),那么它只会判断一次
        //如果有多个“生产者”和多个“消费者”的情况下,那么就会出现“数据混乱”的情况,而不是生产一个,消费一个
        <strong><span style="color:#ff0000;">while</span> </strong>(flag) {
            try {
                this.wait();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

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

        System.out.println(Thread.currentThread().getName() + ".....生产者.." + this.name);
        flag = true;
        //这里需要使用notifyAll方法,将“线程池”中的所有的“生产者”和“消费者”的线程都唤醒,否则可能会出现如下情况:
        //“生产线程”t1唤醒了"生产线程"t2,t3.....,但是却没有唤醒"消费线程",那么很有可能就解不了“锁”,出现了,全部线程wati的状态
        //也就是所谓的“死锁”的情况
       <span style="color:#ff0000;"><strong> this.notifyAll();</strong></span>
    }

    public synchronized void out() {

       <span style="color:#ff0000;"> while </span>(!flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println(Thread.currentThread().getName() + ".....消费者.." + this.name);
        flag = false;
       <strong><span style="color:#ff0000;"> this.notifyAll();</span></strong>
    }
}

//生产者
class Producer implements Runnable {
    private ResourceDemo res;

    public Producer(ResourceDemo res) {
        this.res = res;
    }

    @Override
    public void run() {
        while (true) {
            res.set("商品");
        }

    }
}

//消费者
class Consumer implements Runnable {
    ResourceDemo res;

    public Consumer(ResourceDemo res) {
        this.res = res;
    }

    @Override
    public void run() {
        while (true) {
            res.out();
        }
    }
}</span>

3在jdk1.5之后,我们如何使用java.util.concurrent.locks.Lock对象替代synchronized关键字使用,以及它的新特性

<span style="font-size:18px;">    package com;

    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;

    /**
     * User: OF895
     * Date: 2014/12/1
     * Time: 13:24
     *
     *
     * 这个例子,才能达到有多个”生产者“和”消费者’线程的情况下
     * 我们实现生产一个,消费一个的现象的出现
     */
    public class ProducerConsumerDemo {
        public static void main(String[] args) {

            ResourceDemo res = new ResourceDemo();
            Producer producer = new Producer(res);
            Consumer consumer = new Consumer(res);

            Thread t1 = new Thread(producer);
            Thread t3 = new Thread(producer);
            Thread t2 = new Thread(consumer);
            Thread t4 = new Thread(consumer);

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

    }


    class ResourceDemo {
        private String name;
        private int count = 1;
        private boolean flag = false;
        //建立一个“锁”对象
        private Lock lock = new ReentrantLock();

        //通过lock对象的newCondition()方法我们可以获得一个“condition”实例
        //通过这个condition我们可以获得wait,notify和notifyAll()方法
        //Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。

        //所以我们使用这个lock对象这个新特性后,在“同一把锁”上面我们可以有多个condition对象
        //所以我们可以做到“区别”await()和signal()
        //这是jdk1.5之后的一个很重要的特性
       <span style="color:#ff0000;"><strong> private Condition condition_produce = lock.newCondition();
        private Condition condition_consummer = lock.newCondition();</strong></span>
        public synchronized void set(String name) {
            //这里的调用“锁”方法,就相当于原来的synchronized关键字,加上“锁”。
            lock.lock();
            try {
                //这里需要使用while循环判断这个flag,因为如果只使用if(flag),那么它只会判断一次
                //如果有多个“生产者”和多个“消费者”的情况下,那么就会出现“数据混乱”的情况,而不是生产一个,消费一个
                while (flag) {
                    /**
                     * 这里为了区分“生成者”和“消费者”线程的等待和唤醒
                     * 避免下面使用condition.SingAll()方法将所有的线程,比如这里面“生产者”await()的情况下
                     * 执行完“生产的动作”后,我们只需要signAal()"消费者"线程就可以了,避免使用singAll()方法
                     * 所以我们使用这个lock对象这个新特性后,在“同一把锁”上面我们可以有多个condition对象
                     * 所以我们可以做到“区别”await()和signal()
                     */
                   <span style="background-color: rgb(255, 0, 0);"> <strong>condition_produce.await();</strong></span>
                }
                this.name = name + "---" + count++;

                System.out.println(Thread.currentThread().getName() + ".....生产者.." + this.name);
                flag = true;
                //这里需要使用notifyAll方法,将“线程池”中的所有的“生产者”和“消费者”的线程都唤醒,否则可能会出现如下情况:
                //“生产线程”t1唤醒了"生产线程"t2,t3.....,但是却没有唤醒"消费线程",那么很有可能就解不了“锁”,出现了,全部线程wati的状态
                //也就是所谓的“死锁”的情况
                condition_consummer.signal();//这里就相当于原来的this.notify()方法,别单独封装成了对象和方法
            } catch (Exception e) {

            } finally {
                //最后无论如何都要执行释放锁的操作
                lock.unlock();
            }


        }

        public void out() {
            lock.lock();
            try {
                while (!flag) {
                    try {
                        /<span style="color:#ff0000;"><strong>/消费者线程等待
                        condition_consummer.wait();</strong></span>
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName() + ".....消费者.." + this.name);
                flag = false;
               <span style="color:#ff0000;"><strong> //执行完“消费”任务后,可以使用特定的condition对象,唤醒"生产者"线程,这样就避免使用signAll()方法,也唤醒了“消费者‘线程了
                condition_produce.signal();</strong></span>
            } catch (Exception e) {

            } finally {
                lock.unlock();
            }
        }
    }

    //生产者
    class Producer implements Runnable {
        private ResourceDemo res;

        public Producer(ResourceDemo res) {
            this.res = res;
        }

        @Override
        public void run() {
            while (true) {
                res.set("商品");
            }

        }
    }

    //消费者
    class Consumer implements Runnable {
        ResourceDemo res;

        public Consumer(ResourceDemo res) {
            this.res = res;
        }

        @Override
        public void run() {
            while (true) {
                res.out();
            }
        }
    }</span>

4线程的interrupt方法,可以让一个线程从“冻结"状态,重新获得执行权!解除冻结状态!


5线程的join方法:

<span style="font-size:18px;">package com.ThreadDemo;

/**
 * 演示一下线程的join方法的使用
 * User: OF895
 * Date: 2014/12/1
 * Time: 22:53
 */
public class JoinDemo {
    public static void main(String[] args) throws Exception {
        Demo d = new Demo();

        Thread t1 = new Thread();
        Thread t2 = new Thread();
        t1.start();
        t2.start();
        /**
         * 这个join方法的作用,当A线程执行到了B线程的join方法的时候,那么A线程会释放执行权,进入“冻结”状态
         * 等待B线程执行完,A线程才会再次获得执行权,进行执行!
         *
         * 就比如这里因为t1.join()方法是在main(主线程)中“加入的”,所以这个代码执行的情况是:
         * 当主线程遇到t1.join的时候,主线程进入冻结状态,因为前面t1.start,t2.start,开启了两个线程,所以此时有两个线程在执行状态
         * 那么这两个线程交替执行,当t1执行完了的时候,主线程会再次获得执行权(主线程是不管t2线程是否执行完的),如果此时t2线程还没有执行结束
         * 那么就会出现main线程和t2线程交替执行的状况!
         *
         *
         * 所以join的作用:
         * 就是可以在一个线程中,临时加入一个线程,执行线程中的代码!
         *
         *
         */
       <span style="color:#ff0000;"> t1.join();</span>
        for (int x = 0; x < 80; x++) {
            System.out.println(Thread.currentThread().getName() + ".............." + x);
        }
        System.out.println("over!");
    }
}


class Demo implements Runnable {
    @Override
    public void run() {

        for (int x = 0; x <= 70; x++) {
            System.out.println(Thread.currentThread().getName() + ".............." + x);
        }

    }
}
</span>

java线程池的概念:

<span style="font-size:18px;">package com.Thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * User: OF895
 * Date: 14-9-18
 * Time: 下午10:44
 */
public class ThreadPoolTest {
    public static void main(String[] args) {
        //创建一个可以动态添加“线程”的线程池
        //ExecutorService threadPool = Executors.newCachedThreadPool();
        //创建一个只有一个线程的线程池(这个也相当于单线程了,但是这个有个好处就是,每当这个线程“死掉”的时候,会动态生成一个新的线程,保证线程池中有一个线程)
        //ExecutorService threadPool = Executors.newSingleThreadExecutor();
        //创建一个有固定大小的“线程池”
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        //创建任务(这里的每一个Runnable,中的run()方法的代码块,就是要执行的任务),相当于提交10次任务给“线程池”去做处理。
        for (int i = 1; i <= 10; i++) {
            final int task = i;
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                   for(int j = 1; j <= 10; j++){
                       //循环10次,打印出当前线程的名字
                       System.out.println("线程" + Thread.currentThread().getName() + ",第" + j + "次循环。第" + task + "次任务中!");

                   }
                }
            });

        }
        System.out.println("10个任务都被提交了!");
        //threadPool.shutdown(); 这个可以结束“线程池”中的线程。
    }

}
</span>

带有“定时任务”的线程池:

<span style="font-size:18px;">package com.Thread;

import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * User: OF895
 * Date: 14-9-19
 * Time: 上午12:17
 */
public class ScheduleThreadPool {
    public static void main(String[] args) {

        //带有“定时任务”的线程池(默认创建3个线程),5秒后执行,每3秒执行一次定时任务。
        Executors.newScheduledThreadPool(3).scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + ":booming!");
            }
        },5,3, TimeUnit.SECONDS);
    }
}
</span>

一个小例子:

<span style="font-size:18px;">package com.Thread;

/**
 * 子线程循环10次,主线程循环100次。然后子线程循环10次,然后主线程循环100次。如此循环50次
 * 这里需要采用“面向对象”的思维来分析代码。
 * User: OF895
 * Date: 14-9-15
 * Time: 下午11:33
 */
public class TraditionalSynchronizedCommunication {

    public static void main(String[] args) {
        final Business business = new Business();

        new Thread(new Runnable() {
            @Override
            public void run() {
               for(int i = 1; i < 50; i++){
                   //这里开启一个额外的线程调用子线程50次
                   business.sub(i);
                }
            }
        }

        ).start();

       //这里main方法也是一个线程,调用主线程50次,下面的synchronized关键字,确保了代码的互排斥
        for(int i = 1 ; i < 50 ; i++){
            business.main(i);
        }

    }


}


class Business {

    public synchronized void sub(int i) {

        for (int j = 1; j <= 10; j++) {
            System.out.println("sub sequence :" + j + ", loop of" + i);
        }
    }

    public synchronized void main(int i) {
        for (int j = 1; j <= 100; j++) {
            System.out.println("main sequence :" + j + ", loop of" + i);
        }
    }


}
</span>




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值