一. java线程组特点:
1.所有用户创建的线程默认属于main线程组。main线程组是JVM在应用程序运行时自动创建。
2.如果线程A创建了线程B,创建时未指定线程组,那么线程组B自动加入线程A所在线程组。
3.线程一旦加入某个线程组,该线程就一直留着该组中,直至线程死亡,中途不可改变所属线程组。
4.用户创建的线程组默认都有一个父线程组。如线程A创建了线程组X,那么线程A所在线程组则默认称为线程组X的父线程组。
通过以下示例可验证:
package net.mosang.threadgroup; /** * 线程类 * @author Heiry * 模拟登录 */ public class SignUp implements Runnable{ private String userName; private String passWord; public SignUp(String userName, String passWord) { this.userName = userName; this.passWord = passWord; } @Override public void run() { System.out.println("你登录的用户名是:"+userName+"密码是:"+passWord); if(userName.equals("创建线程组")) { ThreadGroup tg = new ThreadGroup("newTg"); System.out.println("新线程内创建的线程组名是:"+tg.getName()); System.out.println("新线程内创建的线程组父线程组名是:"+tg.getParent().getName()); } } }
package net.mosang.threadgroup; /** * 线程组示例 * @author Heiry * */ public class ThreadGroupDemo { public static void main(String[] args) { SignUp u1 = new SignUp("heiry", "123"); SignUp u2 = new SignUp("mosang", "456"); Thread tu1 = new Thread(u1);//创建线程 Thread tu2 = new Thread(u2);//创建线程 System.out.println(tu1.getThreadGroup().getName());//输出“main”,tu1所属线程组 System.out.println(tu2.getThreadGroup().getName());//输出“main”, tu2所属线程组 ThreadGroup g1 = new ThreadGroup("用户线程组A");//创建线组,不指定父线程组 ThreadGroup g2 = new ThreadGroup("用户线程组B");//创建线组,不指定父线程组 Thread tu3 = new Thread(g1,u1);//创建线程,指定线程组 Thread tu4 = new Thread(g2,u2);//创建线程,指定线程组 System.out.println(tu3.getThreadGroup().getName());//输出“用户线程组A”,tu3所属线程组 System.out.println(tu4.getThreadGroup().getName());//输出“用户线程组B”, tu4所属线程组 System.out.println("g1父线程组是--->"+g1.getParent().getName()); // 输出“main” System.out.println("g2父线程组是--->"+g2.getParent().getName()); // 输出“main” SignUp u3 = new SignUp("创建线程组", "789");//线程内将创建线程组 Thread tu5 = new Thread(g1,u3);//创建线程,指定线程组 tu5.start(); } }
输出结果:
main main 用户线程组A 用户线程组B g1父线程组是--->main g2父线程组是--->main 你登录的用户名是:创建线程组密码是:789 新线程内创建的线程组名是:newTg 新线程内创建的线程组父线程组名是:用户线程组A
Callable 接口与Runnable接口类似可实现多线程的创建,但与Runnable不同的是,其线程方法call()有返回值并可抛出异常(Runnable 不会返回结果,并且无法抛出经过检查的异常)。另外Callable创建多线程依赖于线程池。
通过API查询,可以看到,Callable<V>是带泛型的,其泛型就是call方法返回值类型。
以下通过创建商品“秒杀”多线程FlashSale,实例讲解Callable 实现多线程的步骤:
1. 创建线程类FlashSale,实现Callable接口,重写call方法。
2.创建FlashSale线程实例binge,craze。
3.创建线程池对象实例shoppingPool
4.通过线程池实例shoppingPool的submit方法执行binge,craze。
package net.mosang.callabledemo; import java.util.concurrent.Callable; /** * FlashSale线程类 * @author Heiry * 重写不带任何参数的 call方法,返回String类型的抢购结果提示 */ public class FlashSale implements Callable<String>{ private String goodsName; @Override public String call() throws Exception { if(goodsName.equals("Mac电脑")) { return "抢到了苹果电脑"; }else if (goodsName.equals("Mi手机")) { return "抢到了小米手机"; } else { return "什么也没抢到"; } } public FlashSale(String goodsName) { super(); this.goodsName = goodsName; } }
package net.mosang.callabledemo; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /** * * @author Heiry * */ public class Shopping { public static void main(String[] args) throws InterruptedException, ExecutionException { FlashSale binge = new FlashSale("Mac电脑"); //创建线程实例binge FlashSale craze = new FlashSale("Mi手机");//创建线程实例craze ExecutorService shoppingPool = Executors.newFixedThreadPool(2); Future<String> s1 = shoppingPool.submit(binge); Future<String> s2 = shoppingPool.submit(craze); System.out.println(s1.get()); System.out.println(s2.get()); } }
输出结果
抢到了苹果电脑 抢到了小米手机
可见,相比继承Thread与实现Runable接口方式实现多线程,Callable更方便地得到多线程的返回值并进行异常处理。另外,Callable依赖于线程池,通过此方式创建多线程,步骤繁琐,更适合处理高并发。