您的位置:首页 > 编程语言 > Java开发

java多线程 线程创建、控制、同步、通信

2020-08-09 15:13 106 查看

这里写目录标题

  • 二、线程的创建和启动
  • 三、线程的生命周期
  • 四、控制线程
  • 五、线程同步
  • 六、线程通信
  • 多线程

    一、进程与线程

    1、进程

    • 定义:

      进程是正在运行的程序的实例
    • 进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元
  • 特点(特性):

      动态性:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动态消亡的。

    • 并发性:任何进程都可以同其他进程一起并发执行

    • 独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位;

    • 异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进

    • 结构特征:进程由程序、数据和进程控制块三部分组成。

  • 基本状态:

      1)就绪状态(Ready)

      进程已获得除处理器外的所需资源,等待分配处理器资源;只要分配了处理器进程就可执行。就绪进程可以按多个优先级来划分队列。例如,当一个进程由于时间片用完而进入就绪状态时,排入低优先级队列;当进程由I/O操作完成而进入就绪状态时,排入高优先级队列。

    • **2)运行状态(Running):

      进程占用处理器资源;处于此状态的进程的数目小于等于处理器的数目。在没有其他进程可以执行时(如所有进程都在阻塞状态),通常会自动执行系统的空闲进程。

    • *** 3)**阻塞状态(Blocked):

      由于进程等待某种条件(如I/O操作或进程同步),在条件满足之前无法继续执行。该事件发生前即使把处理器资源分配给该进程,也无法运行。

    2、线程

    • 定义:

      线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
    • 同一进程中的多条线程将共享该进程中的全部系统资源
  • 特点:

      允许跨线程资源共享
    • 创建线程的开销要比进程小
    • 提高进程运行的效率
  • 状态:

      新建态
    • 就绪态(可运行态)
    • 运行态
    • 阻塞态
    • 死亡态
  • 线程与进程的关系:

      线程与资源分配无关,它属于某一个进程,并与进程内的其他线程一起共享进程的资源
    • 线程被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务
  • 线程与进程的区别:

      地址空间和其它资源:

      进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。

    1. 通信:

      进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。

    2. 调度和切换:

      线程上下文切换比进程上下文切换要快得多。

    3、举例详细说明

    • 工厂与工人的关系:工厂为进程,工人为线程

    • 工厂中有多个生产流水线:每条流水线上有许多工人,流水线为进程,工人为线程

    • 工业园与工厂:工业园为进程,工厂为线程

    二、线程的创建和启动

    java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例

    1、继承Thread类创建线程类

    • 定义Thread类的子类,并重写该类的run()方法,run()方法的方法体就代表了线程需要完成的任务。

    • 创建Thread子类的实例,即创建了线程对象(new 子类())

    • 调用线程对象的start()方法来创建并启动该线程

      public class MyThread extends Thread {
      @Override
      public void run() {
      System.out.println("线程Id=" + this.getId() + "\t线程名=" + this.getName());
      }
      public static void main(String[] args) {
      MyThread thread = new MyThread();
      thread.start();
      }
      }

    2、实现Runnable接口创建线程类

    • 定义Runnable接口的实现类,并重写该接口的run()方法,run()方法的方法体就代表了线程需要完成的任务。

    • 创建Runnable接口的实现类的实例,并以此作为Thread的target来创建Thread对象,此对象即为线程对象

    • 调用线程对象的start()方法来创建并启动该线程

      public class MyRunnable implements Runnable {
      @Override
      public void run() {
      System.out.println("线程Id=" + Thread.currentThread().getId()+"\t线程名="+Thread.currentThread().getName());
      }
      
      public static void main(String[] args) {
      MyRunnable runnable = new MyRunnable();
      Thread thread = new Thread(runnable);
      thread.start();
      }
      }
    • Thread.currentThread():currentThread()是Thread类的静态方法(类方法),该方法会返回当前正在执行的线程对象

    • getId(),getName(),是是Thread类的实例方法(成员方法),该方法会返回当前线程对象的id,name

    实现Runnable接口创建线程类是通过实现一个接口,并以此实现类作为target,Thread类的属性(构造器的参数)来创建线程对象,所以可以以匿名内部类的形式创建线程对象,java1.8后更可以使用lambda表达式书写
    public class MyRunnable {
    
    public static void main(String[] args) {
    Thread thread1 = new Thread(new Runnable() {
    @Override
    public void run() {
    System.out.println("线程Id=" + Thread.currentThread().getId()+"\t线程名="+Thread.currentThread().getName());
    }
    });
    thread1.start();
    
    Thread thread2 = new Thread(()->{
    System.out.println("线程Id=" + Thread.currentThread().getId()+"\t线程名="+Thread.currentThread().getName());
    });
    }
    }

    3、使用Callable和Future创建线程

    • 分析一下Callable和Future类结构关系
    public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
    }
    
    public class FutureTask<V> implements RunnableFuture<V> {
    ....
    }
    
    public interface Callable<V> {
    V call() throws Exception;
    }
    • 创建Callable接口的实现类,并重写call()方法,再创建Callable实现类的实例(对象)

      call就是子线程执行的主体
    • 类似于run方法,区别run方法 call方法有返回值
    • call可以抛出异常
  • 使用FutureTask类来包装Callable对象,创建FutureTask类的对象作为Thread对象的target创建并启动新线程

  • 可通过FutureTask类的对象的get()方法获取子线程执行结束后返回的结果,即call()方法的返回结果

    public class MyCallable implements Callable<Long> {
    @Override
    public Long call() throws Exception {
    System.out.println("子线程Id=" + Thread.currentThread().getId() +
    "\t子线程名=" + Thread.currentThread().getName());
    return Thread.currentThread().getId();
    }
    }
    public class MainTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    //写法一
    FutureTask<Long> task = new FutureTask<>(new MyCallable());
    new Thread(task).start();
    long threadId = task.get();
    System.out.println("Callable返回值为:" + threadId);
    }
    }
    public class MainTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    //写法二
    FutureTask<Long> task1 = new FutureTask<>(() -> {
    System.out.println("子线程Id=" + Thread.currentThread().getId() +
    "\t子线程名=" + Thread.currentThread().getName());
    return Thread.currentThread().getId();
    });
    new Thread(task1).start();
    long threadId1 = task1.get();
    System.out.println("Callable返回值为:" + threadId1);
    }
    }
  • 4、创建线程三种方式的比较

    通过继承Thread类或实现Runnable、Callable接口都可以实现多线程,其中实现Runnable和Callable接口方式类似,可将实现Callable接口的方式视为实现Runnable接口的加强,Callable有返回值可抛出异常。所以创建线程方式的比较主要是看继承Thread类与实现接口类两类方式的优缺点。

    • 继承Thread类方式:

      优势:访问当前线程对象可用this代替Tread.currentThread(),编写简单。(真没找出别的优势了。。。)
    • 劣势:java中只能单一继承,不能继承其他父类
  • 实现接口的方式:

      优势:可继承其他父类;多个线程对象可共享一个target对象(适用于多个相同线程处理同一份资源)
    • 劣势:编写复杂,如果要访问当前线程对象,必须使用Tread.currentThread(),
  • 推荐使用实现接口的方式,需要返回值实现Callable,不需要返回值实现Runnable

  • 三、线程的生命周期

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x29E10Ip-1596957064223)(C:\Users\freedom\Desktop\笔记\image-20200806155309894.png)]

    (我也忘了图是哪找的了。。。。网上找的,如有侵权,联系我删掉)

    • 新建态
    • 就绪态(可运行态)
    • 运行态
    • 死亡态
    • 阻塞态: IO流(等待用户输入)
    • Thread.sleep
    • t.join
    • wait()

    关于更多的线程的生命周期,可以看这个链接中的内容

    https://www.cnblogs.com/xidongyu/p/10962657.html

    四、控制线程

    1、join线程:join();

    Thread提供了一个让一个线程等待另一个线程完成的方法join方法。

    public final void join() throws InterruptedException等待这个线程死亡。
    /**
    * @author yhp
    * @CreateDate 2020/8/7 - 10:29
    * @Description: join为线程的方法,在哪个线程中调用则阻塞哪个线程,
    * 在thread0线程中调用thread1的join方法则阻塞thread0线程,等待thread1线程运行死亡
    */
    public class TestJoin {
    public static void main(String[] args) throws InterruptedException {
    Thread threadMain = Thread.currentThread();
    System.out.println(Thread.currentThread().getName() + "开始执行");
    Thread thread = new Thread(() -> {
    for (int i = 0; i < 5; i++) {
    try {
    //子线程thread中,调用主线程的join方法,则阻塞当前子线程thread,主线程运行死亡后才执行当前线程
    //threadMain.join();
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    
    System.out.println("thread正在执行。。。。");
    }
    System.out.println("thread执行结束/");
    });
    thread.start();
    //主线程中调用子线程thread的join方法,则阻塞主线程,子线程thread死亡后主线程才再次运行
    //        thread.join();
    
    for (int i = 0; i < 5; i++) {
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println( "main正在执行。。。。");
    }
    System.out.println("main执行结束/");
    }
    
    }

    2、后台线程:thread.setDaemon(true);

    • 后台线程,又称为“守护线程”或”精灵线程“。
    • 后台线程在后台运行,他的任务是为其他线程提供服务的,所有非后台线程称为前台线程,前台线程结束后后台线程会跟随的结束,无论后台线程是否运行结束。
    public class DaemonThread implements Runnable {
    @Override
    public void run() {
    //后台线程循环100次,每次1秒
    for (int i = 0; i < 100; i++) {
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println(Thread.currentThread().getName() + "\t" + i);
    }
    }
    
    public static void main(String[] args) throws InterruptedException {
    Thread thread = new Thread(new DaemonThread());
    thread.start();
    //将此线程设置成后台线程
    thread.setDaemon(true);
    //启动后台线程
    thread.start();
    
    //主线程继续运行,主线程循环5次,每次1秒
    for (int i = 0; i < 5; i++) {
    Thread.sleep(1000);
    System.out.println(Thread.currentThread().getName() + "\t" + i);
    }
    //主线程到此运行结束进入死亡态,同时后台进程也随之进入死亡态(无论后台线程是否运行完)
    }
    }

    3、线程睡眠:sleep()

    • Thread类的类方法(静态方法),线程睡眠可使线程从运行态进入阻塞态。
    public static void sleep(long millis)throws InterruptedException
    • 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。 线程不会丢失任何显示器的所有权。

    • 参数 millis - 以毫秒为单位的睡眠时间长度

    public static void sleep(long millis,int nanos) throws InterruptedException
    • 导致正在执行的线程以指定的毫秒数加上指定的纳秒数来暂停(临时停止执行),这取决于系统定时器和调度器的精度和准确性。 线程不会丢失任何显示器的所有权。
    • 参数
      millis
      - 以毫秒为单位的睡眠时间长度
    • nanos
      -
      0-999999
      额外的纳秒睡眠

    4、线程让步:yieId

    • Thread类的类方法(静态方法),对调度程序的一个暗示,即当前线程愿意让出cpu资源,重新与其他线程争夺cpu资源分配
    public static void yield()
    • 对调度程序的一个暗示,即当前线程愿意产生当前使用的处理器。

    5、线程优先级

    • 每个线程执行时都具有一定的优先级,优先级高的线程获得cpu资源的几率更大,优先级低的线程获得cpu资源的几率更小。(注:优先级的高度只影响几率,并非一定高的先执行

    • 每个线程默认的优先级玉创建它的父线程优先级相同。默认情况下,main线程具有普通优先级,由main线程创建的子线程也具有普通优先级。(注:main线程优先级默认为5

    • Thread类有两个成员方法,setPriority(int newPriority)、getPriority(),分别为设置优先级和获取指定线程的优先级。

    • 优先级范围:1~10

      Thread.MAX_PRIORITY:10 最高优先级
    • Thread.MIN_PRIORITY: 1 最低优先级
    • Thread.NORM_PRIORITY:5 普通优先级

    五、线程同步

    1、同步代码块

    多个用户同时对一个账户进行取款操作:

    • 账户实体类(一个普通的实体javaBean)

      public class Account implements Serializable {
      private Integer id;
      private String name;
      private Double money;
      
      public Account() {
      }
      
      public Account(Integer id, String name, Double money) {
      this.id = id;
      this.name = name;
      this.money = money;
      }
      
      public Integer getId() {
      return id;
      }
      
      public void setId(Integer id) {
      this.id = id;
      }
      
      public String getName() {
      return name;
      }
      
      public void setName(String name) {
      this.name = name;
      }
      
      public Double getMoney() {
      return money;
      }
      
      public void setMoney(Double money) {
      this.money = money;
      }
      
      @Override
      public String toString() {
      return "Account{" +
      "id=" + id +
      ", name='" + name + '\'' +
      ", money=" + money +
      '}';
      }
      }
    • 线程类,在run()方法中,使用synchronized关键字,创建代码块

      public class AccountThread extends Thread {
      private Account account;
      private Double money;
      private Thread thread;
      
      public AccountThread() {
      }
      
      public AccountThread(Account account, Double money) {
      this.account = account;
      this.money = money;
      }
      
      public void setThread(Thread thread) {
      this.thread = thread;
      }
      
      @Override
      public void run() {
      synchronized (account) {
      if (account.getMoney() >= money) {
      double newMoney = account.getMoney() - money;
      try {
      Thread.sleep(1000);
      } catch (InterruptedException e) {
      e.printStackTrace();
      }
      account.setMoney(newMoney);
      System.out.println("账户名=" + account.getName() + "\t取款额=" + money + "\t账户余额=" + account.getMoney());
      } else {
      System.out.println("账户名=" + account.getName() + "余额不足");
      }
      }
      }
      }
    • 测试类

      public static void main(String[] args) throws InterruptedException {
      Account account = new Account(1008601,"张三",1000d);
      Thread thread1 = new AccountThread(account,200d);
      Thread thread2 = new AccountThread(account,800d);
      thread1.start();
      //        thread1.join();
      thread2.start();
      
      Scanner in = new Scanner(System.in);
      in.next();
      System.out.println("---");
      
      }
      }

    2、同步方法

    • 实体类,与同步代码块不同的是,同步方法时用synchronized修饰方法,在实体类中创建同步方法

      public class Account implements Serializable {
      private Integer id;
      private String name;
      private Double money;
      
      public synchronized void deposit(Double money) {
      if (this.getMoney() >= money) {
      double newMoney = this.getMoney() - money;
      try {
      //睡眠1秒
      Thread.sleep(1000);
      } catch (InterruptedException e) {
      e.printStackTrace();
      }
      this.setMoney(newMoney);
      System.out.println("账户名=" + this.getName() + "\t取款额=" + money + "\t账户余额=" + this.getMoney());
      } else {
      System.out.println("账户名=" + this.getName() + "余额不足");
      }
      }
      
      public Account() {
      }
      
      public Account(Integer id, String name, Double money) {
      this.id = id;
      this.name = name;
      this.money = money;
      }
      
      public Integer getId() {
      return id;
      }
      
      public void setId(Integer id) {
      this.id = id;
      }
      
      public String getName() {
      return name;
      }
      
      public void setName(String name) {
      this.name = name;
      }
      
      public Double getMoney() {
      return money;
      }
      
      public void setMoney(Double money) {
      this.money = money;
      }
      
      @Override
      public String toString() {
      return "Account{" +
      "id=" + id +
      ", name='" + name + '\'' +
      ", money=" + money +
      '}';
      }
      }
    • 线程类

      public class AccountThread extends Thread {
      private Account account;
      private Double money;
      
      public AccountThread(Account account, Double money) {
      this.account = account;
      this.money = money;
      }
      @Override
      public void run() {
      account.deposit(money);
      }
      }
    • 测试类

      public class MainTest {
      public static void main(String[] args) throws InterruptedException {
      Account account = new Account(1008601,"张三",1000d);
      Thread thread1 = new AccountThread(account,200d);
      Thread thread2 = new AccountThread(account,800d);
      thread1.start();
      //        thread1.join();
      thread2.start();
      Scanner in = new Scanner(System.in);
      in.next();
      System.out.println("---");
      }
      }

    3、同步锁

    同步锁方法与同步方法的线程类与测试类相同

    • 实体类

      public class Account implements Serializable {
      private Integer id;
      private String name;
      private Double money;
      
      //定义一个可重现锁
      private final ReentrantLock lock = new ReentrantLock();
      
      public void deposit(Double money) {
      //加锁
      lock.lock();
      if (this.getMoney() >= money) {
      double newMoney = this.getMoney() - money;
      try {
      //睡眠1秒
      Thread.sleep(1000);
      } catch (InterruptedException e) {
      e.printStackTrace();
      }
      this.setMoney(newMoney);
      System.out.println("账户名=" + this.getName() + "\t取款额=" + money + "\t账户余额=" + this.getMoney());
      } else {
      System.out.println("账户名=" + this.getName() + "余额不足");
      }
      //解锁
      lock.unlock();
      }
      
      public Account() {
      }
      
      public Account(Integer id, String name, Double money) {
      this.id = id;
      this.name = name;
      this.money = money;
      }
      
      public Integer getId() {
      return id;
      }
      
      public void setId(Integer id) {
      this.id = id;
      }
      
      public String getName() {
      return name;
      }
      
      public void setName(String name) {
      this.name = name;
      }
      
      public Double getMoney() {
      return money;
      }
      
      public void setMoney(Double money) {
      this.money = money;
      }
      
      @Override
      public String toString() {
      return "Account{" +
      "id=" + id +
      ", name='" + name + '\'' +
      ", money=" + money +
      '}';
      }
      }

    4、死锁

    实例:

    public class Test {
    public static String objA = "strA";
    public static String objB = "strB";
    
    public static void main(String[] args) {
    Thread thread1 = new Thread(new Lock1());
    Thread thread2 = new Thread(new Lock2());
    thread1.start();
    thread2.start();
    }
    
    }
    
    class Lock1 extends Thread{
    @Override
    public void run(){
    try{
    System.out.println("Lock1线程正在运行");
    while(true){
    synchronized(Test.objA){
    System.out.println("Lock1线程获得并持有了Test.objA的锁");
    Thread.sleep(1000);
    System.out.println("Lock1等待Test.objB的锁...");
    synchronized(Test.objB){
    System.out.println("Lock1获得并持有了Test.objB的锁");
    }
    }
    }
    }catch(Exception e){
    e.printStackTrace();
    }
    }
    
    }
    
    class Lock2 extends Thread{
    @Override
    public void run() {
    try{
    System.out.println("Lock2线程正在运行");
    while(true){
    synchronized(Test.objB){
    System.out.println("Lock2线程获得并持有了Test.objB的锁");
    Thread.sleep(1000);
    System.out.println("Lock2等待Test.objA的锁...");
    synchronized(Test.objA){
    System.out.println("Lock2线程获得并持有了Test.objA的锁");
    }
    }
    }
    }catch(Exception e){
    e.printStackTrace();
    }
    }
    }

    六、线程通信

    下面将针对一个账户的取款,存款的操作描述线程间的通信。(同一个账户,多人存款最多同时存在一笔存款,一人取款,有存款方可取)

    1、test1_synchronized

    • 实体类

      /**
      * @author yhp
      * @CreateDate 2020/8/9 - 9:00
      * @Description: 线程间的通信,使用synchronized修饰的同步方法,账户实体类
      */
      public class Account {
      private String accountId;   //账户id
      private double balance;     //账户余额
      
      private boolean flag = false;   //标识账户中是否已有存款的旗标
      
      public Account() {    }
      
      public Account(String accountId, double balance) {
      this.accountId = accountId;
      this.balance = balance;
      }
      
      public double getBalance() {        return balance;    }
      
      public String getAccountId() {        return accountId;    }
      
      public void setAccountId(String accountId) {        this.accountId = accountId;    }
      
      @Override
      public String toString() {
      return "Account{" +
      "accountId='" + accountId + '\'' +
      ", balance=" + balance +
      ", flag=" + flag +
      '}';
      }
      
      //取款方法
      public synchronized void draw(double money) {
      try {
      //判断账户中是否有存款,若无则阻塞,使用while循环,确保本次取钱成功为止
      while (!flag) {
      Thread.yield();
      wait();
      }
      if (flag) {
      Thread.sleep(10);
      //取钱
      System.out.print(Thread.currentThread().getName() + "取钱:" + money);
      balance -= money;
      System.out.println("\t账户余额=" + balance + "\n");
      //唤醒其他线程,将标识符改为false
      flag = false;
      
      notifyAll();
      }
      } catch (InterruptedException e) {
      e.printStackTrace();
      }
      }
      
      //存款方法
      public synchronized void deposit(double money) {
      try {
      //判断账户中是否有存款,若有则阻塞,使用while循环,确保本次存钱成功为止
      while (flag) {
      Thread.yield();
      wait();
      }
      if (!flag) {
      //存钱
      System.out.print(Thread.currentThread().getName() + "存钱:" + money);
      balance += money;
      System.out.println("\t账户余额=" + balance);
      //唤醒其他线程,将标识符改为true
      flag = true;
      notifyAll();
      }
      } catch (InterruptedException e) {
      e.printStackTrace();
      }
      }
      
      }
    • 线程类,分为取款线程与存款线程

      public class DepositThread extends Thread {
      private Account account;    //用户账户
      private double money;       //取款金额
      
      public DepositThread(String name,Account account, double money) {
      super(name);
      this.account = account;
      this.money = money;
      }
      
      @Override   //存钱操作
      public void run() {
      for (int i = 0; i < 10; i++) {
      account.deposit(money);
      }
      }
      }
      public class DrawThread extends Thread{
      private Account account;    //用户账户
      private double money;       //取款金额
      
      public DrawThread(String name,Account account, double money) {
      super(name);
      this.account = account;
      this.money = money;
      }
      
      @Override   //取钱操作
      public void run() {
      for (int i = 0; i < 30; i++) {
      account.draw(money);
      }
      }
      }
    • 测试类

      public class MainTest {
      public static void main(String[] args) {
      Account account = new Account("10086",0d);
      new DrawThread("取钱A",account,10000d).start();
      new DepositThread("A他爸",account,10000d).start();
      new DepositThread("A他妈",account,10000d).start();
      new DepositThread("A他爷",account,10000d).start();
      
      }
      }

    2、test2_Condiition

    • 实体类与同步方法相同

      /**
      * @author yhp
      * @CreateDate 2020/8/9 - 9:00
      * @Description: 线程间的通信,使用synchronized修饰的同步代码块,账户实体类
      */
      public class Account {
      private String accountId;   //账户id
      private double balance;     //账户余额
      private boolean flag = false;   //标识账户中是否已有存款的旗标
      //显示定义Lock对象
      private final Lock lock = new ReentrantLock();
      //获取指定lock对象对应的Condition
      private final Condition condition = lock.newCondition();
      
      public Account() {    }
      public Account(String accountId, double balance) {
      this.accountId = accountId;
      this.balance = balance;
      }
      public double getBalance() {        return balance;    }
      
      public String getAccountId() {        return accountId;    }
      
      public void setAccountId(String accountId) {        this.accountId = accountId;    }
      
      @Override
      public String toString() {
      return "Account{" +
      "accountId='" + accountId + '\'' +
      ", balance=" + balance +
      ", flag=" + flag +
      '}';
      }
      
      //取款方法
      public void draw(double money) {
      //加锁
      lock.lock();
      try {
      //判断账户中是否有存款,若无则阻塞,使用while循环,确保本次取钱成功为止
      while (!flag) {
      Thread.yield();
      condition.await();
      }
      if (flag){
      //取钱
      System.out.print(Thread.currentThread().getName() + "取钱:" + money);
      balance -= money;
      System.out.println("\t账户余额=" + balance + "\n");
      //唤醒其他线程,将标识符改为false
      flag = false;
      condition.signalAll();
      }
      }catch (InterruptedException e){
      e.printStackTrace();
      }finally {
      //解锁
      lock.unlock();
      }
      }
      
      //存款方法
      public synchronized void deposit(double money) {
      //加锁
      lock.lock();
      try {
      //判断账户中是否有存款,若有则阻塞,使用while循环,确保本次存钱成功为止
      while (flag) {
      Thread.yield();
      condition.await();
      }
      if(!flag){
      //存钱
      System.out.print(Thread.currentThread().getName() + "存钱:" + money);
      balance += money;
      System.out.println("\t账户余额=" + balance);
      //唤醒其他线程,将标识符改为true
      flag = true;
      condition.signalAll();
      }
      }catch (InterruptedException e){
      e.printStackTrace();
      }finally {
      //解锁
      lock.unlock();
      }
      }
      }

    3、test3_阻塞队列生产者消费者模式,生产西瓜

    • 生产者

      //生产者 每100ms生产一个西瓜放入框中,一个筐能装10个瓜,装满后运给消费者消费
      public class Producer extends Thread {
      private Integer watermelon = 2;     //西瓜
      private Integer[] frame = new Integer[5];
      private long time;  //生产2个瓜消费的时间
      
      private BlockingQueue sharedQueue;  //运送西瓜的队列
      
      public Producer(String name,long time,BlockingQueue sharedQueue) {
      super(name);
      this.time = time;
      this.sharedQueue = sharedQueue;
      }
      
      @Override
      public void run() {
      //生产者一直生产西瓜,最多生产5框
      while (true){
      try {
      //每次生产2个西瓜,10个一筐
      for (int i = 0; i < 5; i++) {
      Thread.sleep(time);
      frame[i] = watermelon;
      
      if(frame[4] == watermelon){
      System.out.println(this.getName() + "升产了10个西瓜并将西瓜运往消费者------");
      sharedQueue.put(frame);
      Thread.sleep(100);
      frame[4] = 0;
      }
      }
      } catch (InterruptedException e) {
      e.printStackTrace();
      }
      }
      }
      }
    • 消费者

      public class Consumer extends Thread {
      private Integer[] frame;    //筐
      private BlockingQueue sharedQueue;  //运送西瓜的队列
      
      private Lock lock = new ReentrantLock();
      
      public Consumer(BlockingQueue sharedQueue) {
      this.sharedQueue = sharedQueue;
      }
      
      @Override
      public void run() {
      //消费者者一直在消费西瓜
      while (true) {
      try {
      Thread.sleep(100);
      //取出队列中的筐
      frame = (Integer[]) sharedQueue.take();
      System.out.println("消费者消费了一筐10个西瓜,队列中剩余" + sharedQueue.size() + "筐西瓜\n");
      
      } catch (InterruptedException e) {
      e.printStackTrace();
      }
      }
      
      }
      }
    • 测试类

      public class MainTest {
      public static void main(String[] args) {
      //阻塞队列,一次队列最多运算2筐西瓜
      BlockingQueue sharedQueue = new LinkedBlockingDeque(2);
      
      Thread consumer = new Consumer(sharedQueue);
      Thread producer1 = new Producer("张三",100l,sharedQueue);
      consumer.start();
      producer1.start();
      }
      }
    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: 
    相关文章推荐