多线程核心知识总结(五)——趣解Thread和Object类中的线程相关方法

   日期:2020-10-19     浏览:93    评论:0    
核心提示:多线程核心知识总结趣解Thread和Object类中的线程相关方法方法概览wait,notify,notifyAll方法详解作用,用法:阻塞阶段,唤醒阶段,遇到中断

多线程核心知识总结

趣解Thread和Object类中的线程相关方法

方法概览

wait,notify,notifyAll 的作用和方法

阻塞阶段

四种情况下会被唤醒

  1. 另一个线程调用这个对象的notify()方法且刚好被唤醒的是本线程。
  2. 另一个线程调用这个对象的notifyAll()
  3. 过了wait(long timeout)规定的超时时间,如果传入0就是永久等待。
  4. 线程自身调用了interrupt()

唤醒阶段

notify会唤醒单个正在等待某对象monitor的线程,如果有多个线程都在等待,它只会唤醒一个,具体唤醒的选择是任意的,java对此没有明确规范,JVM可以拥有自己的实现,对此有一定的自由裁量权,而notify和wait都得在synchronize保护的代码块或者方法中执行


public class Wait { 

    public static Object object = new Object();

    static class Thread1 extends Thread { 

        @Override
        public void run() { 
            synchronized (object) { 
                System.out.println(Thread.currentThread().getName() + "开始执行了");
                try { 
                    object.wait();
                } catch (InterruptedException e) { 
                    e.printStackTrace();
                }
                System.out.println("线程" + Thread.currentThread().getName() + "获取到了锁。");
            }
        }
    }

    static class Thread2 extends Thread { 

        @Override
        public void run() { 
            synchronized (object) { 
                object.notify();
                System.out.println("线程" + Thread.currentThread().getName() + "调用了notify()");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException { 
        Thread1 thread1 = new Thread1();
        Thread2 thread2 = new Thread2();
        thread1.start();
        Thread.sleep(200);
        thread2.start();
    }
}


public class WaitNotifyAll implements Runnable { 

    private static final Object resourceA = new Object();


    public static void main(String[] args) throws InterruptedException { 
        Runnable r = new WaitNotifyAll();
        Thread threadA = new Thread(r);
        Thread threadB = new Thread(r);
        Thread threadC = new Thread(new Runnable() { 
            @Override
            public void run() { 
                synchronized (resourceA) { 
                    resourceA.notifyAll();
// resourceA.notify();
                    System.out.println("ThreadC notified.");
                }
            }
        });
        threadA.start();
        threadB.start();
// Thread.sleep(200);
        threadC.start();
    }
    @Override
    public void run() { 
        synchronized (resourceA) { 
            System.out.println(Thread.currentThread().getName()+" got resourceA lock.");
            try { 
                System.out.println(Thread.currentThread().getName()+" waits to start.");
                resourceA.wait();
                System.out.println(Thread.currentThread().getName()+"'s waiting to end.");
            } catch (InterruptedException e) { 
                e.printStackTrace();
            }
        }
    }
}

public class WaitNotifyReleaseOwnMonitor { 

    private static volatile Object resourceA = new Object();
    private static volatile Object resourceB = new Object();

    public static void main(String[] args) { 
        Thread thread1 = new Thread(new Runnable() { 
            @Override
            public void run() { 
                synchronized (resourceA) { 
                    System.out.println("ThreadA got resourceA lock.");
                    synchronized (resourceB) { 
                        System.out.println("ThreadA got resourceB lock.");
                        try { 
                            System.out.println("ThreadA releases resourceA lock.");
                            resourceA.wait();

                        } catch (InterruptedException e) { 
                            e.printStackTrace();
                        }
                    }
                }
            }
        });

        Thread thread2 = new Thread(new Runnable() { 
            @Override
            public void run() { 
                try { 
                    Thread.sleep(1000);
                } catch (InterruptedException e) { 
                    e.printStackTrace();
                }
                synchronized (resourceA) { 
                    System.out.println("ThreadB got resourceA lock.");
                    System.out.println("ThreadB tries to resourceB lock.");

                    synchronized (resourceB) { 
                        System.out.println("ThreadB got resourceB lock.");
                    }
                }
            }
        });

        thread1.start();
        thread2.start();
    }
}

wait notify,notifyAll的特点和性质

  1. 用必须先拥有monitor
  2. 只能唤醒其中一个
  3. 属于Object类
  4. 类似功能的Condition
  5. 同时持有多个锁的情况 :只会释放现在找到个wait()对应对象的那把锁。

wait原理

Entry Set 入口集
Wait Set 等待集

生产者消费者设计模式


public class ProducerConsumerModel { 
    public static void main(String[] args) { 
        EventStorage eventStorage = new EventStorage();
        Producer producer = new Producer(eventStorage);
        Consumer consumer = new Consumer(eventStorage);
        new Thread(producer).start();
        new Thread(consumer).start();
    }
}

class Producer implements Runnable { 

    private EventStorage storage;

    public Producer(
            EventStorage storage) { 
        this.storage = storage;
    }

    @Override
    public void run() { 
        for (int i = 0; i < 100; i++) { 
            storage.put();
        }
    }
}

class Consumer implements Runnable { 

    private EventStorage storage;

    public Consumer(
            EventStorage storage) { 
        this.storage = storage;
    }

    @Override
    public void run() { 
        for (int i = 0; i < 100; i++) { 
            storage.take();
        }
    }
}

class EventStorage { 

    private int maxSize;
    private LinkedList<Date> storage;

    public EventStorage() { 
        maxSize = 10;
        storage = new LinkedList<>();
    }

    public synchronized void put() { 
        while (storage.size() == maxSize) { 
            try { 
                wait();
            } catch (InterruptedException e) { 
                e.printStackTrace();
            }
        }
        storage.add(new Date());
        System.out.println("仓库里有了" + storage.size() + "个产品。");
        notify();
    }

    public synchronized void take() { 
        while (storage.size() == 0) { 
            try { 
                wait();
            } catch (InterruptedException e) { 
                e.printStackTrace();
            }
        }
        System.out.println("拿到了" + storage.poll() + ",现在仓库还剩下" + storage.size());
        notify();
    }
}

wait,notify常见面试问题

用程序实现两个线程交替打印0~100的奇偶数

基本思路 synchronize



public class WaitNotifyPrintOddEvenSyn { 

    private static int count;

    private static final Object lock = new Object();

    //新建2个线程
    //1个只处理偶数,第二个只处理奇数(用位运算)
    //用synchronized来通信
    public static void main(String[] args) { 
        new Thread(new Runnable() { 
            @Override
            public void run() { 
                while (count < 100) { 
                    synchronized (lock) { 
                        if ((count & 1) == 0) { 
                            System.out.println(Thread.currentThread().getName() + ":" + count++);
                        }
                    }
                }
            }
        }, "偶数").start();

        new Thread(new Runnable() { 
            @Override
            public void run() { 
                while (count < 100) { 
                    synchronized (lock) { 
                        if ((count & 1) == 1) { 
                            System.out.println(Thread.currentThread().getName() + ":" + count++);
                        }
                    }
                }
            }
        }, "奇数").start();
    }
}

用wait和notify减少废操作


public class WaitNotifyPrintOddEveWait { 

    private static int count = 0;
    private static final Object lock = new Object();


    public static void main(String[] args) { 
        new Thread(new TurningRunner(), "偶数").start();
        new Thread(new TurningRunner(), "奇数").start();
    }

    //1. 拿到锁,我们就打印
    //2. 打印完,唤醒其他线程,自己就休眠
    static class TurningRunner implements Runnable { 

        @Override
        public void run() { 
            while (count <= 100) { 
                synchronized (lock) { 
                    //拿到锁就打印
                    System.out.println(Thread.currentThread().getName() + ":" + count++);
                    lock.notify();
                    if (count <= 100) { 
                        try { 
                            //如果任务还没结束,就让出当前的锁,并休眠
                            lock.wait();
                        } catch (InterruptedException e) { 
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}

手写生产者消费者设计模式
上文已写

为什么wait()需要在同步代码块使用而sleep()不需要
为了让通信变得可靠,防止死锁和永久等待的发生,因为如果我们不把wait和notify都放在代码块里面的话,那么很有可能是执行wait之前,线程突然切换,切换到具有notify的一个线程,因为没有synchronize保护,随时都可以切过去,这样对面的第二个线程就吧程序执行完毕了,会导致进入wait()后,没有notify能唤醒,就会永久等待或者死锁,而sleep不存在这样的问题。

为什么线程通信的方法wait(),notify(),notifyAll()被定义在Object里面,而sleep定义在Thread里面
因为wait(),notify(),notifyAll(),是锁级别的操作,而锁是属于某个对象的,所以这三个方法被定义在Object里面。

wait方法是属于Object对象的,那调用Thread.wait()会怎么样?
Thread也是继承Object的,但是对于Thread类很特殊,线程退出时会自动调用notify(),这样设计的整个流程都会受到影响。

notifyAll后,所有线程都去再次抢夺锁,如果某线程抢夺失败会如何
没有抢到锁的线程会进行等待,直到拿到锁。

sleep方法详解

作用:让线程在预期的时间执行,其他时候不要占用CPU资源
sleep方法不释放锁:

  • 包括synchrinized 和 lock
  • 和wait不同

public class SleepDontReleaseMonitor implements Runnable { 

    public static void main(String[] args) { 
        SleepDontReleaseMonitor sleepDontReleaseMonitor = new SleepDontReleaseMonitor();
        new Thread(sleepDontReleaseMonitor).start();
        new Thread(sleepDontReleaseMonitor).start();
    }

    @Override
    public void run() { 
        syn();
    }

    private synchronized void syn() { 
        System.out.println("线程" + Thread.currentThread().getName() + "获取到了monitor。");
        try { 
            Thread.sleep(5000);
        } catch (InterruptedException e) { 
            e.printStackTrace();
        }
        System.out.println("线程" + Thread.currentThread().getName() + "退出了同步代码块");
    }
}

sleep方法响应中断

  1. 抛出InterruptedException
  2. 清除中断状态

public class SleepInterrupted implements Runnable{ 

    public static void main(String[] args) throws InterruptedException { 
        Thread thread = new Thread(new SleepInterrupted());
        thread.start();
        Thread.sleep(6500);
        thread.interrupt();
    }
    @Override
    public void run() { 
        for (int i = 0; i < 10; i++) { 
            System.out.println(new Date());
            try { 
                TimeUnit.HOURS.sleep(3);
                TimeUnit.MINUTES.sleep(25);
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) { 
                System.out.println("我被中断了!");
                e.printStackTrace();
            }
        }
    }
}

sleep方法可以让线程进入Waiting状态,并且不占用CPU资源,但是不释放锁,直到规定时间后再执行,休眠期间如果被中断,会抛出异常并清除中断状态。

sleep常见面试问题

sleep,wait/nofity异同(方法属于哪个对象?线程状态怎么切换?)

  • 相同
    阻塞:都会让线程进入阻塞状态
    响应中断:即使休眠期间也会响应中断,抛出异常

  • 不同
    同步方法中:wait和notify必须在同步方法中执行(线程安全,防止死锁和永久等待),sleep不需要
    释放锁: wait会释放锁,sleep不会
    指定时间:sleep必须传参,wait如果不传参会直到自己被唤醒。
    所属类

join 方法详解

  • 作用:因为新的线程加入了我们,所以我们要等他执行完再出发
  • 用法: main等待thread1执行完毕,注意谁等谁
  • 普通用法:
 
public class Join { 
    public static void main(String[] args) throws InterruptedException { 
        Thread thread = new Thread(new Runnable() { 
            @Override
            public void run() { 
                try { 
                    Thread.sleep(1000);
                } catch (InterruptedException e) { 
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "执行完毕");
            }
        });
        Thread thread2 = new Thread(new Runnable() { 
            @Override
            public void run() { 
                try { 
                    Thread.sleep(1000);
                } catch (InterruptedException e) { 
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "执行完毕");
            }
        });

        thread.start();
        thread2.start();
        System.out.println("开始等待子线程运行完毕");
        thread.join();
        thread2.join();
        System.out.println("所有子线程执行完毕");
    }
}

join注意点
CountDownLatch 或CyclicBarrier类
相同效果的实现类

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;
            }
        }
    }

写出join的替代方法


public class JoinPrinciple { 

    public static void main(String[] args) throws InterruptedException { 
        Thread thread = new Thread(new Runnable() { 
            @Override
            public void run() { 
                try { 
                    Thread.sleep(1000);
                } catch (InterruptedException e) { 
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "执行完毕");
            }
        });

        thread.start();
        System.out.println("开始等待子线程运行完毕");
        thread.join();
// synchronized (thread) { 
// thread.wait();
// }
        System.out.println("所有子线程执行完毕");
    }
}

join常见面试问题

在join期间,线程处于哪种线程状态?

join期间线程会处于waiting的状态

yield方法详解

  • 作用:释放我的CPU时间片
  • 定位:JVM不保证遵循
  • yield和sleep区别:是否随时可能再次被调度 sleep期间被阻塞,yield只是暂时将调度权让出
 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服