【Java并发编程】-【线程组与线程池】

    技术2022-07-13  66

    一、线程组

    Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。 ①public final ThreadGroup getThreadGroup() 默认情况下,所有的线程都属于主线程组。 ②public final String getName() 返回此线程组的名称。 修改线程所在的组 创建一个线程组,创建其他线程的时候,把其他线程的组指定为我们自己新建线程组。 Thread(ThreadGroup group, Runnable target, String name)

    // ThreadGroup(String name) ThreadGroup tg = new ThreadGroup("这是一个新的组"); MyRunnable my = new MyRunnable(); // Thread(ThreadGroup group, Runnable target, String name) Thread t1 = new Thread(tg, my, "刘织忋"); Thread t2 = new Thread(tg, my, "马化腾"); System.out.println(t1.getThreadGroup().getName()); System.out.println(t2.getThreadGroup().getName()); //通过组名称设置后台线程,表示该组的线程都是后台线程 tg.setDaemon(true); //可通过ThreadGroup方法统一操作整个线程组线程

    二、线程池

    1.线程池概述

    程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。 线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。 在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池。

    2.Executors工厂类

    JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法: ①public static ExecutorService newCachedThreadPool() 创建一个具有缓存功能的线程池。 ②public static ExecutorService newFixedThreadPool(int nThreads) 创建一个可重用的,具有固定线程数的线程池。 ③public static ExecutorService newSingleThreadExecutor() 创建一个只有单线程的线程池,相当于上个方法的参数是1。

    这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。它提供了如下方法: Future<?> submit(Runnable task) Future submit(Callable task) Future表示异步计算的结果。

    实现线程代码步骤

    ①创建一个线程池对象,控制要创建几个线程对象。 -public static ExecutorService newFixedThreadPool(int nThreads) ②这种线程池的线程可以执行: -可以执行Runnable对象或者Callable对象代表的线程 -做一个类实现Runnable接口。 ③调用如下方法即可 -Future<?> submit(Runnable task) - Future submit(Callable task) ④结束线程池

    public class ExecutorsDemo { public static void main(String[] args) { // 创建一个线程池对象,控制要创建几个线程对象。 // public static ExecutorService newFixedThreadPool(int nThreads) ExecutorService pool = Executors.newFixedThreadPool(2); // 可以执行Runnable对象或者Callable对象代表的线程 pool.submit(new MyRunnable()); pool.submit(new MyRunnable()); //结束线程池 pool.shutdown(); } }

    3.多线程程序实现方案3:实现Callable接口

    import java.util.concurrent.Callable; //Callable:是带泛型的接口。 //这里指定的泛型其实是call()方法的返回值类型。 public class MyCallable implements Callable { @Override public Object call() throws Exception { for (int x = 0; x < 100; x++) { System.out.println(Thread.currentThread().getName() + ":" + x); } return null; } }

    实现Callable接口特点 好处: 可以有返回值,可以抛出异常。 弊端: 代码比较复杂,所以一般不用。

    异步求和代码示例

    public class CallableDemo { public static void main(String[] args) throws InterruptedException, ExecutionException { // 创建线程池对象 ExecutorService pool = Executors.newFixedThreadPool(2); // 可以执行Runnable对象或者Callable对象代表的线程 Future<Integer> f1 = pool.submit(new MyCallable(100)); Future<Integer> f2 = pool.submit(new MyCallable(200)); // V get() Integer i1 = f1.get(); Integer i2 = f2.get(); System.out.println(i1); System.out.println(i2); // 结束 pool.shutdown(); } }

    三、匿名内部类方式使用多线程

    匿名内部类的格式:

    new 类名或者接口名() { 重写方法; };

    本质:是该类或者接口的子类对象。

    public class ThreadDemo { public static void main(String[] args) { // 继承Thread类来实现多线程 new Thread() { public void run() { for (int x = 0; x < 100; x++) { System.out.println(Thread.currentThread().getName() + ":" + x); } } }.start(); // 实现Runnable接口来实现多线程 new Thread(new Runnable() { @Override public void run() { for (int x = 0; x < 100; x++) { System.out.println(Thread.currentThread().getName() + ":" + x); } } }) { }.start(); // 更有难度的,此时会走子类对象中的run()方法 new Thread(new Runnable() { @Override public void run() { for (int x = 0; x < 100; x++) { System.out.println("hello" + ":" + x); } } }) { public void run() { for (int x = 0; x < 100; x++) { System.out.println("world" + ":" + x); } } }.start(); } }

    四、定时器

    1.定时器概述

    定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行。在Java中,可以通过Timer和TimerTask类来实现定义调度的功能。

    2.Timer

    public Timer() 创建一个新的定时器。 public void schedule(TimerTask task, long delay) 安排在指定的时间执行指定的任务。 public void schedule(TimerTask task,long delay,long period) 安排指定的任务从指定的延迟后开始进行重复的固定延迟执行。

    3.TimerTask

    public abstract void run() 此计时器任务要执行的操作。 public boolean cancel() 取消此计时器的任务。

    public class TimerDemo { public static void main(String[] args) { // 创建定时器对象 Timer t = new Timer(); // 3秒后执行爆炸任务 // t.schedule(new MyTask(), 3000); //结束任务 t.schedule(new MyTask(t), 3000); } } // 做一个任务 class MyTask extends TimerTask { private Timer t; public MyTask(){} public MyTask(Timer t){ this.t = t; } @Override public void run() { System.out.println("boom,爆炸了"); t.cancel(); } }

    在开发中使用Quartz,Quartz是一个完全由java编写的开源调度框架。 思考题

    思考1: 多线程有几种实现方案,分别是哪几种?

    答:

    两种。 继承Thread类 实现Runnable接口 扩展一种:实现Callable接口。这个得和线程池结合。
    思考2: 同步有几种方式,分别是什么?

    答:

    两种。 同步代码块 同步方法
    思考3: 启动一个线程是run()还是start()?它们的区别?

    答:

    start(); run():封装了被线程执行的代码,直接调用仅仅是普通方法的调用。 start():启动线程,并由JVM自动调用run()方法。
    思考4: sleep()和wait()方法的区别

    答:

    sleep():必须指定时间;不释放锁。 wait():可以不指定时间,也可以指定时间;释放锁。
    思考5: 为什么wait(),notify(),notifyAll()等方法都定义在Object类中?

    答:

    因为这些方法的调用是依赖于锁对象的,而同步代码块的锁对象是任意锁。 而Object代表任意的对象,所以,定义在这里面。

    线程的生命周期图

    Processed: 0.009, SQL: 9