• Java核心复习 —— 线程间的协作


    一、线程间的协作

    线程之间如何进行协作,使得多个任务可以一起解决同一个问题?

    wait()与notify()

    wait()方法的作用是,让线程一直处于等待中,在等待过程中,锁是释放的。直到notify()notifyAll()发生,才被唤醒。

    wait()、notify()和notifyAll()都是Object的公有方法。为什么要这样设计?因为锁也是对象的一部分。

    注意

    实际上,只能在同步方法或同步代码块中调用wait()、notify()、notifyAll(),否则会抛出IllegalMonitorStateException异常,该异常是一种运行时异常

    sleep()

    sleep()也会让线程进入等待中,不同的是,wait()会释放锁,而sleep()不会释放锁。

    join()

    在一个线程B中,调用另一个线程A的join方法,B线程会进入等待,直到A线程完成任务,B线程继续执行。

    
    public class JoinDemo {
    
    
        class A extends Thread {
            @Override
            public void run() {
                try {
                    TimeUnit.SECONDS.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("AAAAA");
            }
        }
    
        class B extends Thread {
            private A a;
    
            public B(A a) {
                this.a = a;
            }
    
            @Override
            public void run() {
                try {
                    a.join();
                    System.out.println("BBBB");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
            }
        }
    
        public void test() {
            A a = new A();
            B b = new B(a);
            a.start();
            b.start();
        }
    
    
        public static void main(String[] args) {
            JoinDemo joinDemo = new JoinDemo();
            joinDemo.test();
        }
    
    }
    
    

    运行效果

    二、题目一

    首先出一个简单的题目,需要用到A和B两个线程来完成这个任务。解法会在本节最后列出。

    要求使用A,B两个线程按顺序打印1-100,A打印奇数,B打印偶数。

    题目分析

    该题目可以有多种解法,其中一种方法可以使用wait()notify()来解前面那题。

    首先要想

    ①两个线程要协作完成打印的任务,那么为了防止两个线程同时打印,就应该对打印的方法加上一道synchronized锁

    ②那么synchronized锁应该就要用对象锁(使用同步方法或同步代码块都是可以的),而不是类锁。思考下,为什么用类锁不行?

    ③怎么保证顺序打印呢?A、B两个线程要轮番获得锁,也就是A先获得锁完成打印任务,紧接着下次就不能获得锁了,要由B获得,这时就可以想到让A打印完,进入等待状态,并将锁释放出来交给B,B获得锁之后,完成打印任务,让A恢复执行。B再进入等待状态。

    也就是

    A获得锁,执行任务 ——> A唤醒B ——> A释放锁 ,进入等待——> B获得锁,执行任务 ——> B唤醒A ——> B释放锁,进入等待 ——> A获得锁,执行任务 ——> A唤醒B ——> A、进入等待 ——> B获得锁,执行任务 ——> 依次类推.........

    ④于是就有了 synchronized { task()、notify()、wait()}的代码思路

    运行代码

    
    public class PrintOddEven {
    
        //定义打印的方法
        public synchronized void print(String str,int num){
            System.out.println(str+num);
            notify();
            try {
               //防止最后到了100时,只有等待,没有唤醒
                if(100 != num){
                    wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        //奇数打印线程
        class Odd implements Runnable{
            @Override
            public void run() {
                for(int i=1;i<100;i+=2){
                    print("奇数:",i);
                }
            }
        }
    
        //偶数打印线程
        class Even implements Runnable{
            @Override
            public void run() {
                for(int i=2;i<=100;i+=2){
                    print("偶数:",i);
                }
            }
        }
    
        public static void main(String[] args) {
            PrintOddEven p = new PrintOddEven();
            Odd odd = p.new Odd();
            Even even = p.new Even();
            new Thread(odd).start();
            new Thread(even).start();
        }
    }
    
    
    

    运行结果

    在这里插入图片描述

    题目二

    如果再多一个线程,现在有A、B、C三个线程,分别打印出A,B,C,要如何实现?

    其实跟上面的题的思路很相似,只是额外加上了一个条件,A执行完,通知B执行,B执行完通知C执行。问题的关键是,如何实现A释放锁,由B获得锁,而不让C获得。

    
    public class PrintABC1 implements Runnable {
    
        private Object prev;
    
        private Object curr;
    
        private String value;
    
        public Object getPrev() {
            return prev;
        }
    
        public void setPrev(Object prev) {
            this.prev = prev;
        }
    
        public Object getCurr() {
            return curr;
        }
    
        public void setCurr(Object curr) {
            this.curr = curr;
        }
    
        public String getValue() {
            return value;
        }
    
        public void setValue(String value) {
            this.value = value;
        }
    
        public PrintABC1(Object prev, Object curr, String value) {
            this.prev = prev;
            this.curr = curr;
            this.value = value;
        }
    
        public static void main(String[] args) {
    
            Object lockA = new Object();
            Object lockB = new Object();
            Object lockC = new Object();
    
            Thread a = new Thread(new PrintABC1(lockC, lockA, "A"));
            Thread b = new Thread(new PrintABC1(lockA, lockB, "B"));
            Thread c = new Thread(new PrintABC1(lockB, lockC, "C"));
    
            a.start();
            sleep();
            b.start();
            sleep();
            c.start();
            sleep();
    
        }
    
        static void sleep() {
    
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
        }
    
    
        public void run() {
    
            for (int i = 1; i <= 10; i++) {
    
                synchronized (prev) {
    
                    synchronized (curr) {
    
                        System.out.println(value);
                        curr.notify();
    
                    }
    
                    if (i < 10) {
                        try {
                            prev.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
    
                }
    
            }
    
        }
    }
    
    
    
    

    参考文档

    Java编程思想
    两个线程,分别打印[1,3,5]和[2,4,6],写一个程序,打印[1,2,3,4,5,6]。
    Java线程面试题 Top 50

  • 相关阅读:
    Scraping JavaScript webpages with webkit | WebScraping.com
    linux命令行抓取网页快照-(xvfb+CutyCapt)
    Xvfb+YSlow+ShowSlow搭建前端性能测试框架
    Lind.DDD.Paging分页模块介绍
    Lind.DDD.IoC依赖注入与面向方面的实现
    Lind.DDD.Caching分布式数据集缓存介绍
    Lind.DDD.Events领域事件介绍
    知方可补不足~sqlserver中的几把锁~续
    BUG: scheduling while atomic: events/0/4/总结
    真正理解javascript的五道题目.
  • 原文地址:https://www.cnblogs.com/fonxian/p/6477643.html
Copyright © 2020-2023  润新知