【多线程基础】线程状态 同步 协作 线程池 Lambda表达式
一、基本概念进程 Process进程就是执行程序的一次执行过程它是一个动态的概念是系统资源分配的单位通常在一个进程中可以包含若干个线程当然一个进程中至少有一个线程不然没有存在的意义线程是CPU调度和执行的单位线程 Thread线程就是独立的执行路径在程序运行时即使没有自己创建线程后台也会有多个线程比如主线程GC线程main()称之为主线程为系统的入口用于执行整个程序在一个进程中如果开辟了多个线程线程的运行是由调度器安排调度的调度器是与操作系统紧密相关的先后顺序是不能人为干预的对同一份资源操作时会存在资源抢夺的问题,需要加入并发控制线程会带来额外的开销如CPU调度时间并发控制开销每个线程在自己的工作内存交互内存控制不当会造成数据不一致多线程二、线程创建1、继承 Thread 类Thread 类实现了 Runnable 接口1.1、实现步骤继承Thread类重写run方法创建实例调用start方法public class ThreadTest extends Thread{ Override public void run() { for (int i 0; i 200; i) { System.out.println(我的线程i); } } public static void main(String[] args) { ThreadTest threadTest new ThreadTest(); threadTest.start(); //threadTest.run(); for (int i 0; i 2000; i) { System.out.println(主线程i); } } }1.2、start 和 run 方法的区别?start通过调用Thread类的 start()方法来启动一个线程这时此线程处于就绪(可运行)状态并没有运行一旦得到cpu时间片就开始执行run()方法runrun()方法 称为线程体。只是类的一个普通方法而已。如果直接调用Run方法程序中依然只有主线程这一个线程其程序执行路径还是只有一条还是要顺序执行还是要等待run方法体执行完毕后才可继续执行下面的代码这样就没有达到写线程的目的。1.3、多线程下载图片案例package com.ajun; import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; /** * author ajun * Date 2021/6/27 * version 1.0 */ public class WebDown extends Thread{ private String url; private String file; public WebDown(String url,String file){ this.url url; this.file file; } Override public void run() { WebDownLoader webDownLoader new WebDownLoader(); webDownLoader.downLoader(url,file); System.out.println(下载了图片file); } public static void main(String[] args) { WebDown t1 new WebDown(https://img2020.cnblogs.com/blog/2429409/202106/2429409-20210623132753966-565643795.jpg, 1.jpg); WebDown t2 new WebDown(https://img2020.cnblogs.com/blog/2429409/202106/2429409-20210618095622043-1605637963.jpg, 2.jpg); WebDown t3 new WebDown(https://img2020.cnblogs.com/blog/2429409/202106/2429409-20210614212002983-1003721756.jpg, 3.jpg); t1.start(); t2.start(); t3.start(); } } //下载器 class WebDownLoader{ //下载方法 public void downLoader(String url,String file){ try { FileUtils.copyURLToFile(new URL(url),new File(file)); } catch (IOException e) { e.printStackTrace(); } } }2、实现 Runnable 接口 (推荐)2.1、实现步骤实现Runnable接口Runnable 接口只有run()方法实现run方法创建Thread时作为参数传入,调用start方法public class RunnableTest implements Runnable{ Override public void run() { for (int i 0; i 200; i) { System.out.println(我的线程i); } } public static void main(String[] args) { //创建一个实现 Runnable 接口的类 RunnableTest runnableTest new RunnableTest(); //创建线程把实现Runnable接口的类对象作为参数传入 new Thread(runnableTest).start(); for (int i 0; i 2000; i) { System.out.println(主线程i); } } }2.2、龟兔赛跑public class Race implements Runnable { //胜利者 private static String winner ; //距离 private int m 1_0000_0000; Override public void run() { for (int i 1; i m1; i) { //选手 String player Thread.currentThread().getName(); //兔子每10米就休息 if (player.equals(兔子) i % 10 0) { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } //判断比赛是否结束 if (gameOver(i)) { System.out.println(player --- 跑了 i 米); break; } } } //判断比赛是否结束 private boolean gameOver(int m1) { if (winner ! ) {//已经有胜利者 return true; } else {//没有胜利者 if (m1 m) {//已到达终点 winner Thread.currentThread().getName(); System.out.println(winner 赢了); return true; } } return false; } public static void main(String[] args) { Race race new Race(); new Thread(race, 乌龟).start(); new Thread(race, 兔子).start(); } }3、实现 Callable 接口 (了解)3.1、步骤3.2、改造多线程下载图片案例public class WebDownCallable implements CallableBoolean { private String url; private String file; public WebDownCallable(String url, String file) { this.url url; this.file file; } Override public Boolean call() throws Exception { WebDownLoader1 webDownLoader new WebDownLoader1(); webDownLoader.downLoader(url, file); System.out.println(下载了图片 file); return true; } public static void main(String[] args) throws ExecutionException, InterruptedException { WebDownCallable t1 new WebDownCallable(https://img2020.cnblogs.com/blog/2429409/202106/2429409-20210623132753966-565643795.jpg, 1.jpg); WebDownCallable t2 new WebDownCallable(https://img2020.cnblogs.com/blog/2429409/202106/2429409-20210618095622043-1605637963.jpg, 2.jpg); WebDownCallable t3 new WebDownCallable(https://img2020.cnblogs.com/blog/2429409/202106/2429409-20210614212002983-1003721756.jpg, 3.jpg); //创建执行服务创建线程池 ExecutorService service Executors.newFixedThreadPool(3); //提交执行 FutureBoolean s1 service.submit(t1); FutureBoolean s2 service.submit(t2); FutureBoolean s3 service.submit(t3); //得到返回值 Boolean re1 s1.get(); Boolean re2 s2.get(); Boolean re3 s3.get(); System.out.println(re1); System.out.println(re2); System.out.println(re3); //关闭服务 service.shutdownNow(); } } //下载器 class WebDownLoader1 { //下载方法 public void downLoader(String url, String file) { try { FileUtils.copyURLToFile(new URL(url), new File(file)); } catch (IOException e) { e.printStackTrace(); } } }三、Lambda表达式 (箭头函数)1、介绍λ 希腊字母表中排序第十一位的字母英语名称为 Lambda (兰布达)Lambda表达式 就是 箭头函数。是JDK1.8的一种新特性(语法糖)避免匿名内部类定义过多可以让代码看起来很简洁去掉了一堆没有意义的代码留下核心的逻辑其实质属于函数式编程的概念(params)- expression[表达式] (params) - statement[语句] (params) - {statements}2、函数式接口理解Functional interface(函数式接口)是学习java8 lamda表达式的关键所在函数式接口的定义任何接口如果只包含一个方法那么它就是一个函数式接口public interface Runnable{ public abstract void run(); }对于函数式接口我们可以通过lamda表达式来创建该接口的对象3、示例3.1、定义接口//接口 //只有一个方法 interface ILike{ void lambda(); //带参数 //void lambda(int n); }3.2、传统实现类public class LambdaTest { public static void main(String[] args) { ILike1 iLike1 new ILike1(); iLike1.lambda();//lambda1 } } //传统实现类 class ILike1 implements ILike{ Override public void lambda() { System.out.println(lambda1); } //带参数 //Override //public void lambda(int a) { // System.out.println(lambda1---a); //} }3.3、静态内部类public class LambdaTest { //静态内部类 static class ILike2 implements ILike{ Override public void lambda() { System.out.println(lambda2); } } public static void main(String[] args) { ILike2 iLike2 new ILike2(); iLike2.lambda(3); //lambda2 } }3.4、局部内部类public class LambdaTest { public static void main(String[] args) { //局部内部类 class ILike3 implements ILike{ Override public void lambda() { System.out.println(lambda3); } } ILike3 iLike3 new ILike3(); iLike3.lambda(); //lambda3 } }3.5、匿名内部类public class LambdaTest { public static void main(String[] args) { //匿名内部类 //new 接口 { 实现方法 } ILike iLike4 new ILike() { Override public void lambda() { System.out.println(lambda4); } }; iLike4.lambda(); //lambda4 } }3.6、Lambda表达式完整写法public class LambdaTest { public static void main(String[] args) { //Lambda表达式 ILike i5 () - { System.out.println(lambda5); }; i5.lambda(); //lambda5 } }简写 (省花括号)方法中只有一条执行语句时省略花括号public class LambdaTest { public static void main(String[] args) { //方法中只有一条执行语句时 //花括号可以省略 ILike i5 () - System.out.println(lambda6); i5.lambda(); //lambda6 } }简写(只有一个参数时省略小括号)public class LambdaTest { public static void main(String[] args) { //只有一个参数时省略小括号 ILike i5 a - System.out.println(lambda7---a); i5.lambda(5); //lambda7---5 } }四、线程状态1、状态statepublic enum State { NEW,//新线程未启动 RUNNABLE,//可运行(包含就绪状态 和 运行状态) BLOCKED,//阻塞 WAITING,//等待 TIMED_WAITING,//超时等待 TERMINATED;//中止 }2、线程方法getName()setName()2.1、线程停止2.2、线程休眠 sleep()sleep(毫秒数)指定当前线程停止的时间sleep()存在异常InteruptedExceptionsleep()时间到达后线程进入就绪状态sleep()可以模拟网络延时、倒计时等每一个对象都有一个锁sleep不会释放锁2.3、线程礼让 yield()礼让线程让当前正在执行的线程暂停但不阻塞将线程从运行状态转为就绪状态让CPU重新调度礼让不一定成功看CPU心情2.4、线程强制执行 join()Join合并线程待此线程执行完成后再执行其他线程其他线程阻塞可以想象成插队public class ThreadJoinTest implements Runnable{ Override public void run() { for (int i 0; i 10; i) { System.out.println(线程VIP来了i); } } public static void main(String[] args) throws InterruptedException { ThreadJoinTest joinTest new ThreadJoinTest(); Thread thread new Thread(joinTest); thread.start(); for (int i 0; i 100; i) { if(i30){ thread.join(); } System.out.println(maini); } } }2.5、线程优先级java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程线程调度器按照优先级决定该调度哪个线程来执行线程的优先级用数字来表示范围从1~10Thread.MIN_PRIORITY 1Thread.MAX_PRIORITY 10Thread.NORM_PRIORITY 5使用getPriority()和setPriority()来获取或改变优先级2.6、守护线程setDeamon()setDeamon(true)线程分为用户线程和守护线程虚拟机必须确保用户线程执行完毕虚拟机不用等待守护线程执行完毕如后台记录操作日志监控内存垃圾回收五、线程同步1、介绍处理多线程问题时多个线程访问同一个对象并且某些线程还想修改这个对象这时候我们就需要线程同步线程同步其实是一种等待机制多个需要同时访问此对象的线程进入这个对象的等待池形成队列等待前面的线程使用完毕下一个线程在使用由于同一进程的多个线程共享同一块存储空间在带来方便的同时也带来了访问的冲突问题为了保证数据在方法中被访问时的正确性在访问时加入锁机制syncronized当一个线程获得对象的排他锁独占资源其他线程必须等待使用后释放锁即可,存在一下问题一个线程持有锁会导致其他所有需要此锁的进程挂起在多线程竞争的情况下加锁释放锁会导致比较多的上下文切换和调度延时引起性能问题如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置引起性能问题2、同步方法 和 同步块同步块synchronized(obj){ }obj称之为同步监视器obj可以是任何对象但是推荐使用共享资源作为同步监视器同步方法在方法上添加synchronized关键字锁的是对象本身同步方法中无需指定同步监视器因为同步方法中的同步监视器就是this就是这个对象本身或者是class同步监视器的执行过程第一个线程访问锁定同步监视器执行其中的代码第二个线程访问发现同步监视器被锁定无法访问就等待第一个线程访问完毕解锁同步监视器第二个线程访问发现同步监视器没有锁然后锁定并访问3、三大不安全案例3.1、买票public class BuyTicketTest { public static void main(String[] args) { BuyTicket buyTicket new BuyTicket(); new Thread(buyTicket,小张).start(); new Thread(buyTicket,小王).start(); new Thread(buyTicket,小李).start(); } } class BuyTicket implements Runnable{ private int ticketNum 10; boolean flag true; Override public void run() { while (flag){ buy(); } } //同步方法 private synchronized void buy(){ if(ticketNum 1){ flag false; return; } try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() 拿到了 ticketNum--); } }解决办法把买票方法 buy 同步3.2、取钱public class BankTest { public static void main(String[] args) { Account account new Account(100, 结婚基金); TakeMoney myWife new TakeMoney(account, 80); TakeMoney my new TakeMoney(account, 50); new Thread(myWife,我的妻子).start(); new Thread(my,我).start(); } } //账户 class Account { private int money; private String name; public Account(int money, String name) { this.money money; this.name name; } public int getMoney() { return money; } public void setMoney(int money) { this.money money; } public String getName() { return name; } public void setName(String name) { this.name name; } } //取钱 class TakeMoney implements Runnable { Account account;//账户 int takeNum;//存走的钱 int nowNum;//手里的钱 public TakeMoney(Account account, int takeNum) { this.account account; this.takeNum takeNum; } Override public void run() { //同步块把账户 account 同步 synchronized (account){ String threadName Thread.currentThread().getName(); //判断是否有钱 if (account.getMoney() - takeNum 1) { System.out.println(threadName 钱不够了); return; } try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } //卡内余额 account.setMoney(account.getMoney() - takeNum); //手里的钱 nowNum nowNum takeNum; System.out.println(account.getName() 余额为account.getMoney()); System.out.println(threadName 手里的钱为nowNum); } } }解决办法同步块把账户 account 同步3.3、线程不安全集合public class CollectionTest { public static void main(String[] args) throws InterruptedException { ListString list new ArrayList(); for (int i 0; i 10000; i) { new Thread(()-{ //同步块对集合 list 同步 synchronized (list){ list.add(Thread.currentThread().getName()); } }).start(); } Thread.sleep(3000); System.out.println(list.size()); } }解决办法同步块对集合 list 同步4、死锁4.1、死锁分析多个线程各自占有一些共享资源并且互相等待其他线程占有的资源释放才能运行而导致两个或者多个线程都在等待对方释放资源都停止执行的情形某一个同步块同时拥有两个以上的对象锁时就可能发生死锁现象4.2、死锁产生的必要条件互斥条件 一个资源每次只能被一个线程使用请求保持条件 一个线程因请求资源而阻塞时对以获得的资源保持不放不剥夺条件线程已获得的资源在未使用完之前不能强行剥夺循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系4.3、死锁避免方法5、释放锁Java多线程运行环境中在哪些情况下会使对象锁释放由于等待一个锁的线程只有在获得这把锁之后才能恢复运行所以让持有锁的线程在不再需要锁的时候及时释放锁是很重要的。在以下情况下持有锁的线程会释放锁执行完同步代码块就会释放锁。(synchronized)在执行同步代码块的过程中遇到异常而导致线程终止锁也会被释放。(exception)在执行同步代码块的过程中执行了锁所属对象的wait()方法这个线程会释放锁进入对象的等待池。(wait)除了以上情况以外只要持有锁的线程还没有执行完同步代码块就不会释放锁在下面情况下线程是不会释放锁的执行同步代码块的过程中执行了Thread.sleep()方法当前线程放弃CPU开始睡眠进入堵塞状态在睡眠中不会释放锁在执行同步代码块的过程中执行了Thread.yield()方法当前线程放弃CPU回到就绪状态但不会释放锁方法是否释放锁wait()释放sleep()不释放yield()不释放6、Lock锁从JDK1.5开java提供了更为强大的线程同步机制——通过显式定义同步锁对象来实现同步同步锁使用lock对象来充当java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问每次只能有一个线程对Lock对象加锁线程开始访问共享资源之前应先获得Lock对象ReentrantLock类实现了Lock它拥有与synchronized相同的并发性和内存语义在实现线程安全的控制中比较常用的是ReentrantLock可以显示加锁,释放锁class BuyTicket implements Runnable{ //定义锁 private ReentrantLock lock new ReentrantLock(); private int ticketNum 10; boolean flag true; Override public void run() { while (flag){ buy(); } } //synchronized private void buy(){ lock.lock();//加锁 try{ //业务代码 if(ticketNum 1){ flag false; return; } try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() 拿到了 ticketNum--); }finally { lock.unlock();//解锁 } } }7、Lock 与 synchronizedLock是显式锁(手动开启和关闭别忘记关闭锁) synchronized是隐式锁出了作用域自动释放Lock只有代码块锁synchronized有代码块锁和方法锁使用lock锁JVM将花费较少的时间来调度线程性能更好并且具有更好的扩展性(提供更多的子类)优先使用顺序Lock 同步代码块 同步方法比较Locksynchronized类型java接口内置关键字开启释放显式锁手动 开启 和 关闭隐式锁出了作用域自动释放代码锁支持支持方法锁不支持支持锁状态可以判断是否获取到了锁无法判断获取锁的状态线程阻塞Lock锁就不一定会等待下去线程1(获得锁阻塞)线程2(等待)可重入性可重入的可以判断锁可重入锁不可以中断的公平性默认非公平的(可设置)非公平的性能性能更好JVM将花费较少的时间来调度性能一般JVM将花费较多的时间来调度管理扩展性有更好的扩展性(提供更多的子类)不支持适用场景适合锁大量的同步代码适合锁少量的代码同步问题六、线程协作1、生产者和消费者问题假设仓库中只能存放一件产品生产者将生产出来的产品放入仓库消费者将仓库产品取走消费如果仓库中没有产品则生产者将产品放入仓库否则停止生产并等待直到仓库中的产品被消费者取走为止如果仓库中放有产品则消费者可以将产品取走消费否则停止消费并等待直到仓库中再次放入产品为止这是一个线程同步问题生产者和消费者共享一个资源并且生产者和消费者之间相互依赖互为条件对于生产者没有生产产品之前要通知消费者等待生产了产品之后又要马上通知消费者消费对于消费者在消费之后要通知生产者已经结束消费需要生产新的产品以供消费在生产者、消费者问题上仅有 synchronized 是不够的synchronized 可阻止并发更新同一个共享资源实现了同步synchronized 不能用来实现不同线程之间消息传递(通信)2、Java 中解决线程间通信的方法wait() 与 sleep() 区别wait 在等待时会释放锁sleep 在等待时不会释放锁(抱着锁睡觉)3、解决方案3.1、管程法生产者负责生产数据的模块(可能是方法对象线程数组)消费者:负责处理数据的模块(可能是方法对象线程数组)缓冲区消费者不能直接使用生产者的数据他们之间有个缓冲区生产者将生产好的数据放入缓冲区消费者从缓冲区拿出数据package com.ajun; import java.util.concurrent.locks.ReentrantLock; /** * author ajun * Date 2021/6/28 * version 1.0 * 生产者、消费者问题 * 管程法 */ public class PCTest { public static void main(String[] args) { //定义仓库 Repository repository new Repository(); //生产者 Provider provider new Provider(repository); //消费者 Consumer consumer new Consumer(repository); new Thread(provider).start(); new Thread(consumer).start(); } } //产品 class Product { private int id; public int getId() { return id; } public void setId(int id) { this.id id; } public Product(int id) { this.id id; } } //生产者 class Provider implements Runnable { //有仓库属性 private Repository repository new Repository(); //构造器 public Provider(Repository repository) { this.repository repository; } Override public void run() { //循环生产100件产品 for (int i 1; i 101; i) { System.out.println(生产了第 i 件产品); //存入仓库 repository.push(new Product(i)); } } } //消费者 class Consumer implements Runnable { //有仓库属性 private Repository repository new Repository(); //构造器 public Consumer(Repository repository) { this.repository repository; } Override public void run() { //循环消费100件产品 for (int i 1; i 101; i) { //从仓库取出 Product product repository.out(); System.out.println(消费了第 product.getId() 件产品); } } } //仓库(缓存区) class Repository { //存放产品的数组 Product[] products new Product[10]; int count 0;//计数器 //生产者放入产品 //放产品时需要线程同步 public synchronized void push(Product product) { //判断仓库是否满了 if(count products.length){//满了 try { this.wait();//等待 } catch (InterruptedException e) { e.printStackTrace(); } } //仓库没有满 products[count] product;//把产品放入仓库 count;//计器数加1 //通知消费者可以消费了 this.notifyAll(); } //消费者取产品 //取产品时需要线程同步 public synchronized Product out() { //Product product null; //判断仓库中是否有产品 if(count 0){//没有产品 try { this.wait();//等待 } catch (InterruptedException e) { e.printStackTrace(); } }/*else{//有产品 count--;//计数器减1 product products[count];//取出产品 }*/ count--;//计数器减1 Product product products[count];//取出产品 this.notifyAll(); return product; } }在有 wait 和 notityAll 的同步时不能用 ReentrantLockwait 、notity 方法只能在 同步方法 或 同步代码块 中使用否则会抛出 IllegalMonitorStateException 异常3.2、信号灯法package com.ajun; /** * author ajun * Date 2021/6/28 * version 1.0 * 生产者、消费者问题 * 信号灯法 */ public class PCTest1 { public static void main(String[] args) { TV tv new TV(); Player player new Player(tv); Watcher watcher new Watcher(tv); new Thread(player).start(); new Thread(watcher).start(); } } //生产者演员 class Player implements Runnable { private TV tv; public Player(TV tv) { this.tv tv; } Override public void run() { for (int i 0; i 5; i) { tv.play(第 (i 1) 个节目); } } } //消费者观众 class Watcher implements Runnable { private TV tv; public Watcher(TV tv) { this.tv tv; } Override public void run() { for (int i 0; i 5; i) { tv.watch(); } } } //产品节目 class TV { //具体节目 String voice; //控制开关 //节目未上映演员生产观众等待false //节目已上映观众观看演员休息等待true boolean flag false;//未上映 //表演 public synchronized void play(String voice) { //如果已上映观众正在观看这里演员就可以等待休息 if (flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(演员制作完成上映了 voice); //展示节目 this.voice voice; //通知观众观看 this.notifyAll(); //更新控制开关 this.flag !this.flag; } //观看 public synchronized void watch() { //如果未上映演员正在制作观众休息等待 if (!flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(观众观看了 voice); //通知演员制作 this.notifyAll(); //更新控制开关 this.flag !this.flag; } }七、线程池1、介绍背景经常创建和销毁使用量特别大的资源比如并发情况下的线程对性能影响很大思路提前创建好多个线程放入线程池中使用时直接获取使用完毕放回池中可以避免频繁的创建销毁实现重复利用类似生活中的公共交通工具好处提高了响应速度(减少了创建新线程的时间)降低资源消耗(重复利用线程池中线程不需要每次都创建)便于线程管理(…)corePoolSize 核心池的大小maximumPoolSize最大线程数keepAliveTime 线程没有任务时最多保持多长时间后会终止2、使用线程池线程池工具类 Executors2.1、execute()没有返回值执行 Runnable 线程类package com.ajun; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * author ajun * Date 2021/6/28 * version 1.0 */ public class ThreadPoolTest { public static void main(String[] args) { //用线程池工厂类 Executors,创建线程池 ExecutorService service Executors.newFixedThreadPool(10); //执行线程 service.execute(new MyThread()); service.execute(new MyThread()); service.execute(new MyThread()); service.execute(new MyThread()); //关闭连接 service.shutdown(); } } //实现Runnable接口的类 class MyThread implements Runnable{ Override public void run() { System.out.println(Thread.currentThread().getName()); } }2.2、submit()有返回值 Future执行 Callable 接口的类package com.ajun; import java.util.concurrent.*; /** * author ajun * Date 2021/6/28 * version 1.0 */ public class ThreadPoolTest1 { public static void main(String[] args) throws ExecutionException, InterruptedException { //用线程池工厂类 Executors,创建线程池 ExecutorService service Executors.newFixedThreadPool(10); //提交执行 FutureBoolean s1 service.submit(new MyThread1()); FutureBoolean s2 service.submit(new MyThread1()); FutureBoolean s3 service.submit(new MyThread1()); //得到返回值 Boolean rs1 s1.get(); Boolean rs2 s2.get(); Boolean rs3 s3.get(); //关闭连接 service.shutdown(); } } class MyThread1 implements CallableBoolean{ Override public Boolean call() throws Exception { System.out.println(Thread.currentThread().getName()); return true; } }
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2423959.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!