java线程知识
创始人
2024-11-11 13:10:23
0

java线程间的通讯方式

多个线程按顺序执行

使用Thread类里面的join()方法。
例子:B 在 A 全部打印 完后再开始打印。

private static void demo2() {     Thread A = new Thread(new Runnable() {         @Override         public void run() {             printNumber("A");         }     });     Thread B = new Thread(new Runnable() {         @Override         public void run() {             System.out.println("B 开始等待 A");             try {                 A.join();             } catch (InterruptedException e) {                 e.printStackTrace();             }             printNumber("B");         }     });     B.start();     A.start(); } 

让两个线程按照指定方式有序交叉运行

利用 object.wait() 和 object.notify(),这两个方法要在synchronized代码块里执行。

/**  * A 1, B 1, B 2, B 3, A 2, A 3  */ private static void demo3() {     Object lock = new Object();     Thread A = new Thread(new Runnable() {         @Override         public void run() {             synchronized (lock) {                 System.out.println("A 1");                 try {                     lock.wait();                 } catch (InterruptedException e) {                     e.printStackTrace();                 }                 System.out.println("A 2");                 System.out.println("A 3");             }         }     });     Thread B = new Thread(new Runnable() {         @Override         public void run() {             synchronized (lock) {                 System.out.println("B 1");                 System.out.println("B 2");                 System.out.println("B 3");                 lock.notify();             }         }     });     A.start();     B.start(); } 

D 要等到 A B C 全执行完毕后才执行,而且 A B C 是同步运行的

使用CountdownLatch里面的await() 和 countDown()方法。
CountdownLatch基本用法:
1、创建一个计数器,设置初始值,CountdownLatch countDownLatch = new CountDownLatch(2);
2、在 等待线程 里调用 countDownLatch.await() 方法,进入等待状态,直到计数值变成 0;
3、在 其他线程 里,调用 countDownLatch.countDown() 方法,该方法会将计数值减小 1;
4、当 其他线程 的 countDown() 方法把计数值变成 0 时,等待线程 里的 countDownLatch.await() 立即退出,继续执行下面的代码。

private static void runDAfterABC() {     int worker = 3;     CountDownLatch countDownLatch = new CountDownLatch(worker);     new Thread(new Runnable() {         @Override         public void run() {             System.out.println("D is waiting for other three threads");             try {                 countDownLatch.await();                 System.out.println("All done, D starts working");             } catch (InterruptedException e) {                 e.printStackTrace();             }         }     }).start();     for (char threadName='A'; threadName <= 'C'; threadName++) {         final String tN = String.valueOf(threadName);         new Thread(new Runnable() {             @Override             public void run() {                 System.out.println(tN + " is working");                 try {                     Thread.sleep(100);                 } catch (Exception e) {                     e.printStackTrace();                 }                 System.out.println(tN + " finished");                 countDownLatch.countDown();             }         }).start();     } } 

等到三线程都准备好后,再一起执行

使用CyclicBarrier
基本用法:
1、先创建一个公共 CyclicBarrier 对象,设置 同时等待 的线程数,CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
2、这些线程同时开始自己做准备,自身准备完毕后,需要等待别人准备完毕,这时调用 cyclicBarrier.await(); 即可开始等待别人;
3、当指定的 同时等待 的线程数都调用了 cyclicBarrier.await();时,意味着这些线程都准备完毕好,然后这些线程才 同时继续执行。

private static void runABCWhenAllReady() {     int runner = 3;     CyclicBarrier cyclicBarrier = new CyclicBarrier(runner);     final Random random = new Random();     for (char runnerName='A'; runnerName <= 'C'; runnerName++) {         final String rN = String.valueOf(runnerName);         new Thread(new Runnable() {             @Override             public void run() {                 long prepareTime = random.nextInt(10000) + 100;                 System.out.println(rN + " is preparing for time: " + prepareTime);                 try {                     Thread.sleep(prepareTime);                 } catch (Exception e) {                     e.printStackTrace();                 }                 try {                     System.out.println(rN + " is prepared, waiting for others");                     cyclicBarrier.await(); // 当前运动员准备完毕,等待别人准备好                 } catch (InterruptedException e) {                     e.printStackTrace();                 } catch (BrokenBarrierException e) {                     e.printStackTrace();                 }                 System.out.println(rN + " starts running"); // 所有运动员都准备好了,一起开始跑             }         }).start();     } } 

把任务执行结果回传给主线程

使用Callable+FutureTask,但FutureTask的get()方法会阻塞主线程。
例子:让子线程去计算从 1 加到 100,并把算出的结果返回到主线程。

private static void doTaskWithResultInWorker() {     Callable callable = new Callable() {         @Override         public Integer call() throws Exception {             System.out.println("Task starts");             Thread.sleep(1000);             int result = 0;             for (int i=0; i<=100; i++) {                 result += i;             }             System.out.println("Task finished and return result");             return result;         }     };     FutureTask futureTask = new FutureTask<>(callable);     new Thread(futureTask).start();     try {         System.out.println("Before futureTask.get()");         System.out.println("Result: " + futureTask.get());         System.out.println("After futureTask.get()");     } catch (InterruptedException e) {         e.printStackTrace();     } catch (ExecutionException e) {         e.printStackTrace();     } } 

备注:由于FutureTask的get()方法会阻塞主线程,因此应该把FutureTask放到线程池里面执行。

线程池 + FutureTask执行多任务计算

用线程池+ FutureTask将1个查询拆分成多个小查询。

public class Test {  //线程池最好作为全局变量, 若作为局部变量记得用完后shutdown()  ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("thread-start-runner-%d").build();  ExecutorService taskExe= new ThreadPoolExecutor(10,20,800L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue(100),namedThreadFactory);    int count=0;  @Test  public void test(String[] args) {      //任务列表   List> taskList=new ArrayList>();   for(int i=0;i<100;i++){    //创建100个任务放入【任务列表】    FutureTask futureTask=new FutureTask(new Callable() {     @Override     public Integer call() throws Exception {      return 1;     }    });    //执行的结果装回原来的FutureTask中,后续直接遍历集合taskList来获取结果即可    taskList.add(futureTask);    taskExe.submit(futureTask);   }   //获取结果   try{    for(FutureTask futureTask:taskList){                 count+=futureTask.get();             }   } catch (InterruptedException e) {    logger.error("线程执行被中断",e);   } catch (ExecutionException e) {    logger.error("线程执行出现异常",e);   }   //关闭线程池   taskExe.shutdown();   //打印: 100   System.out.println(count);  } } 

子线程出的异常抛不出的情况

submit(Runnable task)提交任务的方式 ,是存在“隐患”的:FutureTask内部的run()代码块会把异常给吞进去。
正确做法:FutureTask内部的run()代码块用try-catch来截获抛出的异常,然后通过get()方法获取到异常。

线程池submit()提交任务的方法和execute()执行任务的方法有什么区别

submit()方法,当子线程执行异常时不会中断退出,而会一直往下执行,相当于把异常信息吞掉了。
execute()方法,当子线程执行异常时会中断退出,打印异常信息。

解决异常被吞的方法

把 submit 返回的 Future 放到了 List 中,随后遍历 List 来捕获所有任务的异常。

 List tasks = IntStream.rangeClosed(1, 10).mapToObj(i -> threadPool.submit(() -> {     if (i == 5) throw new RuntimeException("error");     log.info("I'm done : {}", i); })).collect(Collectors.toList());  tasks.forEach(task-> {     try {         task.get();     } catch (Exception e) {         log.error("Got exception", e);     } }); 
解决execute()打印异常信息没有统一的错误日志格式

在声明线程池时自定义线程池的未捕获异常处理程序:

new ThreadFactoryBuilder()   .setNameFormat(prefix+"%d")   .setUncaughtExceptionHandler((thread, throwable)-> log.error("ThreadPool {} got exception", thread, throwable))   .get()  

相关内容

热门资讯

烘培辅助!天酷大厅辅助!曝光真... 烘培辅助!天酷大厅辅助!曝光真的有辅助攻略(有挂分享)1、打开软件启动之后找到中间准星的标志长按。2...
第三方辅助挂!天天微友辅助(辅... 第三方辅助挂!天天微友辅助(辅助)总是是真的有辅助神器(有挂规律)1、天天微友辅助有没有辅助教程、天...
手筋辅助!h5反杀程序!科普有... 手筋辅助!h5反杀程序!科普有辅助工具(有挂存在)1、h5反杀程序公共底牌简单,h5反杀程序透视插件...
今日!随意玩辅助器视频透视挂(... 今日!随意玩辅助器视频透视挂(辅助)总是存在有辅助挂(有挂秘诀)随意玩辅助器视频透视挂透视方法中分为...
秘籍辅助!青橙竞技游戏辅助!关... 秘籍辅助!青橙竞技游戏辅助!关于是真的有辅助教程(有挂秘籍)所有人都在同一条线上,像星星一样排成一排...
出乎意料的是!旺旺填大坑透视挂... 出乎意料的是!旺旺填大坑透视挂(辅助)本来真的是有辅助器(了解有挂)所有人都在同一条线上,像星星一样...
窍门辅助!雀友会广东潮汕麻雀有... 窍门辅助!雀友会广东潮汕麻雀有挂么!关于有辅助方法(有挂解密)1、这是跨平台的雀友会广东潮汕麻雀有挂...
据悉!湖北逍遥辅助(辅助)真是... 据悉!湖北逍遥辅助(辅助)真是是真的有辅助插件(有人有挂)湖北逍遥辅助是不是有人用挂微扑克wpk插件...
操作辅助!UU Games辅助... 操作辅助!UU Games辅助器!揭幕真的有辅助攻略(有挂方针)1、让任何用户在无需UU Games...
做出回应!辣椒互娱辅助(辅助)... 做出回应!辣椒互娱辅助(辅助)竟然确实有辅助攻略(确实有挂)1、玩家可以在辣椒互娱辅助透视最简单三个...