使用CountDownLatch模拟王者荣耀玩家进度加载

香编程~

加个“星标”,每日良时,好文必达呀!!

使用CountDownLatch模拟王者荣耀玩家进度加载

终于抽出时间来更新啦。。。

今天要分享的内容是CountDownLatch的使用以及如何通过CountDownLatch来模拟王者荣耀玩家进度条加载


1、CountDownLatch的使用

1.1 CountDownLatch简介

CountDownLatch是Java并发编程中常用的一个同步工具类,主要用于协调多个线程之间的同步,实现线程间的通信。简单点来讲就是用来进行线程同步协作,等待所有线程完成倒计时

1.2 CountDownLatch的用法以及实现原理

CountDownLatch是继承了AbstractQueuedSynchronizer(AQS)同步器类

CountDownLatch的实现原理:

  • 让一个线程等待其他多个线程完成各自的任务后再继续执行。在这种情况下,可以将CountDownLatch的计数器初始化为需要等待的线程数量。每当一个线程完成自己的任务后,就调用countDown()方法将计数器的值减1。当计数器的值变为0时,在CountDownLatch上await()的线程就会被唤醒,继续执行后续的任务。

  • 实现多个线程同时开始执行任务的最大并行性。这类似于赛跑中的发令枪响,将多个线程放到起点,等待发令枪响后同时开始执行。在这种情况下,可以将CountDownLatch的计数器初始化为需要等待的线程数量,并在每个线程开始执行任务前调用await()方法。当所有线程都调用await()方法后,它们会一起等待计数器的值变为0,然后同时开始执行任务。

需要注意的是,CountDownLatch的计数器只能被设置一次,且不能重新设置。此外,在使用CountDownLatch时,需要确保在finally块中调用countDown()方法,以避免死锁的发生。

CountDownLatch的用法:其中构造参数用来初始化等待计数值(一般就是你的线程个数),await() 用来等待计数归零,countDown() 用来让计数减一。

CountDownLatch的构造方法:

  CountDownLatch countDownLatch = new CountDownLatch(4);

对应调用源码

  public CountDownLatch(int count) {
       if (count < 0) throw new IllegalArgumentException("count < 0");
       this.sync = new Sync(count);
  }

CountDownLatch的countDown()方法:

  CountDownLatch countDownLatch = new CountDownLatch(4);
 countDownLatch.countDown();

对应调用源码主要就是tryReleaseShared()这个方法,让state的值减1

  public void countDown() {
       sync.releaseShared(1);
  }

public final boolean releaseShared(int arg) {
       if (tryReleaseShared(arg)) {
           doReleaseShared();
           return true;
      }
       return false;
  }

// 主要就是这个方法,让state的值减1
protected boolean tryReleaseShared(int releases) {
           // Decrement count; signal when transition to zero
           for (;;) {
               int c = getState();
               if (c == 0)
                   return false;
               int nextc = c-1;
               if (compareAndSetState(c, nextc))
                   return nextc == 0;
          }
  }

CountDownLatch的await()方法:

    CountDownLatch countDownLatch = new CountDownLatch(4);
       try {
           countDownLatch.await();
      } catch (InterruptedException e) {
           throw new RuntimeException(e);
      }

对应源码主要关注tryAcquireShared()这个方法,判断state的值是否为0,如果为0返回正1,不在执行下面的阻塞方法

  public void await() throws InterruptedException {
       sync.acquireSharedInterruptibly(1);
  }

public final void acquireSharedInterruptibly(int arg)
           throws InterruptedException {
       if (Thread.interrupted())
           throw new InterruptedException();
       // 这里判断是否继续阻塞
       if (tryAcquireShared(arg) < 0)
           doAcquireSharedInterruptibly(arg);
  }
// 这里判断state是否为0 如果为0返回1
protected int tryAcquireShared(int acquires) {
           return (getState() == 0) ? 1 : -1;
  }

1.3 CountDownLatch的简单使用

  public static void test1() throws InterruptedException {
       CountDownLatch latch = new CountDownLatch(3);
       new Thread(() -> {
           System.out.println("begin...");
           try {
               sleep(100);
          } catch (InterruptedException e) {
               throw new RuntimeException(e);
          }
           latch.countDown();
           System.out.println("end..." + latch.getCount());
      },"t1").start();
       new Thread(() -> {
           System.out.println("begin...");
           try {
               sleep(200);
          } catch (InterruptedException e) {
               throw new RuntimeException(e);
          }
           latch.countDown();
           System.out.println("end..." + latch.getCount());
      },"t2").start();
       new Thread(() -> {
           System.out.println("begin...");
           try {
               sleep(300);
          } catch (InterruptedException e) {
               throw new RuntimeException(e);
          }
           latch.countDown();
           System.out.println("end..." + latch.getCount());
      },"t3").start();
       System.out.println("waiting...");
       latch.await();
       System.out.println("wait end...");
  }

输出结果:

begin...
begin...
waiting...
begin...
end...2
end...1
end...0
wait end...

可以配合线程池使用,改进如下:

  public static void test2(){
       CountDownLatch latch = new CountDownLatch(3);
       ExecutorService service = Executors.newFixedThreadPool(4);
       service.submit(() -> {
           System.out.println("begin...");
           try {
               sleep(100);
          } catch (InterruptedException e) {
               throw new RuntimeException(e);
          }
           latch.countDown();
           System.out.println("end..." + latch.getCount());
      });
       service.submit(() -> {
           System.out.println("begin...");
           try {
               sleep(200);
          } catch (InterruptedException e) {
               throw new RuntimeException(e);
          }
           latch.countDown();
           System.out.println("end..." + latch.getCount());
      });
       service.submit(() -> {
           System.out.println("begin...");
           try {
               sleep(200);
          } catch (InterruptedException e) {
               throw new RuntimeException(e);
          }
           latch.countDown();
           System.out.println("end..." + latch.getCount());
      });
       service.submit(()->{
           try {
               System.out.println("waiting...");
               latch.await();
               System.out.println("wait end...");
          } catch (InterruptedException e) {
               e.printStackTrace();
          }
      });
  }

输出结果:

begin...
begin...
begin...
waiting...
end...2
end...1
end...0
wait end...

2、模拟王者荣耀玩家进度条加载

2.1 实现效果

这个动图有水印,大家凑合看,下面有加载过程的截图

使用CountDownLatch模拟王者荣耀玩家进度加载

加载过程截图:使用CountDownLatch模拟王者荣耀玩家进度加载加载完成截图:使用CountDownLatch模拟王者荣耀玩家进度加载

2.2 实现过程

其中有几个需要注意的点:

  • 创建一个10个线程的线程池代表10个玩家

  • new CountDownLatch(10); 进行线程同步协作,等待所有线程完成倒计时

  • 第一层循环代表执行每个玩家的进度,第二层循环代表每个玩家的进度从0到100%

  • System.out.print(“r” + Arrays.toString(all));  其中 r 代表的意思是:它通常用于将光标移回当前行的开头。在某些情况下,它可能用于覆盖之前的内容,从而创建一种”刷新”或”更新”显示的效果

  • 最后latch.await();  主线程等所有玩家加载完成  然后进入游戏

  /**
    * 王者荣耀游戏进度加载
    */
   public static void gameLoading(){
       AtomicInteger num = new AtomicInteger(0);

       // 创建线程池
       ExecutorService service = Executors.newFixedThreadPool(10,(r) ->{
           return new Thread(r, "玩家" + (num.getAndIncrement() + 1));
      });

       try {
           CountDownLatch latch = new CountDownLatch(10);
           String[] all = new String[10];
           Random r = new Random();
           // j 代表10个玩家
           for (int j = 0; j < 10; j++) {
               int x = j;
               service.submit(() -> {
                   // 加载进度100
                   for (int i = 0; i <= 100; i++) {
                       try {
                           sleep(r.nextInt(100));
                      } catch (InterruptedException e) {
                      }
                       all[x] = Thread.currentThread().getName() + "(" + (i + "%") + ")";
                       // 这是一个回车字符。在文本终端中,它通常用于将光标移回当前行的开头。在某些情况下,它可能用于覆盖之前的内容,从而创建一种"刷新"或"更新"显示的效果。但请注意,它不会删除之前的内容,只是将光标移回行的开头。
                       System.out.print("r" + Arrays.toString(all));
                  }
                   latch.countDown();
              });
          }
           // 主线程等所有玩家加载完成 然后进入游戏
           latch.await();

      } catch (InterruptedException e) {
           throw new RuntimeException("游戏加载异常", e);
      }
       System.out.print("n游戏开始...");
       System.out.println("n进入王者峡谷");
       service.shutdown();

  }

温馨提示:实际的游戏场景中肯定要比我们的实现复杂的多的多,我们这里只是通过这个示例来学习如何使用和应用CountDownLatch


本篇文章到这里就结束了,最后送大家一句话 白驹过隙,沧海桑田

END


使用CountDownLatch模拟王者荣耀玩家进度加载


PS:防止找不到本篇文章,可以收藏点赞,方便翻阅查找哦。

原文始发于微信公众号(迷迭香编程):使用CountDownLatch模拟王者荣耀玩家进度加载

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/227212.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!