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

原文地址:https://www.cnblogs.com/fonxian/p/6477643.html