【线程间通讯之wait和notify】 1.notify后wait的线程无法立刻唤醒 2.要等到notify的线程退出synchronized后才能去真正抢锁 3.N个线程交替打印数字(理解内部类)

Data.java

package org.example.testwaitnotify;

public class Data {
    private int value;

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }
}

ThreadA.java

package org.example.testwaitnotify;

import java.util.concurrent.TimeUnit;

public class ThreadA extends Thread {
    private Data data;

    private Object lock;

    public ThreadA(Data data, Object lock) {
        this.data = data;
        this.lock = lock;
    }

    @Override
    public void run() {
        System.out.println("A start");
        synchronized (lock) {
            System.out.println("A Enter");
            try {
                // 模拟线程干活需要2s得到结果
                TimeUnit.SECONDS.sleep(2);
                data.setValue(666);
                // 得到结果后就通知等待的那个线程
                lock.notify();
                // 然而通知后,另外那个线程无法被cpu调度,因为这个线程没有退出synchronized块,还没释放锁
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Main.java

package org.example.testwaitnotify;

public class Main {
    public static void main(String[] args) {
        Data data = new Data();
        Object lock = new Object();
        new ThreadA(data, lock).start();

        System.out.println("Main start");
        synchronized (lock) {
            try {
                // wait后线程进入WAITING状态。 
                // 释放cpu执行权 + 释放锁
                // 没退出synchronized也要释放锁,不释放别的线程也没办法干活了呀!
                lock.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Main read val=" + data.getValue());
        }
    }
}

/*
Main start
A start
A Enter
Main read val=666
 */

理解:

在Main等着的时候,ThreadA即使notify了,只是说:Main处于就绪状态了,依然无法立刻获得锁。

依然要等到ThreadA执行完,退出synchronized后,才会释放锁,Main才会获得锁接着执行。

思考:

wait后是线程进入WAITING状态。 别的线程notify它后,应该是进入等待被cpu调度的状态,其实就是RUNNABLE状态,注意这个包含有就绪和正在运行的线程 (NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED)

==================例子:N个线程交替指定0~M个数字================

package org.example.testcrossprint;

public class Main {
    /**
     * 当前打印到的数字
     */
    private static volatile int curNum = 0;
    /**
     * 打印到的最大数字
     */
    private static final int MAX_NUM = 100;
    /**
     * 线程数
     */
    private static final int THREAD_NUM = 10;
    /**
     * 共享的锁
     */
    private static final Object lock = new Object();

    public static void main(String[] args) {
        for (int i = 1; i <= THREAD_NUM; i++) {
            new Thread(new Turn(i), "线程" + i).start();
        }
    }

    private static class Turn implements Runnable {
        private int index;

        public Turn(int index) {
            this.index = index;
        }

        @Override
        public void run() {
            while (curNum <= MAX_NUM) {
                synchronized (lock) {
                    if ((curNum % THREAD_NUM + 1) != index) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    } else {
                        System.out.println(Thread.currentThread().getName() + ":" + curNum++);
                        lock.notifyAll();
                    }
                }
            }
        }
    }
}

/*
线程1:0
线程2:1
线程3:2
线程4:3
线程5:4
线程6:5
线程7:6
线程8:7
线程9:8
线程10:9
线程1:10
线程2:11
线程3:12
线程4:13
线程5:14
线程6:15
线程7:16
线程8:17
线程9:18
线程10:19
线程1:20
线程2:21
线程3:22
线程4:23
线程5:24
线程6:25
线程7:26
线程8:27
线程9:28
线程10:29
线程1:30
线程2:31
线程3:32
线程4:33
线程5:34
线程6:35
线程7:36
线程8:37
线程9:38
线程10:39
线程1:40
线程2:41
线程3:42
线程4:43
线程5:44
线程6:45
线程7:46
线程8:47
线程9:48
线程10:49
线程1:50
线程2:51
线程3:52
线程4:53
线程5:54
线程6:55
线程7:56
线程8:57
线程9:58
线程10:59
线程1:60
线程2:61
线程3:62
线程4:63
线程5:64
线程6:65
线程7:66
线程8:67
线程9:68
线程10:69
线程1:70
线程2:71
线程3:72
线程4:73
线程5:74
线程6:75
线程7:76
线程8:77
线程9:78
线程10:79
线程1:80
线程2:81
线程3:82
线程4:83
线程5:84
线程6:85
线程7:86
线程8:87
线程9:88
线程10:89
线程1:90
线程2:91
线程3:92
线程4:93
线程5:94
线程6:95
线程7:96
线程8:97
线程9:98
线程10:99
线程1:100
 */

轻松的写出来了,可见自己已经理解了wait和notify了。 而且这里必须使用notifyAll才行,不然notify是随机唤醒一个,它不满足条件就会一直wait下去,程序就无法运行下去了。

笔记:

这次对内部类有了一个认识,之前总喜欢把类都单独定义,这样子让每个类短一点,这是没错的。 static 内部类的好处是:可以共享主类的一个普通变量。如:这里的计数。

如果单独定义一个Data类对象作为参数传递给每个线程,即使加了volatile变量,但是可见性未必行。应该 是 AtomicInteger这种应该是可以的。

你可能感兴趣的:(#,java多线程,java)