多线程(五) 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

时间不一定。不释放锁。

原文地址:https://www.cnblogs.com/t96fxi/p/12634758.html