精华内容
下载资源
问答
  • 这个问题用Object的wait(),notify()就可以很方便的解决。 代码如下: public class QueueThread implements Runnable{ private Object current; private Object next; private int max=100; ...

    总结一个关于多线程顺序执行的万能方法。

    之前也参考了http://blog.csdn.net/zyplus的博客,觉得写得挺好,但是运行时或多或少又存在着一些小小的问题,算作为对于之前作者文章的一个补充吧!

    例题描述:建立三个线程,A线程打印100次A,B线程打印100次B,C线程打印100次C,要求线程同时运行,交替打印100次ABC。这个问题用Object的wait(),notify()就可以很方便的解决。

    代码如下:

    public class QueueThread implements Runnable{
    	
    	private Object current;
    	private Object next;
    	private int max=100;
    	private String word;
    		
    	public QueueThread(Object current, Object next, String word) {
    		this.current = current;
    		this.next = next;
    		this.word = word;
    	}
    
    	@Override
    	public void run() {
    		// TODO Auto-generated method stub
    		for(int i=0;i<max;i++){
    			synchronized (current) {
    				synchronized (next) {
    					System.out.println(word);<span id="transmark"></span>
    					next.notify();
    				}
    				try {
    					current.wait();
    				} catch (InterruptedException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    			}
    		}
    		//必须做一下这样处理,否则thread1-thread4停不了
    		synchronized (next) {
    			next.notify();
    			System.out.println(Thread.currentThread().getName()+"执行完毕");
    		}
    		
    	}
    	
    	public static void main(String[] args) throws InterruptedException {
    		long startTime = System.currentTimeMillis();
    		Object a = new Object();
    		Object b = new Object();
    		Object c = new Object();
    		Object d = new Object();
    		Object e = new Object();
                    //之所以每次当前线程都要sleep(10)是为了保证线程的执行顺序
    		new Thread(new QueueThread(a,b,"a")).start();
    		Thread.sleep(10);
    		new Thread(new QueueThread(b,c,"b")).start(); 
    		Thread.sleep(10);
    		new Thread(new QueueThread(c,d,"c")).start(); 
    		Thread.sleep(10);
    		new Thread(new QueueThread(d,e,"d")).start();
    		Thread.sleep(10);
    		Thread thread4 = new Thread(new QueueThread(e,a,"e")); 
    		thread4.start();
    		thread4.join();//因为线程0-4停止是依次执行的,所以如果保证主线程在线程4后停止,那么就能保证主线程是最后关闭的
    		System.out.println("程序耗时:"+ (System.currentTimeMillis()-startTime ));
    		
    	}
    }
    
    
    

    文章的末尾加上一些个人对于此程序的理解:

    其实这个程序很容易理解,首先,我们保证了线程0-线程4依次启动,并设置了Thread.sleep(10),保证线程0-4依次执行他们的run方法。

    其次,我们看QueueThread的run()便可知:1.线程获得current锁,2.获得next锁。3.打印并notify拥有next锁的一个对象4.线程执行current.wait(),释放current锁对象,并使线程处于阻塞状态。

    然后,假设已经执行到了thread-4的run方法,那么此时的情况是这样的:

    线程0处于阻塞状态,需要a.notify()才能使其回到runnale状态

    线程1处于阻塞状态,需要b.notify()才能使其回到runnale状态

    线程2处于阻塞状态,需要c.notify()才能使其回到runnale状态

    线程3处于阻塞状态,需要d.notify()才能使其回到runnale状态

    而线程4恰好可以需要执行a.notify(),所以能够使线程0回到runnale状态。然后执行e,wait()方法,使自身线程阻塞,需要e.notify()才能唤醒。

    依次执行下去,就可以发现规律了!

    最后之所以要在for循环后加上一个处理,是因为,如果不进行处理,除了线程0能够结束for循环,其余线程1-4实际上是会停在current,wait()这句代码的。

    假设已经执行到最后一次循环了,此时线程4唤醒线程0,并将自身阻塞。线程0被唤醒后,继续执行,然而因为i=max的缘故,他无法再进入循环了。然而如果循环后没有唤醒下一个线程的操作的话,那么剩下的线程1-4就会一直处于阻塞状态!也就不会停止了。但是加了处理之后就完美解决了。

    这就是个人的一些理解吧,希望对大家有帮助。新人第一次发博客,希望以后坚持下去,加油!



    展开全文
  • notify和wait

    万次阅读 2017-04-12 17:31:32
    // 当使用wait notify 的时候 , 一定要配合着synchronized关键字去使用 final Object lock = new Object(); Thread t1 = new Thread( new Runnable() { @Override public void run () { try...
    public class ListAdd2 {
        private volatile static List list = new ArrayList();    
    
        public void add(){
            list.add("bjsxt");
        }
        public int size(){
            return list.size();
        }
    
        public static void main(String[] args) {
    
            final ListAdd2 list2 = new ListAdd2();
    
            // 1 实例化出来一个 lock
            // 当使用wait 和 notify 的时候 , 一定要配合着synchronized关键字去使用
            final Object lock = new Object();
    
            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        synchronized (lock) {
                            for(int i = 0; i <10; i++){
                                list2.add();
                                System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素..");
                                Thread.sleep(500);
                                if(list2.size() == 5){
                                    System.out.println("已经发出通知..");
                                    lock.notify();
                                }
                            }                       
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
    
                }
            }, "t1");
    
            Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (lock) {
                        if(list2.size() != 5){
                            try {
                                System.out.println("t2进入...");
                                lock.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        System.out.println("当前线程:" + Thread.currentThread().getName() + "收到通知线程停止..");
                    }
                }
            }, "t2");   
    
            t2.start();
            t1.start();
    
        }
    
    }

    输出结果

    t2进入...
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    已经发出通知..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t2收到通知线程停止..

    可以看出来wait方法会让出锁的使用权,而notify不会让出来
    现在引入了CountDownLatch方法

    CountDownLatch是通过“共享锁”实现的。在创建CountDownLatch中时,会传递一个int类型参数count,该参数是“锁计数器”的初始状态,表示该“共享锁”最多能被count线程同时获取。当某线程调用该CountDownLatch对象的await()方法时,该线程会等待“共享锁”可用时,才能获取“共享锁”进而继续运行。而“共享锁”可用的条件,就是“锁计数器”的值为0!而“锁计数器”的初始值为count,每当一个线程调用该CountDownLatch对象的countDown()方法时,才将“锁计数器”-1;通过这种方式,必须有count个线程调用countDown()之后,“锁计数器”才为0,而前面提到的等待线程才能继续运行!

    CountDownLatch的两个主要方法

    // 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。
    void await()

    // 递减锁存器的计数,如果计数到达零,则释放所有等待的线程。
    void countDown()

    package heihei;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.CountDownLatch;
    /**
     * wait notfiy 方法,wait释放锁,notfiy不释放锁
     * @author alienware
     *
     */
    public class ListAdd2 {
        private volatile static List list = new ArrayList();    
    
        public void add(){
            list.add("bjsxt");
        }
        public int size(){
            return list.size();
        }
    
        public static void main(String[] args) {
    
            final ListAdd2 list2 = new ListAdd2();
    
            final CountDownLatch countDownLatch = new CountDownLatch(1);
    
            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        for(int i = 0; i <10; i++){
                                list2.add();
                                System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素..");
                                Thread.sleep(500);
                                if(list2.size() == 5){
                                    System.out.println("已经发出通知..");
                                    countDownLatch.countDown();
    
                                }
                            }                       
    
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
    
                }
            }, "t1");
    
            Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    //synchronized (lock) {
                        if(list2.size() != 5){
                            try {
    
                                countDownLatch.await();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        System.out.println("当前线程:" + Thread.currentThread().getName() + "收到通知线程停止..");
                        throw new RuntimeException();
    
                }
            }, "t2");   
    
            t2.start();
            t1.start();
    
        }
    
    }
    

    这样做的话输出结果是

    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    已经发出通知..
    当前线程:t1添加了一个元素..
    当前线程:t2收到通知线程停止..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..
    当前线程:t1添加了一个元素..

    顺序正确

    展开全文
  • 3(深入理解WaitNotify和Wait与sleep区别).rar
  • 生产者消费者问题(notify和wait)

    千次阅读 2018-03-23 14:51:22
    使用notify和wait机制实现生产者消费者 设计: 产品: Product类 产品库: ProductRepository类 生产者线程 消费者线程 Product.java: public class Product { private String name; private long ...

    使用notify和wait机制实现生产者和消费者

    设计:

    • 产品: Product类
    • 产品库: ProductRepository类
    • 生产者线程
    • 消费者线程

    Product.java:

    public class Product {
    
        private String name;
        private long productNo;
    
        public Product(String name, long productNo) {
            this.name = name;
            this.productNo = productNo;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public long getProductNo() {
            return productNo;
        }
    
        public void setProductNo(long productNo) {
            this.productNo = productNo;
        }
    
        @Override
        public String toString() {
            return "Product{" +
                    "name='" + name + '\'' +
                    ", productNo=" + productNo +
                    '}';
        }
    }

    ProductRepository.java:

    public class ProductRepository {
    
        private Vector<Product> products = new Vector<>();
        private static final int MAX_NUM = 10;
    
        public void produce(){
            synchronized (products){
                if (products.size() == MAX_NUM){ //如果仓库已经满了
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
    
                //products.size() < MAX_NUM 如果仓库还没有满
                Product product = new Product("包子", System.currentTimeMillis());
                products.add(product);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+ " 生产了: "+ product+" ,产品仓库当前数量: "+ products.size());
                //通知等待的消费者来消费
                notify();
            }
        }
    
    
        public void consume(){
            synchronized (products){
                if (products.size() == 0){ //产品仓库空了,等待生产者生产
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                Product product = products.firstElement();
                products.remove(0);
                System.out.println(Thread.currentThread().getName()+ " 消费了: " + product+" ,产品仓库当前数量: "+ products.size());
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //通知生产者生产
                notify();
            }
        }
    
    }
    

    Consumer.java:

    public class Consumer implements Runnable {
    
        private ProductRepository repository;
    
        public Consumer(ProductRepository repository) {
            this.repository = repository;
        }
    
        @Override
        public void run() {
            while (true){
                repository.consume();
            }
        }
    }

    Producer.java:

    public class Producer implements Runnable {
    
        private ProductRepository repository;
    
        public Producer(ProductRepository repository) {
            this.repository = repository;
        }
    
        @Override
        public void run() {
            while (true){
                repository.produce();
            }
        }
    }

    一个生产者,一个消费者

    测试类:

    public class ProductorConsumerTest {
    
    
        /**
         * 一个生产者,一个消费者
         * @throws InterruptedException
         */
        public static void oneProducerAndOneConsumer() throws InterruptedException {
            ProductRepository repository = new ProductRepository();
            Consumer consumer = new Consumer(repository);
            Producer producer = new Producer(repository);
            Thread t1 = new Thread(producer,"producer-A");
            Thread t2 = new Thread(consumer,"consumer-B");
            t1.start();
            t2.start();
            t1.join();
            t2.join(); //join(): 主线程等待t1线程和禾t2线程都执行完
        }
        public static void main(String[] args) throws InterruptedException {
               oneProducerAndOneConsumer();
        }
    }
    

    结果:

    Exception in thread "producer-A" java.lang.IllegalMonitorStateException
        at java.lang.Object.notify(Native Method)
        at com.gml.pinlor.thread.pcproblem.ProductRepository.produce(ProductRepository.java:33)
        at com.gml.pinlor.thread.pcproblem.Producer.run(Producer.java:17)
        at java.lang.Thread.run(Thread.java:745)
    producer-A 生产了: Product{name='包子', productNo=1521786358450} ,产品仓库当前数量: 1
    consumer-B 消费了: Product{name='包子', productNo=1521786358450} ,产品仓库当前数量: 0

    java.lang.IllegalMonitorStateException是什么鬼?在API中是这么描述的:

    Thrown to indicate that a thread has attempted to wait on an object’s monitor or to notify other threads waiting on an object’s monitor without owning the specified monitor(一个线程尝试等待一个对象的监视器,或者去通知其他正在等待对象监视器线程,却没有指明一个具体的监视器,才会抛出这个异常)

    原来是将synchronized监视到products对象上了,但调用waitnotify方法却不是products对象,将ProductRepository.java换成:

    public class ProductRepository {
    
        private Vector<Product> products = new Vector<>();
        private static final int MAX_NUM = 10;
    
        public void produce(){
            synchronized (products){
                if (products.size() == MAX_NUM){ //如果仓库已经满了
                    try {
                        products.wait();      //让products等待!!!
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
    
                //products.size() < MAX_NUM 如果仓库还没有满
                Product product = new Product("包子", System.currentTimeMillis());
                products.add(product);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+ " 生产了: "+ product+" ,产品仓库当前数量: "+ products.size());
                //通知等待的消费者来消费
                products.notify();
    //            products.notifyAll();
            }
        }
    
    
        public void consume(){
            synchronized (products){
                if (products.size() == 0){ //产品仓库空了,等待生产者生产
                    try {
                        products.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                Product product = products.firstElement();
                products.remove(0);
                System.out.println(Thread.currentThread().getName()+ " 消费了: " + product+" ,产品仓库当前数量: "+ products.size());
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //通知生产者生产
                products.notify();
    //            products.notifyAll();
            }
        }
    
    }

    结果:

    producer-A 生产了: Product{name='包子', productNo=1521786975941} ,产品仓库当前数量: 1
    consumer-B 消费了: Product{name='包子', productNo=1521786975941} ,产品仓库当前数量: 0
    producer-A 生产了: Product{name='包子', productNo=1521786978944} ,产品仓库当前数量: 1
    producer-A 生产了: Product{name='包子', productNo=1521786979944} ,产品仓库当前数量: 2
    producer-A 生产了: Product{name='包子', productNo=1521786980944} ,产品仓库当前数量: 3
    producer-A 生产了: Product{name='包子', productNo=1521786981945} ,产品仓库当前数量: 4
    producer-A 生产了: Product{name='包子', productNo=1521786982945} ,产品仓库当前数量: 5
    consumer-B 消费了: Product{name='包子', productNo=1521786978944} ,产品仓库当前数量: 4
    consumer-B 消费了: Product{name='包子', productNo=1521786979944} ,产品仓库当前数量: 3

    一个生产者,多个消费者

    只需要在ProductRepository中将produce方法中products.notify()方法换成products.notifyAll()方法即可

    public class ProductRepository {
    
        private Vector<Product> products = new Vector<>();
        private static final int MAX_NUM = 10;
    
        public void produce(){
            synchronized (products){
                if (products.size() == MAX_NUM){ //如果仓库已经满了
                    try {
                        products.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
    
                //products.size() < MAX_NUM 如果仓库还没有满
                Product product = new Product("包子", System.currentTimeMillis());
                products.add(product);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+ " 生产了: "+ product+" ,产品仓库当前数量: "+ products.size());
                //通知等待的消费者来消费
    //            products.notify();
                products.notifyAll();
            }
        }
    
    
        public void consume(){
            synchronized (products){
                if (products.size() == 0){ //产品仓库空了,等待生产者生产
                    try {
                        products.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                Product product = products.firstElement();
                products.remove(0);
                System.out.println(Thread.currentThread().getName()+ " 消费了: " + product+" ,产品仓库当前数量: "+ products.size());
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //通知生产者生产
                products.notify();
    //            products.notifyAll();
            }
        }
    }
    

    测试:

    public class ProductorConsumerTest {
    
        /**
         * 一个生产者,多个消费者
         * @throws InterruptedException
         */
        public static void oneProducerAndMoreConsumer() throws InterruptedException {
            ProductRepository repository = new ProductRepository();
            Consumer consumer = new Consumer(repository);
            Consumer consumer2 = new Consumer(repository);
            Producer producer = new Producer(repository);
            Thread t1 = new Thread(producer,"producer-A");
            Thread t2 = new Thread(consumer,"consumer-B");
            Thread t3 = new Thread(consumer,"consumer-C");
            t1.start();
            t2.start();
            t3.start();
            t1.join();
            t2.join();
            t3.join();
        }
        public static void main(String[] args) throws InterruptedException {
     //        oneProducerAndOneConsumer();
             oneProducerAndMoreConsumer();
     //        moreProducerAndMoreConsumer();
         }
    }
    

    结果:

    producer-A 生产了: Product{name='包子', productNo=1521787226868} ,产品仓库当前数量: 1
    producer-A 生产了: Product{name='包子', productNo=1521787227868} ,产品仓库当前数量: 2
    producer-A 生产了: Product{name='包子', productNo=1521787228868} ,产品仓库当前数量: 3
    producer-A 生产了: Product{name='包子', productNo=1521787229868} ,产品仓库当前数量: 4
    producer-A 生产了: Product{name='包子', productNo=1521787230868} ,产品仓库当前数量: 5
    producer-A 生产了: Product{name='包子', productNo=1521787231868} ,产品仓库当前数量: 6
    producer-A 生产了: Product{name='包子', productNo=1521787232868} ,产品仓库当前数量: 7
    producer-A 生产了: Product{name='包子', productNo=1521787233868} ,产品仓库当前数量: 8
    producer-A 生产了: Product{name='包子', productNo=1521787234868} ,产品仓库当前数量: 9
    producer-A 生产了: Product{name='包子', productNo=1521787235868} ,产品仓库当前数量: 10
    consumer-C 消费了: Product{name='包子', productNo=1521787226868} ,产品仓库当前数量: 9
    consumer-C 消费了: Product{name='包子', productNo=1521787227868} ,产品仓库当前数量: 8
    consumer-B 消费了: Product{name='包子', productNo=1521787228868} ,产品仓库当前数量: 7
    consumer-C 消费了: Product{name='包子', productNo=1521787229868} ,产品仓库当前数量: 6
    consumer-C 消费了: Product{name='包子', productNo=1521787230868} ,产品仓库当前数量: 5
    producer-A 生产了: Product{name='包子', productNo=1521787246869} ,产品仓库当前数量: 6

    多个生产者,多个消费者

    同样只需要修改ProductRepository中的consumer方法中的products.notity()products.notifyAll(),通知所有的生产者

    public class ProductRepository {
    
        private Vector<Product> products = new Vector<>();
        private static final int MAX_NUM = 10;
    
        public void produce(){
            synchronized (products){
                if (products.size() == MAX_NUM){ //如果仓库已经满了
                    try {
                        products.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
    
                //products.size() < MAX_NUM 如果仓库还没有满
                Product product = new Product("包子", System.currentTimeMillis());
                products.add(product);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+ " 生产了: "+ product+" ,产品仓库当前数量: "+ products.size());
                //通知等待的消费者来消费
    //            products.notify();
                products.notifyAll();
            }
        }
    
    
        public void consume(){
            synchronized (products){
                if (products.size() == 0){ //产品仓库空了,等待生产者生产
                    try {
                        products.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                Product product = products.firstElement();
                products.remove(0);
                System.out.println(Thread.currentThread().getName()+ " 消费了: " + product+" ,产品仓库当前数量: "+ products.size());
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //通知生产者生产
    //            products.notify();
                products.notifyAll();
            }
        }
    
    }

    测试:

    public class ProductorConsumerTest {
    
        /**
         * 多个生产者,多个消费者
         * @throws InterruptedException
         */
        public static void moreProducerAndMoreConsumer() throws InterruptedException {
            ProductRepository repository = new ProductRepository();
            Consumer consumer = new Consumer(repository);
            Consumer consumer1 = new Consumer(repository);
            Producer producer = new Producer(repository);
            Producer producer1 = new Producer(repository);
            Thread t1 = new Thread(producer,"producer-A");
            Thread t2 = new Thread(consumer,"consumer-B");
            Thread t3 = new Thread(consumer1,"consumer-C");
            Thread t4 = new Thread(producer1,"producer-D");
            t1.start();
            t4.start();
            t2.start();
            t3.start();
    
            t1.join();
            t4.join();
            t2.join();
            t3.join();
        }
    
        public static void main(String[] args) throws InterruptedException {
           // oneProducerAndOneConsumer();
            // oneProducerAndMoreConsumer();
           moreProducerAndMoreConsumer();
        }
    }
    

    结果:

    producer-A 生产了: Product{name='包子', productNo=1521787607751} ,产品仓库当前数量: 1
    producer-A 生产了: Product{name='包子', productNo=1521787608752} ,产品仓库当前数量: 2
    producer-A 生产了: Product{name='包子', productNo=1521787609752} ,产品仓库当前数量: 3
    producer-A 生产了: Product{name='包子', productNo=1521787610752} ,产品仓库当前数量: 4
    producer-A 生产了: Product{name='包子', productNo=1521787611753} ,产品仓库当前数量: 5
    producer-A 生产了: Product{name='包子', productNo=1521787612753} ,产品仓库当前数量: 6
    producer-A 生产了: Product{name='包子', productNo=1521787613753} ,产品仓库当前数量: 7
    producer-A 生产了: Product{name='包子', productNo=1521787614753} ,产品仓库当前数量: 8
    producer-D 生产了: Product{name='包子', productNo=1521787615753} ,产品仓库当前数量: 9
    producer-D 生产了: Product{name='包子', productNo=1521787616753} ,产品仓库当前数量: 10
    consumer-C 消费了: Product{name='包子', productNo=1521787607751} ,产品仓库当前数量: 9
    consumer-B 消费了: Product{name='包子', productNo=1521787608752} ,产品仓库当前数量: 8
    consumer-B 消费了: Product{name='包子', productNo=1521787609752} ,产品仓库当前数量: 7
    consumer-B 消费了: Product{name='包子', productNo=1521787610752} ,产品仓库当前数量: 6
    consumer-C 消费了: Product{name='包子', productNo=1521787611753} ,产品仓库当前数量: 5
    consumer-C 消费了: Product{name='包子', productNo=1521787612753} ,产品仓库当前数量: 4
    consumer-C 消费了: Product{name='包子', productNo=1521787613753} ,产品仓库当前数量: 3
    展开全文
  • waitnotify和notifyAll wait和notify(notifyAll)一般是成对搭配出现的,用来资源调控。wait用来将当然线程挂起,notify/notifyAll用来恢复线程。它是类Object的方法,也就是所有的对象都可以使用。一个简单的例子...

    wait、notify和notifyAll

    wait和notify(notifyAll)一般是成对搭配出现的,用来资源调控。wait用来将当前线程挂起,notify/notifyAll用来恢复线程。它是类Object的方法,也就是所有的对象都可以使用。一个简单的例子

    public class WaitClassDemo {
    
        private static SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
    
        public static void main(String[] args) {
            Object obj = new Object();
            new AThread(obj).start();
            new BThread(obj).start();
        }
    
        static class AThread extends Thread {
    
            Object obj;
    
            public AThread(Object obj) {
                setName("AThread");
                this.obj = obj;
            }
    
            @Override
            public void run() {
                synchronized (obj) {
                    System.out.println(sdf.format(new Date()) + " AThread before wait()");
                    try {
                        obj.wait(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(sdf.format(new Date()) + " AThread after wait()");
                }
            }
        }
    
        static class BThread extends Thread {
    
            Object obj;
    
            public BThread(Object obj) {
                setName("BThread");
                this.obj = obj;
            }
    
            @Override
            public void run() {
                synchronized (obj) {
                    System.out.println(sdf.format(new Date()) + " BThread before notify()");
                    obj.notify();
                    try {
                        Thread.sleep(5000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(sdf.format(new Date()) + " BThread after notify()");
                }
            }
        }
    }
    //打印
    //14:22:34 AThread before wait()
    //14:22:34 BThread before notify()
    //14:22:39 BThread after notify()
    //14:22:39 AThread after wait()

    1、wait/notify是需要需要获取对象锁的,也就是需要写在同步代码块或同步方法内部,可以理解为用synchronize包裹的。如果不用,编译会通过,但运行时会抛出java.lang.IllegalMonitorStateException。

    2、wait/notify是针对某个对象,是类Object的方法,并且注意要保证synchronize、waite和notify3者都是针对同一个具体对象。比如上面的synchronize锁的是obj这个对象,wait和notify也是由的obj对象。

    3、上面这个中wait()执行后该线程就处于阻塞阶段,并且把当前的锁给释放了。BThread得以继续。notify调用后,会立即轮转到wait()方法那吗?答案是不会,上面的例子显示,notify()需要把这个代码块的Thread.sleep(5000L)执行完,退出代码块后才轮转到wait()方法那。也很合理,毕竟同一时间里,只有一个线程能拿到锁执行synchronize包裹的代码里。

    wait方法也有带参数版的,wait(long timeout)和wait(long timeout, int nanos),后者看了下源码,只是判断如果如果nanos>0,让timeout++。看来虚拟机时间还是没精确到纳秒的地步。

    带参数的wait方法意思是等过了timeout毫秒后,就会尝试获得该锁。

    但是如果此时锁在别的线程那里,wait(1000)处于的AThread是不能往下执行,下面例子中如果把BThread的注释打开,就是要等BThread走出synchronize块后才可以。

    public class WaitClassDemo {
    
        private static SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
    
        public static void main(String[] args) {
            Object obj = new Object();
            new AThread(obj).start();
            //new BThread(obj).start();
        }
    
        static class AThread extends Thread {
    
            Object obj;
    
            public AThread(Object obj) {
                setName("AThread");
                this.obj = obj;
            }
    
            @Override
            public void run() {
                synchronized (obj) {
                    System.out.println(sdf.format(new Date()) + " AThread before wait()");
                    try {
                        obj.wait(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(sdf.format(new Date()) + " AThread after wait()");
                }
            }
        }
    
        static class BThread extends Thread {
    
            Object obj;
    
            public BThread(Object obj) {
                setName("BThread");
                this.obj = obj;
            }
    
            @Override
            public void run() {
                synchronized (obj) {
                    System.out.println(sdf.format(new Date()) + " BThread before");
                    try {
                        Thread.sleep(5000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(sdf.format(new Date()) + " BThread after");
                }
            }
        }
    }
    //注释BThread
    //19:03:43 AThread before wait()
    //19:03:44 AThread after wait()
    //不注释BThread
    //19:14:13 AThread before wait()
    //19:14:13 BThread before
    //19:14:18 BThread after
    //19:14:18 AThread after wait()

    调用notify()会按照先进先出(FIFO)的原则恢复线程。

    调用notifyAll()会按照后进先出(LIFO)的原则恢复线程。

    ps:这种顺序可能是因为某个具体的JVM实现决定的,规范上应该是随机的唤醒顺序。

    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    public class WaitClassDemo {
    
        private static SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
    
        public static void main(String[] args) {
            Object obj = new Object();
            for (int i = 0; i < 5; i++) {
                new AThread(i + "", obj).start();
            }
            new BThread(obj).start();
        }
    
        static class AThread extends Thread {
    
            Object obj;
    
            public AThread(String name, Object obj) {
                setName("AThread" + name);
                this.obj = obj;
            }
    
            @Override
            public void run() {
                synchronized (obj) {
                    System.out.println(sdf.format(new Date()) + " " + getName() + " before wait()");
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(sdf.format(new Date()) + " " + getName() + " after wait()");
                    obj.notify();
                }
            }
        }
    
        static class BThread extends Thread {
    
            Object obj;
    
            public BThread(Object obj) {
                setName("BThread");
                this.obj = obj;
            }
    
            @Override
            public void run() {
                synchronized (obj) {
                    System.out.println(sdf.format(new Date()) + " BThread before notify()");
                    obj.notify();
    //                obj.notifyAll();
                    try {
                        Thread.sleep(5000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(sdf.format(new Date()) + " BThread after notify()");
                }
            }
        }
    }
    //调用notify()
    //14:54:57 AThread0 before wait()
    //14:54:57 AThread3 before wait()
    //14:54:57 AThread4 before wait()
    //14:54:57 AThread2 before wait()
    //14:54:57 AThread1 before wait()
    //14:54:57 BThread before notify()
    //14:55:02 BThread after notify()
    //14:55:02 AThread0 after wait()
    //14:55:02 AThread3 after wait()
    //14:55:02 AThread4 after wait()
    //14:55:02 AThread2 after wait()
    //14:55:02 AThread1 after wait()
    
    //注释掉notify,只调用一次notifyAll()
    //14:56:51 AThread0 before wait()
    //14:56:51 AThread3 before wait()
    //14:56:51 AThread4 before wait()
    //14:56:51 AThread2 before wait()
    //14:56:51 AThread1 before wait()
    //14:56:51 BThread before notify()
    //14:56:56 BThread after notify()
    //14:56:56 AThread1 after wait()
    //14:56:56 AThread2 after wait()
    //14:56:56 AThread4 after wait()
    //14:56:56 AThread3 after wait()
    //14:56:56 AThread0 after wait()

    下面是一个利用wait和notifyAll实现的生产者消费者队列。因为即使notifyAll调用了,也需要退出synchronize代码才会真正去唤醒另一个线程,所以notifyAll可以写在Queue的操作之前。

    public class MainClass {
    
        public static void main(String[] args) {
            QueueBuffer q = new QueueBuffer(2);
            for(int i=0; i<5; i++) {
                Producer p = new Producer(q);
                p.start();
            }
            for(int i=0; i<2; i++) {
                Consumer c = new Consumer(q);
                c.start();
            }
        }
    
        static class QueueBuffer{
            Queue<Integer> queue = new LinkedList<>();
            int size;
            AtomicInteger seq = new AtomicInteger();
    
            public QueueBuffer(int size) {
                this.size = size;
            }
    
            public synchronized void put() {
                while (queue.size() == size) {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                notifyAll();
                int num = seq.getAndIncrement();
                queue.offer(num);
                System.out.println("producer --- " + num);
            }
    
            public synchronized int get() {
                while (queue.size() == 0) {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                notifyAll();
                return queue.poll();
            }
        }
    
    
        static class Producer extends Thread{
    
            QueueBuffer q;
            static AtomicInteger seq = new AtomicInteger();
    
            public Producer(QueueBuffer q) {
                this.q = q;
            }
    
            @Override
            public void run() {
                while (true) {
                    q.put();
                }
            }
        }
    
        static class Consumer extends Thread{
    
            QueueBuffer q;
    
            public Consumer(QueueBuffer q) {
                this.q = q;
            }
    
            @Override
            public void run() {
                while (true) {
                    int num = q.get();
                    try {
                        sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("consumer --- " + num);
                }
            }
        }
    }

    sleep

    Thread.sleep(long)应该是我们最常用的,一般也知道sleep方法不会释放锁(如果写在synchronize里的话)。

    所以跟wait的区别是

    1、sleep是Thread类的方法,是「静态方法」。wait是Object类的方法,调用需要具体的对象。

    2、sleep是不释放锁的,解除方法要么是timeout,或者interrupt一下让它抛出InterruptedException。wait是释放锁的,可以被notify/notifyAll恢复,同样也可以timeout或者interrupt。

    3、sleep在哪里都可以调用,wait必须在同步方法或同步块里调用,并且同步的对象要跟wait的对象一样。

    4、sleep作用只是线程的操作,用于短时间暂停线程,wait/notify可以用作线程间通信,达到资源调度的功能。

    yield

    yield方法也是Thread类的静态方法,会把当前线程从可运行状态变成就绪状态,之后会cpu会从众多就绪状态的线程中选择一个来执行。选线程是根据线程优先级顺序的,如果没有比当前线程更高优先级的就绪线程,完全有可能选回刚才执行yield方法的线程。

    join

    join也是Thread类方法,非静态,表示等待该线程结束,当前线程才继续执行。

    public class JoinClassDemo {
    
        private static SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
    
        public static void main(String[] args) {
    
            System.out.println(sdf.format(new Date()) + " MainThread entry");
    
            JoinThread t = new JoinThread();
            t.start();
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            System.out.println(sdf.format(new Date()) + " MainThread exit");
    
        }
    
        static class JoinThread extends Thread {
    
            @Override
            public void run() {
                System.out.println(sdf.format(new Date()) + " JoinThread entry");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(sdf.format(new Date()) + " JoinThread exit");
            }
        }
    }
    
    //17:24:18 MainThread entry
    //17:24:18 JoinThread entry
    //17:24:23 JoinThread exit
    //17:24:23 MainThread exit
    

    线程中断

    首先Thread有两个一个暂停方法suspend()和一个停止方法stop()。两个都已经已经@deprecated废弃了。suspend()暂停和resume()继续容易造成死锁,stop()具有固有的不安全性。具体可以看Java API的文档注释。

    所以抛弃上面的方法后,一般我们会用以下几个方法退出线程。

    1.设计标记位法

    public class InterruptDemo {
    
        private static SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
    
        public static void main(String[] args) {
            new InterruptThread().start();
        }
    
        static class InterruptThread extends Thread {
    
            public boolean stopFlag = true;
    
            @Override
            public void run() {
                while (stopFlag) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(sdf.format(new Date()));
                }
            }
        }
    }

    一个变量作为标记位,判断标记位以确定退出循环达到退出线程。

    缺点就是如果代码并没有这种循环语句,或者线程被其他语句阻塞了,线程可能一直不会去检查标记位。

    2.interrupt中断

    public class Thread implements Runnable {
    
        //中断目标线程
        public void interrupt() {
            if (this != Thread.currentThread())
                checkAccess();
    
            synchronized (blockerLock) {
                Interruptible b = blocker;
                if (b != null) {
                    interrupt0();           // Just to set the interrupt flag
                    b.interrupt(this);
                    return;
                }
            }
            interrupt0();
        }
    
        //返回目标线程的中断状态	static在这里理解为:只供当前线程使用这个方法
        public static boolean interrupted() {
            return currentThread().isInterrupted(true);
        }
    
        //判断目标线程是否中断
        public boolean isInterrupted() {
            return isInterrupted(false);
        }
    
        private native boolean isInterrupted(boolean ClearInterrupted);
    
    }

    线程里有一个boolean类型的中断状态,是一个标记位,是存在Native层的。当使用Thread的interrupt()方法时,线程的中断状态会被设置为true。一些阻塞方法就会抛出一个异常InterruptedException。如果没有这种阻塞方法?那就什么都不会做。下面是两种标准用法

    public class InterruptDemo {
    
        private static SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss:SSS");
    
        public static void main(String[] args) {
            InterruptThread t = new InterruptThread();
    //        Interrupt2Thread t = new Interrupt2Thread();
            t.start();
            try {
                Thread.sleep(3500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            t.interrupt();
        }
    
        static class InterruptThread extends Thread {
    
            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        //抛出InterruptedException后中断标志被清除
                        System.out.println(sdf.format(new Date()) + " catch " + isInterrupted());
                        return;
                    }
                    System.out.println(sdf.format(new Date()) + " " + isInterrupted());
                }
            }
        }
    
        static class Interrupt2Thread extends Thread {
    
            @Override
            public void run() {
                while (!isInterrupted()) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        //抛出InterruptedException后中断标志被清除
                        //可以再次调用interrupt恢复中断
                        System.out.println(sdf.format(new Date()) + " catch " + isInterrupted());
                        interrupt();
                    }
                    System.out.println(sdf.format(new Date()) + " " + isInterrupted());
                }
            }
        }
    }
    //15:08:01:249 false
    //15:08:02:251 false
    //15:08:03:252 false
    //15:08:03:749 catch false

    InterruptThread是在catch中直接return结束线程。Interrupt2Thread是catch中再次调用interrupt恢复中断状态,下次判断isInterrupted()中结束线程。

    需要注意的点如下

    ①、线程不应该交给别的线程中断,应该由自己中断自己,过程中保证资源和变量已合理的处理了(该关的关,该释放的释放)。

    ②、所谓的interrupt线程中断,只是修改了一个标记位,需要我们判断标记位做后续的处理。如果catch代码块什么都不处理,会继续跑完剩下的代码。所以应该理解为『并不是中断,而是通知你应该自行中断了』

    ③、注意在Thread.sleep这些方法,抛出InterruptedException异常后会清除标记位状态。下图为文档说明

    类似的方法有

    Thread.sleep

    Thread.join

    Object.wait

    BlockingQueue.put(e)和take()   这可以用于实现生产者消费者队列

    3.使用FutureTask.cancel(true)或者使用线程池的shutdown()方法(比如ThreadPoolExecutor.shutdown)

    AsyncTask源码解析 从AsyncTask讲到线程池》中讲到了java1.5的java.util.concurrent包带来新的线程处理方式。比如说FutureTask和ExecutorService。

    看FutureTask.cancel源码可以知道,所谓的cancel(true),内部也只是调用了interrupt()

    public class FutureTask<V> implements RunnableFuture<V> 
    	...
        public boolean cancel(boolean mayInterruptIfRunning) {
            if (!(state == NEW &&
                  UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
                      mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
                return false;
            try {    // in case call to interrupt throws exception
                if (mayInterruptIfRunning) {
                    try {
                        Thread t = runner;
                        if (t != null)
                            t.interrupt();
                    } finally { // final state
                        UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
                    }
                }
            } finally {
                finishCompletion();
            }
            return true;
        }
        ...
    }

    ThreadPoolExecutor.shutdown方法也是一样

    public class ThreadPoolExecutor extends AbstractExecutorService {
    	...
        public void shutdown() {
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                checkShutdownAccess();
                advanceRunState(SHUTDOWN);
                interruptIdleWorkers();
                onShutdown(); // hook for ScheduledThreadPoolExecutor
            } finally {
                mainLock.unlock();
            }
            tryTerminate();
        }
    
        private void interruptIdleWorkers() {
            interruptIdleWorkers(false);
        }
    
        private void interruptIdleWorkers(boolean onlyOne) {
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                for (Worker w : workers) {
                    Thread t = w.thread;
                    if (!t.isInterrupted() && w.tryLock()) {
                        try {
                            t.interrupt();
                        } catch (SecurityException ignore) {
                        } finally {
                            w.unlock();
                        }
                    }
                    if (onlyOne)
                        break;
                }
            } finally {
                mainLock.unlock();
            }
        }
        ...
    }

    展开全文
  • Java线程同步中wait()和notify()简洁例子 搞懂这两个的用法之前,请你务必搞懂线程同步的道理,否则,下面这一大篇你应该是看不懂的。 wait()和notify()一系列的方法,是属于对象的,不是属于线程的。它们用在...
  • java中多线程编程notifywait使用
  • wait和notify讲解

    2017-08-30 10:43:33
    wait和notify讲解
  • import threading con = threading.Condition() def job1(): con.acquire() print("JOB1:床前明月光") ... con.wait() # 等待对方回应消息,使用wait阻塞线程,等待对方通过notify唤醒本线程 ...
  • wait, notify, notyfyAll 都是Object里面...一般wait和notify都是要配合synchronized一起使用的 假设线程A执行一下代码,首先获得test的锁,然后test调用wait方法(在线程A中调用wait方法的时候,线程A会释放test的锁)
  • waitnotify notifyAll,这些在多线程中被经常用到的保留关键字,在实际开发的时候很多时候却并没有被大家重视。本文对这些关键字的使用进行了描述。  在 Java 中可以用 waitnotify notifyAll 来实现...
  • notify public final void notify() ...wait 方法,在对象的监视器上等待。 直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;
  • Java编程语言在应用的时候需要我们不断的进行相关知识的学习,首先我们先要理解notify()和wait(),只有理解了这个语句才能更好的进行相关的应用。希望大家有所收获。    为什么在前几天纪录线程的时候没有纪录这...
  • wait 和notify使用的时候有很多的限制: 例如必须用在schronized的同步块里面,因为他们依赖于schronized里面的锁。 wait和notify不能指定唤醒哪一个线程,只能随机的返回一个线程。 当使用调用wait时,虽然当前...
  • wait和notify方法的使用

    2020-09-15 20:45:43
    wait和notify方法的使用 1.wait notify 均为 Object 的方法 Object.wait() —— 暂停一个线程 Object.notify() —— 唤醒一个线程 2.使用这两个方法时,我们需要先有一个Object对象,配合synchronize使用 3....
  • wait和notify的理解与使用

    万次阅读 多人点赞 2016-12-03 23:09:56
    1.对于wait()和notify()的理解对于wait()和notify()的理解,还是要从jdk官方文档中开始,在Object类方法中有: void notify() Wakes up a single thread that is waiting on this object’s monitor. 译:唤醒...
  • 永远在synchronized的函数或对象里使用waitnotify和notifyAll,不然Java虚拟机会生成IllegalMonitorStateException。 永远在while循环里而不是if语句下使用wait。这样,循环会在线程睡眠前后都检查wait的条件,并...
  • JAVA线程同步中的notify和wait()函数

    千次阅读 2016-03-11 11:42:21
    其中Synchronized(Object){}可以理解为一个条件语句 if(Object 被占用){ 则等待Object被释放 } else{ 执行{}中代码 } 需要注意的是,一旦开始执行后面的{}中的代码,则Object被占用 ...1、wait和notify必须在
  • notify-wait模式说去

    2016-01-09 17:20:37
    在java的并发编程之中,有几个比较重要的模式,其中notify-wait的模式显得很重要,因为他是经典的生产者-消费者模式实现的基础,而在生产者-消费者模式上,又可以演变出很多的设计方法,比如java的线程池就是典型的...
  • 通常可以使用synchronized和notify,notifyAll以及wait方法来实现线程之间的数据传递及控制。对于对象obj来说: obj.wait():该方法的调用,使得调用该方法的执行线程(T1)放弃obj的对象锁并阻塞,直到别的线程调用...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 150,070
精华内容 60,028
关键字:

使用notify和wait