线程-线程、多线程、线程同步、线程通信

Java中线程、多线程、线程同步、线程通信
一、线程
1. 概念
线程是CPU调度执行的最小单位,存在与进程中,进程则是系统中一个独立的功能的程序,是系统进行资源分配和调度的独立单位,每个进程中必须最少有一个线程。
2. 线程创建
2.1 继承Thread类型

package demo;

public class MyThread extends Thread{

    @Override
    public void run() {
        System.out.println("线程执行!");
    }

    public static void main(String[] args) {
        // 创建线程
        MyThread myThread = new MyThread();
        // 启动线程
        myThread.start();

    }
}

2.2 实现Runnable接口

package demo;

public class MyRunable implements Runnable{
    @Override
    public void run() {
        System.out.println("线程执行!");
    }

    public static void main(String[] args) {
        // 创建Runable接口
        MyRunable myRunable = new MyRunable();
        // 将Runable实现类作为构造参数创建Thread对象
        Thread thread = new Thread(myRunable);
        // 启动线程
        thread.start();
    }
}

2.3 实现Callable接口,和Future对象

package demo;

import java.util.concurrent.Callable;

public class MyCallable<String> implements Callable<String> {
    @Override
    public String call() throws Exception {
        String str = (String) "线程执行!";
        return str;
    }

}
package demo;

import java.util.concurrent.FutureTask;

public class CallableTest {

    public static void main(String[] args) {
        // 创建Callable接口实现类
        MyCallable<String> myCallable = new MyCallable<>();
        // 使用FutureTask包装Callable接口实现类
        // FutureTask是Future的实现类
        // FutureTask可以包装Callable接口的call()返回值
        FutureTask<String> futureTask = new FutureTask<>(myCallable);
        // 将FutureTask对象作为构造参数创建Thread对象
        Thread thread = new Thread(futureTask);
        // 启动线程
        thread.start();
        try {
            // FutureTask对象中通过get()获得Callable接口的call()返回值
            String returnStr = futureTask.get();
            System.out.println(returnStr);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3. 线程的生命周期
3.1 图示:在这里插入图片描述
3.2 生命周期各阶段解释
3.2.1 新建状态
当创建一个线程对象后,该线程对象就处于新建状态。处于新建状态的线程有自己的内存空间,通过调用start()方法进入就绪状态。不能对已经启动的线程再次调用start()方法,否则会出现java.lang.IllegalThreadStateException异常。
3.2.2 就绪状态
处于就绪状态的线程已经具有了运行的条件,但还没有分配到CPU资源,处于线程就绪队列(尽管是采用队列形式,事实上,把它称为可运行池而不是可运行队列。因为cpu的调度不一定是按照先进先出的顺序来调度的),等待系统为其分配CPU。等待状态并不是执行状态,当系统选定一个等待执行的Thread对象后,它就会从等待执行状态进入执行状态,系统挑选的动作称之为“cpu调度”。一旦获得CPU,线程就进入运行状态并自动调用自己的run方法。
3.2.3 运行状态
处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
处于就绪状态的线程,如果获得了cpu的调度,就会从就绪状态变为运行状态,执行run()方法中的任务。如果该线程失去了cpu资源,就会又从运行状态变为就绪状态。重新等待系统分配资源。也可以对在运行状态的线程调用yield()方法,它就会让出cpu资源,再次变为就绪状态。
注: 当发生如下情况是,线程会从运行状态变为阻塞状态:
(1)、 线程调用sleep方法主动放弃所占用的系统资源
(2)、线程调用一个阻塞式IO方法,在该方法返回之前,该线程被阻塞
(3)、线程试图获得一个同步监视器,但更改同步监视器正被其他线程所持有
线程在等待某个通知(notify)
(4)、程序调用了线程的suspend()方法将线程挂起。不过该方法容易
导致死锁,所以 程序应该尽量避免使用该方法。
(5)、当线程的run()方法执行完,或者被强制性地终止,例如出现异常,
或者调用了 stop()、desyory()方法等等,就会从运行状态转变为死亡状态。
3.2.4 阻塞状态
处于运行状态的线程在某些情况下,如执行sleep()方法,或者等I/O设备等资源,将让出CPU并暂时停止自己的运行,进入阻塞状态。
在阻塞状态的线程不能进入就绪队列。只有当引起阻塞的原因消除时,如睡眠时间已到,或等待的I/O设备空闲下来,线程便转入就绪状态,重新到就绪队列中排队等待,被系统选中后从原来停止的位置开始继续运行。有三种方法可以暂停Threads执行:
3.2.5 死亡状态
当线程的run()方法执行完成,或者被强制行的终止,就认为它死去。这个线程对象也许是活的,但是他已经不是一个单独执行的线程。线程一旦死亡,就不能复生。如果在一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。
4.线程常用方法
4.1 sleep(long millons) 线程睡眠

package demo;

public class MyThread extends Thread{

    @Override
    public void run() {
        System.out.println("线程执行!");
    }

    public static void main(String[] args) throws Exception{
        // 创建线程
        MyThread myThread = new MyThread();
        System.out.println(System.currentTimeMillis());
        Thread.sleep(10000);
        System.out.println(System.currentTimeMillis());
        // 启动线程
        myThread.start();

    }
}

执行结果:睡了10秒

1625129356709
1625129366710
线程执行!

4.2 currentThread()获取当前线程

package demo;

public class MyThread extends Thread{

    @Override
    public void run() {
        System.out.println("线程执行!");
    }

    public static void main(String[] args) throws Exception{
        // 创建线程
        MyThread myThread = new MyThread();
        System.out.println(Thread.currentThread());
        // 启动线程
        myThread.start();

    }
}

执行结果:

Thread[main,5,main]
线程执行!

4.3 setName(String arg) 设置线程名字

package demo;

public class MyThread extends Thread{

    @Override
    public void run() {
        System.out.println("线程执行!");
    }

    public static void main(String[] args) throws Exception{
        // 创建线程
        MyThread myThread = new MyThread();
        myThread.setName("设置线程名字");
        String threadName = myThread.getName();
        System.out.println(threadName);
        myThread.start();

    }
}

执行结果:

设置线程名字
线程执行!

4.4 getName() 获取线程名字
代码同上
4.5 setPriority(int newPriority) 设置线程优先级,范围:1~10,即使设置了高优先级也不一定先执行,还是得看CPU调度算法如何分配资源

  package demo;

public class MyThread extends Thread{

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("子线程:" + i);
        }
    }

    public static void main(String[] args) throws Exception{
        // 创建线程
        MyThread myThread = new MyThread();
        myThread.setPriority(10);
        myThread.start();
        for (int i = 200; i < 210; i++) {
            System.out.println("主线程让步:" + i);
        }
    }
}

4.6 setDaemon(boolean on) 设置守护线程

package demo;

public class MyThread extends Thread{

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(i);
        }
    }

    public static void main(String[] args) throws Exception{
        // 创建线程
        MyThread myThread = new MyThread();
        myThread.setDaemon(true);
        for (int i = 200; i < 210; i++) {
            System.out.println(i);
        }
        myThread.start();

    }
}

守护线程使用的情况较少,但并非无用,举例来说,JVM的垃圾回收、内存管理等线程都是守护线程。还有就是在做数据库应用时候,使用的数据库连接池,连接池本身也包含着很多后台线程,监控连接个数、超时时间、状态等等。调用线程对象的方法setDaemon(true),则可以将其设置为守护线程。
守护线程的用途为:
守护线程通常用于执行一些后台作业,例如在你的应用程序运行时播放背景音乐,在文字编辑器里做自动语法检查、自动保存等功能。
Java的垃圾回收也是一个守护线程。守护线的好处就是你不需要关心它的结束问题。例如你在你的应用程序运行的时候希望播放背景音乐,如果将这个播放背景音乐的线程设定为非守护线程,那么在用户请求退出的时候,不仅要退出主线程,还要通知播放背景音乐的线程退出;如果设定为守护线程则不需要了。
4.7 yield() 线程让步

package demo;

public class MyThread extends Thread{

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("子线程:" + i);
        }
    }

    public static void main(String[] args) throws Exception{
        // 创建线程
        MyThread myThread = new MyThread();
        Thread.yield();
        for (int i = 200; i < 210; i++) {
            System.out.println("主线程让步:" + i);
        }
        myThread.start();

    }
}

执行结果:

主线程让步:200
主线程让步:201
主线程让步:202
主线程让步:203
主线程让步:204
主线程让步:205
主线程让步:206
主线程让步:207
主线程让步:208
主线程让步:209
子线程:0
子线程:1
子线程:2
子线程:3
子线程:4
子线程:5
子线程:6
子线程:7
子线程:8
子线程:9

yield()方法和sleep()方法有点相似,它也是Thread类提供的一个静态的方法,它也可以让当前正在执行的线程暂停,让出CPU资源给其他的线程。但是和sleep()方法不同的是,它不会进入到阻塞状态,而是进入到就绪状态。yield()方法只是让当前线程暂停一下,重新进入就绪的线程池中,让系统的线程调度器重新调度器重新调度一次,完全可能出现这样的情况:当某个线程调用yield()方法之后,线程调度器又将其调度出来重新进入到运行状态执行。实际上,当某个线程调用了yield()方法暂停之后,优先级与当前线程相同,或者优先级比当前线程更高的就绪状态的线程更有可能获得执行的机会,当然,只是有可能,因为我们不可能精确的干涉CPU调度线程。
4.8 join() 线程合并(重要)

package demo;

public class MyThread extends Thread{


    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + i);
        }
    }

    public static void main(String[] args) throws Exception{
        // 创建线程
        MyThread myThread1 = new MyThread("子线程1");
        MyThread myThread2 = new MyThread("子线程2");
        myThread2.start();
        myThread2.join();
        myThread1.start();
        myThread1.join();
    }
}

执行结果:

子线程2: 0
子线程2: 1
子线程2: 2
子线程2: 3
子线程2: 4
子线程2: 5
子线程2: 6
子线程2: 7
子线程2: 8
子线程2: 9
子线程1: 0
子线程1: 1
子线程1: 2
子线程1: 3
子线程1: 4
子线程1: 5
子线程1: 6
子线程1: 7
子线程1: 8
子线程1: 9

join()方法是让让线程串行化执行,join()方法的底层是利用wait()方法实现的。join方法是一个同步方法,当主线程调用myThread2.join()方法时,主线程先获得了myThread2对象的锁,随后进入方法,调用了myThread2对象的wait()方法,使主线程进入了myThread2对象的等待池,此时,myThread2线程则还在执行,并且随后的myThread1.start()还没被执行,因此,myThread1线程也还没开始。等到myThread2线程执行完毕之后,主线程继续执行,走到了myThread1.start(),myThread1线程才会开始执行。
4.9 正确的停止线程
Thread.stop()、Thread.suspend、Thread.resume、Runtime.runFinalizersOnExit这些终止线程运行的方法已经被废弃了,使用它们是极端不安全的!

package demo;

public class MyThread extends Thread{


    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + i);
            if (i == 5){
                return;
            }
        }
    }

    public static void main(String[] args) throws Exception{
        // 创建线程
        MyThread myThread1 = new MyThread("子线程1");
        myThread1.start();
    }
}

执行结果:

子线程1: 0
子线程1: 1
子线程1: 2
子线程1: 3
子线程1: 4
子线程1: 5

5.线程同步
5.1 java中为什么需要线程同步
java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查),将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,从而保证了该变量的唯一性和准确性。
5.2 synchronized关键字
不同步情况下:

package demo;

public class Account {

    private String accountNo;
    private double balance;

    public Account() {
    }

    public Account(String accountNo, double balance) {
        super();
        this.accountNo = accountNo;
        this.balance = balance;
    }

    public String getAccountNo() {
        return accountNo;
    }

    public void setAccountNo(String accountNo) {
        this.accountNo = accountNo;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    public int hashCode() {
        return accountNo.hashCode();
    }

    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj != null && obj.getClass() == Account.class) {
            Account target = (Account) obj;
            return target.getAccountNo().equals(accountNo);
        }
        return false;
    }
}
package demo;

public class DrawThread extends Thread{

    //模拟用户账户
    private Account account;

    //当前取钱线程所希望的钱数
    private double drawAmount;

    public DrawThread(String name,Account account,double drawAmount) {
        super(name);
        this.account=account;
        this.drawAmount=drawAmount;
    }

    //多个线程修改同一个共享数据,可能发生线程安全问题
    @Override
    public void run() {
        if(account.getBalance()>drawAmount) {
            System.out.println(getName()+"取钱成功"+" "+drawAmount);
            try {
                Thread.sleep(1);
            }catch(Exception e) {
                e.printStackTrace();
            }
            account.setBalance(account.getBalance()-drawAmount);
            System.out.println("\t余额为"+" "+account.getBalance());
        }else {
            System.out.println("余额不足,取钱失败");
        }

    }
}
package demo;

public class DrawTest {

    public static void main(String[] args) {
        Account account=new Account("1234567",1000);
        //模拟两个线程同时操作账号
        new DrawThread("甲", account, 800).start();;
        new DrawThread("乙", account, 800).start();;
    }
}

执行结果:

乙取钱成功 800.0
甲取钱成功 800.0
	余额为 -600.0
	余额为 200.0

账户总共1000元,甲乙两人都是取800,而且都成功了,明显不合理,这就是线程安全问题,可以同步代码来解决。
5.2.1 synchronized关键字同步方法
将Account修改如下新增了方法:

public synchronized void drawAccount(Account account,double drawAccount,Thread thread)

修改DrawThread中的run()方法
代码如下:

package demo;

public class Account {

    private String accountNo;
    private double balance;

    public Account() {
    }

    public Account(String accountNo, double balance) {
        super();
        this.accountNo = accountNo;
        this.balance = balance;
    }

    public String getAccountNo() {
        return accountNo;
    }

    public void setAccountNo(String accountNo) {
        this.accountNo = accountNo;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    public int hashCode() {
        return accountNo.hashCode();
    }

    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj != null && obj.getClass() == Account.class) {
            Account target = (Account) obj;
            return target.getAccountNo().equals(accountNo);
        }
        return false;
    }

    public synchronized void drawAccount(Account account,double drawAmount,Thread thread){
        if(account.getBalance()>drawAmount) {
            System.out.println(thread.getName()+"取钱成功"+" "+drawAmount);
            try {
                Thread.sleep(1);
            }catch(Exception e) {
                e.printStackTrace();
            }
            account.setBalance(account.getBalance()-drawAmount);
            System.out.println("\t余额为"+" "+getBalance());
        }else {
            System.out.println("余额不足,取钱失败");
        }
    }
}
package demo;

public class DrawThread extends Thread {

    //模拟用户账户
    private Account account;

    //当前取钱线程所希望的钱数
    private double drawAmount;

    public DrawThread(String name, Account account, double drawAmount) {
        super(name);
        this.account = account;
        this.drawAmount = drawAmount;
    }

    //多个线程修改同一个共享数据,可能发生线程安全问题
    @Override
    public void run() {
        account.drawAccount(account, drawAmount, this);
    }
}
package demo;

public class DrawTest {

    public static void main(String[] args) {
        Account account=new Account("1234567",1000);
        //模拟两个线程同时操作账号
        new DrawThread("甲", account, 800).start();;
        new DrawThread("乙", account, 800).start();;
    }
}

运行结果:

甲取钱成功 800.0
	余额为 200.0
余额不足,取钱失败

5.2.2 synchronized同步代码块

package demo;

public class Account {

    private String accountNo;
    private double balance;

    public Account() {
    }

    public Account(String accountNo, double balance) {
        super();
        this.accountNo = accountNo;
        this.balance = balance;
    }

    public String getAccountNo() {
        return accountNo;
    }

    public void setAccountNo(String accountNo) {
        this.accountNo = accountNo;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    public int hashCode() {
        return accountNo.hashCode();
    }

    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj != null && obj.getClass() == Account.class) {
            Account target = (Account) obj;
            return target.getAccountNo().equals(accountNo);
        }
        return false;
    }

}
package demo;

public class DrawThread extends Thread{

    //模拟用户账户
    private Account account;

    //当前取钱线程所希望的钱数
    private double drawAmount;

    public DrawThread(String name,Account account,double drawAmount) {
        super(name);
        this.account=account;
        this.drawAmount=drawAmount;
    }

    //多个线程修改同一个共享数据,可能发生线程安全问题
    @Override
    public void run() {
        synchronized (account) {
            if (account.getBalance() > drawAmount) {
                System.out.println(getName() + "取钱成功" + " " + drawAmount);
                try {
                    Thread.sleep(1);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                account.setBalance(account.getBalance() - drawAmount);
                System.out.println("\t余额为" + " " + account.getBalance());
            } else {
                System.out.println("余额不足,取钱失败");
            }
        }

    }
}
package demo;

public class DrawTest {

    public static void main(String[] args) {
        Account account=new Account("1234567",1000);
        //模拟两个线程同时操作账号
        new DrawThread("甲", account, 800).start();;
        new DrawThread("乙", account, 800).start();;
    }
}

运行结果:

甲取钱成功 800.0
	余额为 200.0
余额不足,取钱失败

5.3 Lock同步锁
从Java5开始,Java提供了一种功能更强大的线程同步机制—一通过显式定义同步锁对象来实现同步,在这种机制下,同步锁由Lock对象充当。
Lock提供了比 synchronized方法和 synchronized代码块更广泛的锁定操作,Lock允许实现更灵活的结构,可以具有差别很大的属性,并且支持多个相关的 Condition对象。

package demo;

import java.util.concurrent.locks.ReentrantLock;

public class Account {

    private String accountNo;
    private double balance;
    ReentrantLock reentrantLock;

    public Account() {
    }

    public Account(String accountNo, double balance, ReentrantLock reentrantLock) {
        super();
        this.accountNo = accountNo;
        this.balance = balance;
        this.reentrantLock = reentrantLock;
    }

    public String getAccountNo() {
        return accountNo;
    }

    public void setAccountNo(String accountNo) {
        this.accountNo = accountNo;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    public int hashCode() {
        return accountNo.hashCode();
    }

    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj != null && obj.getClass() == Account.class) {
            Account target = (Account) obj;
            return target.getAccountNo().equals(accountNo);
        }
        return false;
    }

    public void drawAccount(Account account, double drawAmount, Thread thread) {

        try {
            reentrantLock.lock();
            if (account.getBalance() > drawAmount) {
                System.out.println(thread.getName() + "取钱成功" + " " + drawAmount);
                try {
                    Thread.sleep(1);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                account.setBalance(account.getBalance() - drawAmount);
                System.out.println("\t余额为" + " " + getBalance());
            } else {
                System.out.println("余额不足,取钱失败");
            }
        } finally {
            reentrantLock.unlock();
        }
    }
}
package demo;

public class DrawThread extends Thread {

    //模拟用户账户
    private Account account;

    //当前取钱线程所希望的钱数
    private double drawAmount;

    public DrawThread(String name, Account account, double drawAmount) {
        super(name);
        this.account = account;
        this.drawAmount = drawAmount;
    }

    //多个线程修改同一个共享数据,可能发生线程安全问题
    @Override
    public void run() {
        account.drawAccount(account, drawAmount, this);
    }
}
package demo;

import java.util.concurrent.locks.ReentrantLock;

public class DrawTest {

    public static void main(String[] args) {
        ReentrantLock reentrantLock = new ReentrantLock();
        Account account = new Account("1234567", 1000, reentrantLock);
        //模拟两个线程同时操作账号
        new DrawThread("甲", account, 800).start();
        ;
        new DrawThread("乙", account, 800).start();
        ;
    }
}

// TODO 以后会补充同步锁这边
5.4 死锁
当两个线程相互等待对方释放同步监视器时就会发生死锁,Java虚拟机没有监测,也没有采取措施来处理死锁情况,所以多线程编程时应该采取措施避免死锁岀现。一旦岀现死锁,整个程序既不会发生任何异常,也不会给出任何提示,只是所有线程处于阻塞状态,无法继续。

class A{
    public synchronized void foo(B b) {
        System.out.println("当前线程名:"+Thread.currentThread().getName()+"进入A实例的foo方法");//①
         
        try {
            Thread.sleep(200);
        }catch(InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("当前线程名:"+Thread.currentThread().getName()+"企图调用B的方法");//③
        b.last();
    }
     
    public synchronized void last() {
        System.out.println("进入了A类的last方法");
    }
}
class B{
     
    public synchronized void bar(A a) {
        System.out.println("当前线程名:"+Thread.currentThread().getName()+"进入B实例的bar方法");//②
        try {
            Thread.sleep(200);
        }catch(InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("当前线程名:"+Thread.currentThread().getName()+"企图调用A的方法");//④
        a.last();
    }
     
    public synchronized void last() {
        System.out.println("进入了B类的last方法");
    }
     
}

public class DeadLock implements Runnable {
     
    A a=new A();
    B b=new B();
     
    public void init() {
        Thread.currentThread().setName("主线程");
        a.foo(b);
        System.out.println("进入了主线程之后");
    }
     
     
     
    @Override
    public void run() {
        Thread.currentThread().setName("副线程");
        b.bar(a);
        System.out.println("进入副线程之后");
    }
     
     
    public static void main(String[] args) {
        DeadLock d=new DeadLock();
        new Thread(d).start();
        d.init();
    }
     
     
}

程序既无法向下执行,也不会抛出任何异常,就一直“僵持”着。究其原因,是因为:上面程序中A对象和B对象的方法都是同步方法,也就是A对象和B对象都是同步锁。程序中两个线程执行,副线程的线程执行体是 DeadLock类的run()方法,主线程的线程执行体是 Deadlock的main()方法(主线程调用了init()方法)。其中run()方法中让B对象调用b进入foo()方法之前,该线程对A对象加锁—当程序执行到①号代码时,主线程暂停200ms:CPU切换到执行另一个线程,让B对象执行bar()方法,所以看到副线程开始执行B实例的bar()方法,进入bar()方法之前,该线程对B对象加锁——当程序执行到②号代码时,副线程也暂停200ms:接下来主线程会先醒过来,继续向下执行,直到③号代码处希望调用B对象的last()方法——执行该方法之前必须先对B对象加锁,但此时副线程正保持着B对象的锁,所以主线程阻塞;接下来副线程应该也醒过来了,继续向下执行,直到④号代码处希望调用A对象的 last()方法——执行该方法之前必须先对A对象加锁,但此时主线程没有释放对A对象的锁——至此,就出现了主线程保持着A对象的锁,等待对B对象加锁,而副线程保持着B对象的锁,等待对A对象加锁,两个线程互相等待对方先释放,所以就出现了死锁。
5.5 线程通信
5.5.1 wait()、notify()、notifyAll()解释:
wait()=>让当前线程 释放对象锁并进入等待(阻塞)状态。
notify()=>唤醒一个正在等待相应对象锁的线程,使其进入就绪队列,以便在当前线程释放锁后竞争锁,进而得到CPU的执行。
notifyAll()=>唤醒所有正在等待相应对象锁的线程,使它们进入就绪队列,以便在当前线程释放锁后竞争锁,进而得到CPU的执行。
5.5.2先看一个不加唤醒机制的例子

package threadCommunication;

import java.util.ArrayList;
import java.util.List;

/**
 * 线程操作资源
 */
public class Resource {

    /**
     * 线程操作的资源集合
     */
    private List<String> resourceList = new ArrayList<>();

    /**
     * 往资源集合里面添加资源
     */
    public void addResource() {
        synchronized (this) {
            System.out.println("添加资源的线程拿到锁");
            int count = 0;
            while (true) {
                resourceList.add("resource" + count);
                count++;
                if (count == 10) {
                    break;
                }
            }
        }
    }

    /**
     * 获取资源集合里面的资源并删除资源
     *
     * @return
     */
    public String subResource() {
        String resourceStr = "";
        synchronized (this) {
            System.out.println("获取资源的线程拿到锁");
            int count = 0;
            try {
                count = resourceList.size() - 1;
                while (true) {
                    resourceStr = resourceList.get(count);
                    resourceList.remove(count);
                    System.out.println("获取的资源:" + resourceStr);
                    count--;
                    if (count == -1) {
                        break;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return resourceStr;
    }
}
package threadCommunication;

/**
 * 添加资源线程
 */
public class AddThread implements Runnable{

    private Resource resource;

    private String threadName;

    AddThread(String threadName,Resource resource){
        this.threadName = threadName;
        this.resource = resource;
    }

    @Override
    public void run() {
        System.out.println(threadName + "启动");
        resource.addResource();
        System.out.println(threadName + "运行结束");
    }
}
package threadCommunication;

/**
 * 获取资源的线程
 */
public class SubThread implements Runnable {

    private Resource resource;

    private String threadName;

    SubThread(String threadName, Resource resource) {
        this.threadName = threadName;
        this.resource = resource;
    }

    @Override
    public void run() {
        System.out.println(threadName + "启动");
        resource.subResource();
        System.out.println(threadName + "运行结束");
    }
}
package threadCommunication;

public class ThreadTest {


    public static void main(String[] args) throws Exception{

        Resource resource = new Resource();
        AddThread addThread = new AddThread("添加资源线程",resource);
        SubThread subThread = new SubThread("获取资源线程",resource);
        Thread thread1 = new Thread(addThread);
        Thread thread2 = new Thread(subThread);
        thread1.start();
        //这里thread1.join()是为了保证添加资源线程先执行
        //现在thread1(添加资源线程)在主线程main中运行,join()方法可以让main线程等待thread1(添加资源线程)执行完再向下执行
        //所以这时候thread2(获取资源线程)还未启动,不然交替执行的话,如果thread2(获取资源线程)
        thread1.join();
        thread2.start();
    }
}

运行结果:

添加资源线程启动
添加资源的线程拿到锁
添加资源线程运行结束
获取资源线程启动
获取资源的线程拿到锁
获取的资源:resource9
获取的资源:resource8
获取的资源:resource7
获取的资源:resource6
获取的资源:resource5
获取的资源:resource4
获取的资源:resource3
获取的资源:resource2
获取的资源:resource1
获取的资源:resource0
获取资源线程运行结束

5.5.3通过wait()、notify()、notifyAll()的线程通信(1个添加资源线程+1个获取资源线程)
注意:wait()、notify()、notityAll()是Object类的方法,不属于线程方法,而且必须在同步代码中使用,因为这些方法是操作线程状态的,必须明确操作的是哪个对象的锁上的线程。
特别注意:在用wait()、notify()、notifyAll()进行线程通信的时候,千万不要用if(…)作为判断唤醒还是等待,不然会出现虚假唤醒的情况,使用while(…)循环判断,这也是官方提倡的,学习中遇到的一个坑,记录。

package threadCommunication;

import java.util.ArrayList;
import java.util.List;

/**
 * 线程操作资源
 */
public class Resource {

    /**
     * 线程操作的资源集合
     */
    private volatile List<String> resourceList = new ArrayList<>();

    /**
     * 往资源集合里面添加资源
     */
    public void addResource() {
        synchronized (this) {
            try {
                System.out.println(Thread.currentThread().getName() + "拿到锁");
                // 如果资源没有被消耗完,添加资源线程进入等待,等待资源消耗完
                while (resourceList.size() > 0) {
                    System.out.println(Thread.currentThread().getName() + "资源未被消耗释放锁锁");
                    this.wait();
                }
                int count = 0;
                while (true) {
                    resourceList.add("resource" + count);
                    count++;
                    while (count == 10) {
                        // 资源添加完成唤醒消耗资源的线程,进行资源消耗
                        System.out.println(Thread.currentThread().getName() + "添加完成,唤醒其他线程");
                        this.notify();
                        return;
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 获取资源集合里面的资源并删除资源
     *
     * @return
     */
    public void subResource() {
        String resourceStr = "";
        synchronized (this) {
            System.out.println(Thread.currentThread().getName() + "拿到锁");
            int count = 0;
            try {
                // 如果当前资源未添加或者资源被消耗完,获取资源线程进入等待
                while (resourceList.size() == 0) {
                    System.out.println(Thread.currentThread().getName() + "暂无资源释放锁");
                    this.wait();
                }
                count = resourceList.size() - 1;
                while (true) {
                    resourceStr = resourceList.get(count);
                    resourceList.remove(count);
                    System.out.println(Thread.currentThread().getName() + "获取的资源:" + resourceStr);
                    count--;
                    while (count == -1) {
                        // 资源消耗完,唤醒添加资源线程
                        System.out.println(Thread.currentThread().getName() + "获取资源完成,唤醒其他线程");
                        this.notify();
                        return;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


}
package threadCommunication;

/**
 * 添加资源线程
 */
public class AddThread implements Runnable {

    private Resource resource;

    private String threadName;

    AddThread(String threadName, Resource resource) {
        this.threadName = threadName;
        this.resource = resource;
    }

    @Override
    public void run() {
        // 无限循环添加资源
        while (true) {
            System.out.println(threadName + "启动");
            resource.addResource();
            System.out.println(threadName + "运行结束");
        }
    }
}
package threadCommunication;

/**
 * 获取资源的线程
 */
public class SubThread implements Runnable {

    private Resource resource;

    private String threadName;

    SubThread(String threadName, Resource resource) {
        this.threadName = threadName;
        this.resource = resource;
    }

    @Override
    public void run() {
        // 无限循环获取资源
        while (true) {
            System.out.println(threadName + "启动");
            resource.subResource();
            System.out.println(threadName + "运行结束");
        }
    }
}
package threadCommunication;

public class ThreadTest {


    public static void main(String[] args) throws Exception{

        Resource resource = new Resource();
        AddThread addThread = new AddThread("添加-资源线程",resource);
        SubThread subThread = new SubThread("获取-资源线程",resource);
        Thread thread1 = new Thread(addThread,"添加-资源线程-1");
        Thread thread2 = new Thread(subThread,"获取-资源线程-1");
        thread1.start();
        thread2.start();

    }
}

运行结果:

添加-资源线程启动
获取-资源线程-1获取的资源:resource9
获取-资源线程-1获取的资源:resource8
获取-资源线程-1获取的资源:resource7
获取-资源线程-1获取的资源:resource6
获取-资源线程-1获取的资源:resource5
获取-资源线程-1获取的资源:resource4
获取-资源线程-1获取的资源:resource3
获取-资源线程-1获取的资源:resource2
获取-资源线程-1获取的资源:resource1
获取-资源线程-1获取的资源:resource0
获取-资源线程-1获取资源完成,唤醒其他线程
获取-资源线程运行结束
获取-资源线程启动
添加-资源线程-1拿到锁
添加-资源线程-1添加完成,唤醒其他线程
添加-资源线程运行结束
获取-资源线程-1拿到锁
获取-资源线程-1获取的资源:resource9
获取-资源线程-1获取的资源:resource8
获取-资源线程-1获取的资源:resource7
获取-资源线程-1获取的资源:resource6
获取-资源线程-1获取的资源:resource5
获取-资源线程-1获取的资源:resource4
获取-资源线程-1获取的资源:resource3
获取-资源线程-1获取的资源:resource2
获取-资源线程-1获取的资源:resource1
获取-资源线程-1获取的资源:resource0
获取-资源线程-1获取资源完成,唤醒其他线程
获取-资源线程运行结束
获取-资源线程启动
获取-资源线程-1拿到锁
获取-资源线程-1暂无资源释放锁
添加-资源线程启动
添加-资源线程-1拿到锁
添加-资源线程-1添加完成,唤醒其他线程
添加-资源线程运行结束
添加-资源线程启动
获取-资源线程-1获取的资源:resource9
获取-资源线程-1获取的资源:resource8
获取-资源线程-1获取的资源:resource7
获取-资源线程-1获取的资源:resource6
获取-资源线程-1获取的资源:resource5
获取-资源线程-1获取的资源:resource4
获取-资源线程-1获取的资源:resource3
获取-资源线程-1获取的资源:resource2
获取-资源线程-1获取的资源:resource1
获取-资源线程-1获取的资源:resource0
获取-资源线程-1获取资源完成,唤醒其他线程
获取-资源线程运行结束
........................

5.5.4通过wait()、notify()、notifyAll()的线程通信(多个添加资源线程+多个获取资源线程)

package threadCommunication;

import java.util.ArrayList;
import java.util.List;

/**
 * 线程操作资源
 */
public class Resource {

    /**
     * 线程操作的资源集合
     */
    private volatile List<String> resourceList = new ArrayList<>();

    /**
     * 往资源集合里面添加资源
     */
    public void addResource() {
        synchronized (this) {
            try {
                System.out.println(Thread.currentThread().getName() + "拿到锁");
                // 如果资源没有被消耗完,添加资源线程进入等待,等待资源消耗完
                while (resourceList.size() > 0) {
                    System.out.println(Thread.currentThread().getName() + "资源未被消耗释放锁锁");
                    this.wait();
                }
                int count = 0;
                while (true) {
                    resourceList.add("resource" + count);
                    count++;
                    // 这里要用while循环判断,不然可能会出现虚假唤醒
                    // 就是说this.notifyAll()唤醒了所有等待中线程,而且正好自己由分配到了cpu资源
                    // 这时候代码不会从synchronized那里执行了,直接从int count = 0;;这里执行
                    // 这时候就会出现resourceList集合中添加超过10条的数据,这里使用notifyAll()是为了防止自己唤醒自己的情况,没有意义,而且会陷入死锁
                    while (count == 10) {
                        // 资源添加完成唤醒消耗资源的线程,进行资源消耗
                        System.out.println(Thread.currentThread().getName() + "添加完成,唤醒其他线程");
                        this.notifyAll();
                        return;
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 获取资源集合里面的资源并删除资源
     *
     * @return
     */
    public void subResource() {
        String resourceStr = "";
        synchronized (this) {
            System.out.println(Thread.currentThread().getName() + "拿到锁");
            int count = 0;
            try {
                // 如果当前资源未添加或者资源被消耗完,获取资源线程进入等待
                while (resourceList.size() == 0) {
                    System.out.println(Thread.currentThread().getName() + "暂无资源释放锁");
                    this.wait();
                }
                count = resourceList.size() - 1;
                while (true) {
                    resourceStr = resourceList.get(count);
                    resourceList.remove(count);
                    System.out.println(Thread.currentThread().getName() + "获取的资源:" + resourceStr);
                    count--;
                    // 这里要用while循环判断,不然可能会出现虚假唤醒
                    // 就是说this.notifyAll()唤醒了所有等待中线程,而且正好自己由分配到了cpu资源
                    // 这时候代码不会从synchronized那里执行了,直接从count = resourceList.size() - 1;这里执行
                    // 这时候就会出现下标越界的情况,这里使用notifyAll()是为了防止自己唤醒自己的情况,没有意义,而且会陷入死锁
                    while (count == -1) {
                        // 资源消耗完,唤醒添加资源线程
                        System.out.println(Thread.currentThread().getName() + "获取资源完成,唤醒其他线程");
                        this.notifyAll();
                        return;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


}
package threadCommunication;

/**
 * 添加资源线程
 */
public class AddThread implements Runnable {

    private Resource resource;

    private String threadName;

    AddThread(String threadName, Resource resource) {
        this.threadName = threadName;
        this.resource = resource;
    }

    @Override
    public void run() {
        // 无限循环添加资源
        while (true) {
            System.out.println(threadName + "启动");
            resource.addResource();
            System.out.println(threadName + "运行结束");
        }
    }
}
package threadCommunication;

/**
 * 获取资源的线程
 */
public class SubThread implements Runnable {

    private Resource resource;

    private String threadName;

    SubThread(String threadName, Resource resource) {
        this.threadName = threadName;
        this.resource = resource;
    }

    @Override
    public void run() {
        // 无限循环获取资源
        while (true) {
            System.out.println(threadName + "启动");
            resource.subResource();
            System.out.println(threadName + "运行结束");
        }
    }
}
package threadCommunication;

public class ThreadTest {


    public static void main(String[] args) throws Exception{

        Resource resource = new Resource();
        AddThread addThread = new AddThread("添加-资源线程",resource);
        SubThread subThread = new SubThread("获取-资源线程",resource);
        Thread thread1 = new Thread(addThread,"添加-资源线程-1");
        Thread thread2 = new Thread(addThread,"添加-资源线程-2");
        Thread thread3 = new Thread(addThread,"添加-资源线程-3");
        Thread thread4 = new Thread(addThread,"添加-资源线程-4");
        Thread thread5 = new Thread(subThread,"获取-资源线程-1");
        Thread thread6 = new Thread(subThread,"获取-资源线程-2");
        Thread thread7 = new Thread(subThread,"获取-资源线程-3");
        Thread thread8 = new Thread(subThread,"获取-资源线程-4");
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
        thread5.start();
        thread6.start();
        thread7.start();
        thread8.start();
    }
}

运行结果:

添加-资源线程启动
添加-资源线程-2拿到锁
添加-资源线程-2资源未被消耗释放锁锁
添加-资源线程-3资源未被消耗释放锁锁
添加-资源线程-4资源未被消耗释放锁锁
添加-资源线程-1资源未被消耗释放锁锁
获取-资源线程-4获取的资源:resource9
获取-资源线程-4获取的资源:resource8
获取-资源线程-4获取的资源:resource7
获取-资源线程-4获取的资源:resource6
获取-资源线程-4获取的资源:resource5
获取-资源线程-4获取的资源:resource4
获取-资源线程-4获取的资源:resource3
获取-资源线程-4获取的资源:resource2
获取-资源线程-4获取的资源:resource1
获取-资源线程-4获取的资源:resource0
获取-资源线程-4获取资源完成,唤醒其他线程
获取-资源线程运行结束
获取-资源线程启动
获取-资源线程-4拿到锁
获取-资源线程-4暂无资源释放锁
获取-资源线程-3暂无资源释放锁
添加-资源线程-1添加完成,唤醒其他线程
添加-资源线程运行结束
添加-资源线程启动
添加-资源线程-1拿到锁
添加-资源线程-1资源未被消耗释放锁锁
添加-资源线程-4资源未被消耗释放锁锁
添加-资源线程-3资源未被消耗释放锁锁
添加-资源线程-2资源未被消耗释放锁锁
获取-资源线程-2获取的资源:resource9
获取-资源线程-2获取的资源:resource8
获取-资源线程-2获取的资源:resource7
获取-资源线程-2获取的资源:resource6
获取-资源线程-2获取的资源:resource5
获取-资源线程-2获取的资源:resource4
获取-资源线程-2获取的资源:resource3
获取-资源线程-2获取的资源:resource2
获取-资源线程-2获取的资源:resource1
获取-资源线程-2获取的资源:resource0
获取-资源线程-2获取资源完成,唤醒其他线程
获取-资源线程运行结束
获取-资源线程启动
获取-资源线程-2拿到锁
获取-资源线程-2暂无资源释放锁
获取-资源线程-1暂无资源释放锁
添加-资源线程-2添加完成,唤醒其他线程
添加-资源线程运行结束
添加-资源线程启动
添加-资源线程-2拿到锁
添加-资源线程-2资源未被消耗释放锁锁
添加-资源线程-3资源未被消耗释放锁锁
添加-资源线程-4资源未被消耗释放锁锁
添加-资源线程-1资源未被消耗释放锁锁
获取-资源线程-3获取的资源:resource9
获取-资源线程-3获取的资源:resource8
获取-资源线程-3获取的资源:resource7
获取-资源线程-3获取的资源:resource6
获取-资源线程-3获取的资源:resource5
获取-资源线程-3获取的资源:resource4
获取-资源线程-3获取的资源:resource3
获取-资源线程-3获取的资源:resource2
获取-资源线程-3获取的资源:resource1
获取-资源线程-3获取的资源:resource0
获取-资源线程-3获取资源完成,唤醒其他线程
获取-资源线程运行结束
获取-资源线程启动
获取-资源线程-3拿到锁
获取-资源线程-3暂无资源释放锁
获取-资源线程-4暂无资源释放锁
添加-资源线程-1添加完成,唤醒其他线程
添加-资源线程运行结束
添加-资源线程启动
添加-资源线程-1拿到锁
添加-资源线程-1资源未被消耗释放锁锁
添加-资源线程-4资源未被消耗释放锁锁
添加-资源线程-3资源未被消耗释放锁锁
添加-资源线程-2资源未被消耗释放锁锁
获取-资源线程-1获取的资源:resource9
获取-资源线程-1获取的资源:resource8
获取-资源线程-1获取的资源:resource7
获取-资源线程-1获取的资源:resource6
获取-资源线程-1获取的资源:resource5
获取-资源线程-1获取的资源:resource4
获取-资源线程-1获取的资源:resource3
获取-资源线程-1获取的资源:resource2
获取-资源线程-1获取的资源:resource1
获取-资源线程-1获取的资源:resource0
获取-资源线程-1获取资源完成,唤醒其他线程
获取-资源线程运行结束
获取-资源线程启动
获取-资源线程-1拿到锁
获取-资源线程-1暂无资源释放锁
获取-资源线程-2暂无资源释放锁
添加-资源线程-2添加完成,唤醒其他线程
添加-资源线程运行结束
添加-资源线程启动
添加-资源线程-2拿到锁
添加-资源线程-2资源未被消耗释放锁锁
添加-资源线程-3资源未被消耗释放锁锁
添加-资源线程-4资源未被消耗释放锁锁
添加-资源线程-1资源未被消耗释放锁锁
获取-资源线程-4获取的资源:resource9
获取-资源线程-4获取的资源:resource8
获取-资源线程-4获取的资源:resource7
获取-资源线程-4获取的资源:resource6
获取-资源线程-4获取的资源:resource5
获取-资源线程-4获取的资源:resource4
获取-资源线程-4获取的资源:resource3
获取-资源线程-4获取的资源:resource2
获取-资源线程-4获取的资源:resource1
获取-资源线程-4获取的资源:resource0
获取-资源线程-4获取资源完成,唤醒其他线程
获取-资源线程运行结束
获取-资源线程启动
获取-资源线程-4拿到锁
获取-资源线程-4暂无资源释放锁
获取-资源线程-3暂无资源释放锁
添加-资源线程-1添加完成,唤醒其他线程
添加-资源线程运行结束
添加-资源线程-4资源未被消耗释放锁锁
添加-资源线程启动
添加-资源线程-3资源未被消耗释放锁锁
添加-资源线程-2资源未被消耗释放锁锁
获取-资源线程-2获取的资源:resource9
获取-资源线程-2获取的资源:resource8
获取-资源线程-2获取的资源:resource7
获取-资源线程-2获取的资源:resource6
获取-资源线程-2获取的资源:resource5
获取-资源线程-2获取的资源:resource4
获取-资源线程-2获取的资源:resource3
获取-资源线程-2获取的资源:resource2
获取-资源线程-2获取的资源:resource1
获取-资源线程-2获取的资源:resource0
获取-资源线程-2获取资源完成,唤醒其他线程
获取-资源线程运行结束
获取-资源线程启动
获取-资源线程-2拿到锁
获取-资源线程-2暂无资源释放锁
获取-资源线程-1暂无资源释放锁
添加-资源线程-2添加完成,唤醒其他线程
添加-资源线程运行结束
添加-资源线程启动
添加-资源线程-3资源未被消耗释放锁锁
添加-资源线程-4资源未被消耗释放锁锁
添加-资源线程-1拿到锁
添加-资源线程-1资源未被消耗释放锁锁
获取-资源线程-3获取的资源:resource9
获取-资源线程-3获取的资源:resource8
获取-资源线程-3获取的资源:resource7
获取-资源线程-3获取的资源:resource6
获取-资源线程-3获取的资源:resource5
获取-资源线程-3获取的资源:resource4
获取-资源线程-3获取的资源:resource3
获取-资源线程-3获取的资源:resource2
获取-资源线程-3获取的资源:resource1
获取-资源线程-3获取的资源:resource0
获取-资源线程-3获取资源完成,唤醒其他线程
获取-资源线程运行结束
获取-资源线程-4暂无资源释放锁
获取-资源线程启动
获取-资源线程-3拿到锁
获取-资源线程-3暂无资源释放锁
添加-资源线程-1添加完成,唤醒其他线程
添加-资源线程运行结束
添加-资源线程启动
添加-资源线程-1拿到锁
添加-资源线程-1资源未被消耗释放锁锁
添加-资源线程-4资源未被消耗释放锁锁
添加-资源线程-3资源未被消耗释放锁锁
添加-资源线程-2拿到锁
添加-资源线程-2资源未被消耗释放锁锁
获取-资源线程-1获取的资源:resource9
获取-资源线程-1获取的资源:resource8
获取-资源线程-1获取的资源:resource7
获取-资源线程-1获取的资源:resource6
获取-资源线程-1获取的资源:resource5
获取-资源线程-1获取的资源:resource4
获取-资源线程-1获取的资源:resource3
获取-资源线程-1获取的资源:resource2
获取-资源线程-1获取的资源:resource1
获取-资源线程-1获取的资源:resource0
获取-资源线程-1获取资源完成,唤醒其他线程
获取-资源线程运行结束
获取-资源线程启动
获取-资源线程-1拿到锁
获取-资源线程-1暂无资源释放锁
获取-资源线程-2暂无资源释放锁
添加-资源线程-2添加完成,唤醒其他线程
添加-资源线程运行结束
添加-资源线程启动
添加-资源线程-3资源未被消耗释放锁锁
添加-资源线程-4资源未被消耗释放锁锁
添加-资源线程-1资源未被消耗释放锁锁
获取-资源线程-3获取的资源:resource9
获取-资源线程-3获取的资源:resource8
获取-资源线程-3获取的资源:resource7
获取-资源线程-3获取的资源:resource6
获取-资源线程-3获取的资源:resource5
获取-资源线程-3获取的资源:resource4
获取-资源线程-3获取的资源:resource3
获取-资源线程-3获取的资源:resource2
获取-资源线程-3获取的资源:resource1
获取-资源线程-3获取的资源:resource0
获取-资源线程-3获取资源完成,唤醒其他线程
获取-资源线程运行结束
..............

5.5.5 Lock接口+Condition接口的线程通信

package threadCommunication;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * 线程操作资源
 */
public class Resource {

    /**
     * 线程操作的资源集合
     */
    private volatile List<String> resourceList = new ArrayList<>();

    private Lock lock;
    private Condition addCondition;
    private Condition subCondition;

    Resource(Lock lock) {
        this.lock = lock;
        addCondition = lock.newCondition();
        subCondition = lock.newCondition();
    }

    /**
     * 往资源集合里面添加资源
     */
    public void addResource() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "拿到锁");
            // 如果资源没有被消耗完,添加资源线程进入等待,等待资源消耗完
            while (resourceList.size() > 0) {
                System.out.println(Thread.currentThread().getName() + "资源未被消耗释放锁锁");
                addCondition.await();
            }
            int count = 0;
            while (true) {
                resourceList.add("resource" + count);
                count++;
                while (count == 10) {
                    // 资源添加完成唤醒消耗资源的线程,进行资源消耗
                    System.out.println(Thread.currentThread().getName() + "添加完成,唤醒其他线程");
                    addCondition.signal();
                    return;
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }

    /**
     * 获取资源集合里面的资源并删除资源
     *
     * @return
     */
    public void subResource() {
        String resourceStr = "";
        lock.lock();
        System.out.println(Thread.currentThread().getName() + "拿到锁");
        int count = 0;
        try {
            // 如果当前资源未添加或者资源被消耗完,获取资源线程进入等待
            while (resourceList.size() == 0) {
                System.out.println(Thread.currentThread().getName() + "暂无资源释放锁");
                subCondition.await();
            }
            count = resourceList.size() - 1;
            while (true) {
                resourceStr = resourceList.get(count);
                resourceList.remove(count);
                System.out.println(Thread.currentThread().getName() + "获取的资源:" + resourceStr);
                count--;
                while (count == -1) {
                    // 资源消耗完,唤醒添加资源线程
                    System.out.println(Thread.currentThread().getName() + "获取资源完成,唤醒其他线程");
                    subCondition.signal();
                    return;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


}
package threadCommunication;

/**
 * 添加资源线程
 */
public class AddThread implements Runnable {

    private Resource resource;

    private String threadName;

    AddThread(String threadName, Resource resource) {
        this.threadName = threadName;
        this.resource = resource;
    }

    @Override
    public void run() {
        // 无限循环添加资源
        while (true) {
            System.out.println(threadName + "启动");
            resource.addResource();
            System.out.println(threadName + "运行结束");
        }
    }
}
package threadCommunication;

/**
 * 获取资源的线程
 */
public class SubThread implements Runnable {

    private Resource resource;

    private String threadName;

    SubThread(String threadName, Resource resource) {
        this.threadName = threadName;
        this.resource = resource;
    }

    @Override
    public void run() {
        // 无限循环获取资源
        while (true) {
            System.out.println(threadName + "启动");
            resource.subResource();
            System.out.println(threadName + "运行结束");
        }
    }
}
package threadCommunication;

import java.util.concurrent.locks.ReentrantLock;

public class ThreadTest {


    public static void main(String[] args) throws Exception{
        ReentrantLock reentrantLock = new ReentrantLock();
        Resource resource = new Resource(reentrantLock);
        AddThread addThread = new AddThread("添加-资源线程",resource);
        SubThread subThread = new SubThread("获取-资源线程",resource);
        Thread thread1 = new Thread(addThread,"添加-资源线程-1");
        Thread thread2 = new Thread(addThread,"添加-资源线程-2");
        Thread thread3 = new Thread(addThread,"添加-资源线程-3");
        Thread thread4 = new Thread(addThread,"添加-资源线程-4");
        Thread thread5 = new Thread(subThread,"获取-资源线程-1");
        Thread thread6 = new Thread(subThread,"获取-资源线程-2");
        Thread thread7 = new Thread(subThread,"获取-资源线程-3");
        Thread thread8 = new Thread(subThread,"获取-资源线程-4");
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
        thread5.start();
        thread6.start();
        thread7.start();
        thread8.start();
    }
}

运行结果:

添加-资源线程启动
添加-资源线程-3拿到锁
添加-资源线程-3资源未被消耗释放锁锁
获取-资源线程-4获取的资源:resource9
获取-资源线程-4获取的资源:resource8
获取-资源线程-4获取的资源:resource7
获取-资源线程-4获取的资源:resource6
获取-资源线程-4获取的资源:resource5
获取-资源线程-4获取的资源:resource4
获取-资源线程-4获取的资源:resource3
获取-资源线程-4获取的资源:resource2
获取-资源线程-4获取的资源:resource1
获取-资源线程-4获取的资源:resource0
获取-资源线程-4获取资源完成,唤醒其他线程
获取-资源线程运行结束
获取-资源线程启动
获取-资源线程-4拿到锁
获取-资源线程-4暂无资源释放锁
添加-资源线程-2添加完成,唤醒其他线程
添加-资源线程运行结束
添加-资源线程启动
添加-资源线程-2拿到锁
添加-资源线程-2资源未被消耗释放锁锁
获取-资源线程-2获取的资源:resource9
获取-资源线程-2获取的资源:resource8
获取-资源线程-2获取的资源:resource7
获取-资源线程-2获取的资源:resource6
获取-资源线程-2获取的资源:resource5
获取-资源线程-2获取的资源:resource4
获取-资源线程-2获取的资源:resource3
获取-资源线程-2获取的资源:resource2
获取-资源线程-2获取的资源:resource1
获取-资源线程-2获取的资源:resource0
获取-资源线程-2获取资源完成,唤醒其他线程
获取-资源线程运行结束
获取-资源线程启动
获取-资源线程-2拿到锁
获取-资源线程-2暂无资源释放锁
添加-资源线程-4添加完成,唤醒其他线程
添加-资源线程运行结束
添加-资源线程启动
获取-资源线程-3获取的资源:resource9
获取-资源线程-3获取的资源:resource8
获取-资源线程-3获取的资源:resource7
获取-资源线程-3获取的资源:resource6
获取-资源线程-3获取的资源:resource5
获取-资源线程-3获取的资源:resource4
获取-资源线程-3获取的资源:resource3
获取-资源线程-3获取的资源:resource2
获取-资源线程-3获取的资源:resource1
获取-资源线程-3获取的资源:resource0
获取-资源线程-3获取资源完成,唤醒其他线程
获取-资源线程运行结束

5.5.6 PipedInputStream与PipedOutputStream、PipedReader与PipedWriter管道流之间线程通信
5.5.6.1 PipedInputStream与PipedOutputStream

package threadCommunication;


import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

/**
 * 线程操作资源
 */
public class Resource {


    Resource() {
    }

    /**
     * 往资源集合里面添加资源
     */
    public void read(PipedInputStream pipedInputStream) {
        byte[] readBytes = new byte[1024];
        try {
            pipedInputStream.read(readBytes);
            System.out.println(new String(readBytes, 0, readBytes.length));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (pipedInputStream != null) {
                try {
                    pipedInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }


    /**
     * 获取资源集合里面的资源并删除资源
     *
     * @return
     */
    public void write(PipedOutputStream pipedOutputStream) {
        String writeData = "Day Day Up";
        try {
            byte[] writeDataBytes = writeData.getBytes();
            pipedOutputStream.write(writeDataBytes);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (pipedOutputStream != null) {
                try {
                    pipedOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


}
package threadCommunication;

import java.io.PipedInputStream;

/**
 * 添加资源线程
 */
public class ReadThread implements Runnable {

    private Resource resource;

    private String threadName;

    private PipedInputStream pipedInputStream;

    ReadThread(String threadName, Resource resource, PipedInputStream pipedInputStream) {
        this.threadName = threadName;
        this.resource = resource;
        this.pipedInputStream = pipedInputStream;
    }

    @Override
    public void run() {
        resource.read(pipedInputStream);
    }
}
package threadCommunication;

import java.io.PipedOutputStream;

/**
 * 获取资源的线程
 */
public class WriteThread implements Runnable {

    private Resource resource;

    private String threadName;

    private PipedOutputStream pipedOutputStream;

    WriteThread(String threadName, Resource resource, PipedOutputStream pipedOutputStream) {
        this.threadName = threadName;
        this.resource = resource;
        this.pipedOutputStream = pipedOutputStream;
    }

    @Override
    public void run() {
        resource.write(pipedOutputStream);
    }
}
package threadCommunication;

import java.io.PipedInputStream;
import java.io.PipedOutputStream;

public class ThreadTest {


    public static void main(String[] args) throws Exception {
        Resource resource = new Resource();
        PipedInputStream pipedInputStream = new PipedInputStream();
        PipedOutputStream pipedOutputStream = new PipedOutputStream();
        WriteThread writeThread = new WriteThread("写-资源线程", resource, pipedOutputStream);
        ReadThread readThread = new ReadThread("读-资源线程", resource, pipedInputStream);
        Thread thread1 = new Thread(writeThread, "写-资源线程-1");
        Thread thread2 = new Thread(readThread, "读-资源线程-1");
        pipedOutputStream.connect(pipedInputStream);
        thread1.start();
        thread2.start();
    }
}

运行结果:

Day Day Up    

5.5.6.2 PipedReader与PipedWriter线程通信

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  
package threadCommunication;


import java.io.IOException;
import java.io.PipedReader;
import java.io.PipedWriter;

/**
 * 线程操作资源
 */
public class Resource {


    Resource() {
    }

    /**
     * 往资源集合里面添加资源
     */
    public void read(PipedReader pipedReader) {
        char[] readCharArr = new char[1024];

        try {
            pipedReader.read(readCharArr);
            System.out.println(new String(readCharArr, 0, readCharArr.length));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (pipedReader != null) {
                try {
                    pipedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }


    /**
     * 获取资源集合里面的资源并删除资源
     *
     * @return
     */
    public void write(PipedWriter pipedWriter) {
        String writeData = "今天真开心!";
        try {
            char[] writeDataBytes = writeData.toCharArray();
            pipedWriter.write(writeDataBytes);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (pipedWriter != null) {
                try {
                    pipedWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


}
package threadCommunication;

import java.io.PipedReader;

/**
 * 添加资源线程
 */
public class ReadThread implements Runnable {

    private Resource resource;

    private String threadName;

    private PipedReader pipedReader;

    ReadThread(String threadName, Resource resource, PipedReader pipedReader) {
        this.threadName = threadName;
        this.resource = resource;
        this.pipedReader = pipedReader;
    }

    @Override
    public void run() {
        resource.read(pipedReader);
    }
}
package threadCommunication;

import java.io.PipedWriter;

/**
 * 获取资源的线程
 */
public class WriteThread implements Runnable {

    private Resource resource;

    private String threadName;

    private PipedWriter pipedWriter;

    WriteThread(String threadName, Resource resource, PipedWriter pipedWriter) {
        this.threadName = threadName;
        this.resource = resource;
        this.pipedWriter = pipedWriter;
    }

    @Override
    public void run() {
        resource.write(pipedWriter);
    }
}
package threadCommunication;

import java.io.PipedReader;
import java.io.PipedWriter;

public class ThreadTest {


    public static void main(String[] args) throws Exception {
        Resource resource = new Resource();
        PipedReader pipedReader = new PipedReader();
        PipedWriter pipedWriter = new PipedWriter();
        WriteThread writeThread = new WriteThread("写-资源线程", resource, pipedWriter);
        ReadThread readThread = new ReadThread("读-资源线程", resource, pipedReader);
        Thread thread1 = new Thread(writeThread, "写-资源线程-1");
        Thread thread2 = new Thread(readThread, "读-资源线程-1");
        pipedWriter.connect(pipedReader);
        thread1.start();
        thread2.start();
    }
}

运行结果:

今天真开心! 

这里管道流我先做了解,等整理到io流那边再详细看一下。
注:内容参考网络上各种资料,还有一些本人的理解和思想,仅为了学习记录和分享一下自己所学之处,如有不足的地方麻烦大牛指出,如有侵权的地方,请联系删除,谢谢。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值