zoukankan      html  css  js  c++  java
  • 多线程(五) Thread和Object中线程相关方法

    1.Object.wait() , wait(long timeout)

    功能:让执行的当前线程休息一下,后面需要再唤醒。释放了该Object的monitor锁

    使用条件:

    • 线程拥有monitor锁才能用wait方法,执行后会放弃锁
    • 必须在synchronize修饰的方法或代码块中

    唤醒方法:

    • 另一个线程调用这个对象的notify方法,恰好唤醒这个线程
    • 另一个线程调用这个对象的notifyAll方法,就会唤醒所有等待线程
    • 过了wait(long timeout) 时间,自动被唤醒了,如果设置的是0就永远等待
    • 另一个线程调用这个线程的interrupt方法,就会抛出异常,释放monitor锁

    2.Object.notify, notifyAll()

    唤醒该Object上wait的线程, notify是随机唤醒一个,notifyAll唤醒所有。

    使用条件:必须在synchronize修饰的方法或代码块中,拥有该monitor,才能执行notify

    注意: 因为进行唤醒的方法notify执行完后,还持有monitor,所以被唤醒wait的线程不能立即获取锁,状态从waiting变成了blocked

    代码示例1:

    研究代码执行顺序,证明wait释放锁,notify不会释放锁,线程仍然持有锁进行下去

    /**
     *   展示wait和notify的使用方法
     * 1.研究代码执行顺序
     * 2.证明wait释放锁
     * 3.notify不会释放锁,线程仍然持有锁进行下去
     * @author Administrator
     *
     */
    public class WaitTest {
        static volatile  Object object = new Object();
        public static void main(String[] args) throws InterruptedException {
            Thread1 w = new Thread1();
            Thread2 w2 = new Thread2();
            Thread t1 = new Thread(w);
            Thread t2 = new Thread(w2);
            t1.start();
            Thread.sleep(200);
            t2.start();
        }
        
        static class Thread1 implements Runnable{
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println(Thread.currentThread().getName()+"正在运行");
                    try {
                        // 1.等待,并释放锁
                        object.wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    // 5.唤醒后,等获取到锁执行
                    System.out.println(Thread.currentThread().getName()+"继续运行");
                }    
            }    
        }
        
        static class Thread2 implements Runnable{
            @Override
            public void run() {
                // 2.获取锁
                synchronized (object) {
                    System.out.println(Thread.currentThread().getName()+"唤醒正在运行");
                    // 3.唤醒object上wait的线程
                    object.notify();
                    // 4.继续执行
                    System.out.println(Thread.currentThread().getName()+"唤醒继续运行");
                }    
            }    
        }
    }
    
    Thread-0正在运行
    Thread-1唤醒正在运行
    Thread-1唤醒继续运行
    Thread-0继续运行
    View Code

    代码示例2:证明wait只释放当前的那把锁

    /**
     *  证明wait只释放当前的那把锁
     * @author Administrator
     *
     */
    public class WaitNotifyReleaseOwnMonitor {
        private static volatile Object object1 = new Object();
        private static volatile Object object2 = new Object();
    
        public static void main(String[] args) throws InterruptedException {
            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (object1) {
                        System.out.println("Thread1 get object1 lock");
                        synchronized (object2) {
                            System.out.println("Thread1 get object2 lock");
                            try {
                                // 持有2把锁,这里只释放object1的锁,进入waiting停止执行
                                object1.wait();
                            } catch (InterruptedException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                            System.out.println("Thread1 release object1 lock");
                        }
                        System.out.println("Thread1 Object1 end");
                    }
                    System.out.println("Thread1 run end");
                }
            });
            
            Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (object1) {
                        System.out.println("Thread2 begin to notify");
                        // 持有object1的锁,并唤醒object1上wait的线程,现在不会释放锁得代码块执行完
                        object1.notify();
                        System.out.println("Thread2 end to notify");
                    }
                }
            });
            
            t1.start();
            Thread.sleep(100);
            t2.start();
        }
    }
    View Code

    代码示例3:实现生产者消费者模式

    /**
     * 生产者消费者模式用wait,notify实现
     * @author Administrator
     *
     */
    public class ProducerConsumerModel {
    
        public static void main(String[] args) {
            EventStorage storage = new EventStorage();
            Consumer consumer = new Consumer(storage);
            Producer producer = new Producer(storage);
            new Thread(consumer).start();
            new Thread(producer).start();
        }
    
    }
    
    // 仓库, 取和放商品交替进行
    class EventStorage{
        LinkedList<Date> list;
        int maxSize;
    
        public EventStorage() {
            list = new LinkedList<Date>();
            maxSize = 10;
        }
        public synchronized void put() {
            // 仓库满了等待放
            if(list.size() == maxSize) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            list.add(new Date());
            System.out.println("仓库里有"+list.size()+"个商品");
            notify();
        }
        
        public synchronized void get() {
            // 仓库空了等待取
            if(list.size() == 0) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            System.out.println("取出"+list.poll()+"商品,仓库中还有"+list.size()+"个商品");
            notify();
        }
    }
    
    class Consumer implements Runnable{
        EventStorage storage;
        public Consumer(EventStorage storage) {
            this.storage = storage;
        }
        @Override
        public void run() {
            for(int i=0; i< 100; i++) {
                storage.get();
            }
        }
    }
    
    class Producer implements Runnable{
        EventStorage storage;
        public Producer(EventStorage storage) {
            this.storage = storage;
        }
        @Override
        public void run() {
            for(int i=0; i< 100; i++) {
                storage.put();
            }
        }
    }
    View Code

    代码示例4:实现两个线程交替打印奇偶数

    /**
     * 两个线程交替打印奇偶数
     * 1.拿到锁就唤醒另一个线程
     * 2.打印完就等待释放锁
     * @author Administrator
     *
     */
    public class WaitNotifyPrintOddEvenSync {
        static int i = 0;
    
        public static void main(String[] args) {
            Object obj = new Object();
                    new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (obj) {
                        while(i<100) {
                            System.out.println(Thread.currentThread().getName()+":"+ (i++));
                            // 唤醒等待的另一个线程
                            obj.notify();
                            try {
                                // 自己陷入等待
                                obj.wait();
                            } catch (InterruptedException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                        }
    
                    }
                }
    
            }).start();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (obj) {
                        while(i<100) {
                            System.out.println(Thread.currentThread().getName()+":"+ (i++));
                            obj.notify();
                            try {
                                obj.wait();
                            } catch (InterruptedException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                        }
    
                    }
                }
            }).start();
        }
    }
    View Code

    面试问题:

    问:wait和notify为什么在synchronize修饰的同步代码块中,sleep不用?

    答: wait和notify是两个线程的通信交流,如果不在同步代码块中,wait执行前可能切换到notify方法的线程,在notify方法执行完后再执行wait造成死锁永久等待。sleep是单个线程的睡眠,不和其他线程交流。

    问:wait和notify为什么定义在Object中,sleep定义在Thread中?

    答: 因为wait和notify涉及到锁的获取释放,锁是对象的。经常会有线程持有多个对象的锁来进行配合。

    3.Thread.sleep(long timeout);

    线程睡眠一段时间进入timedwaiting状态,再执行,不会释放monitor锁和Lock锁。休息的是正在执行这行代码的线程。如果被interrupt会抛出异常并清楚中断状态。

    面试问题:

    问: wait和sleep方法的异同?

    同-都可以使线程阻塞进入waiting或timedwaiting状态; 都可以响应中断,并抛出异常并请出中断状态

    异- wait需要在同步代码块中;wait可以不用参数永久等待,直到被中断或唤醒notify;wait会释放monitor锁,sleep不会释放锁;

          wait,notify是Object方法, sleep是Thread方法

    4.thread.join()

    join会使这行代码所在的线程(主线程)阻塞,直至join方法归属的线程(子线程t1)销毁为止。

    public class Join {
        public static void main(String[] args) {
            Thread main = Thread.currentThread();
            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+" start");
                    try {
                        System.out.println(main.getName()+" "+main.getState());
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+" end");
                }
                
            });
            
            t1.start();
            try {
                t1.join();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+" end");
        }
    }

    join源码:

     public final synchronized void join(long millis)
        throws InterruptedException {
            long base = System.currentTimeMillis();
            long now = 0;
    
            if (millis < 0) {
                throw new IllegalArgumentException("timeout value is negative");
            }
    
            if (millis == 0) {
                while (isAlive()) {
                    wait(0);
                }
            } else {
                while (isAlive()) {
                    long delay = millis - now;
                    if (delay <= 0) {
                        break;
                    }
                    wait(delay);
                    now = System.currentTimeMillis() - base;
                }
            }
        }
    View Code1

    join方法是个同步方法,底层时wait实现,主线程执行时获取t1的锁,主线程一直wait阻塞,直到t1执行完run(),销毁前执行notifyall所有t1上等待的线程,主线程唤醒继续执行

    所以不能写thread.wait(), 因为线程对象在退出时会执行notifyAll, 所以线程对象不适合作为锁对象, 执行wait操作.

    5.Thread.yield()

    让出CPU的资源,让出

    public class Yield {
    
        public static void main(String[] args) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    long begin = System.currentTimeMillis();
                    int count = 0;
                    for(int i=0; i<10000000; i++) {
                        Thread.yield();
                        count+=1;
                    }
                    long cost = System.currentTimeMillis() - begin;
                    System.out.println("耗时" + cost);
                }
                
            }).start();
        }
    
    }
    View Code

    时间不一定。不释放锁。

  • 相关阅读:
    Apache 2.4+php 5.4 安装
    Linux 进程状态
    解决Redhat Linux AS使用yum时出现This system is not registered with RHN的问题(改用CentOS的yum)
    nagios 事件处理
    awk调用shell命令的两种方法:system与print
    磁盘性能分析
    如何通过JQuery将DIV的滚动条滚动到指定的位置
    GCC Windows Linux 下编译学习1
    Linux命令
    GCC Windows Linux 下编译学习2
  • 原文地址:https://www.cnblogs.com/t96fxi/p/12634758.html
Copyright © 2011-2022 走看看