java 多线程-4

十四、sleep方法和wait方法的区别

【面试题】

  • 相同点:
    1. 一旦执行方法,都可以使得当前线程进入阻塞状态。
  • 不同点:
    1. 两个方法的声明位置不同:Thread类声明sleep();Object类中声明wait()
    2. 调用的要求不同:sleep()可以在任何需要的场景下调用;wait()必须使用在同步代码块或者同步方法中
    3. 关于是否收释放同步监视器:如果有两个方法都使用在同步代码块或同步方法中,sleep()不会释放同步监视器,而wait方法会释放锁

十五、JDK5.0新增线程创建方式

因此,java中有四种创建多线程的方式:

  1. 继承Thread类,重写run方法
  2. 实现Runnlable接口,重写run方法
  3. 实现Callable接口,重写cal方法
  4. 使用线程池【真实开发中,多数情况下使用的是线程池的方式

15.1 新增方式一:实现Callable接口

15.1.1 Callable接口简介
  1. 与使用Runnable相比, Callable功能更强大些 :
    • 相比run()方法,可以有返回值
    • 方法可以抛出异常
    • 支持泛型的返回值
    • 需要借助FutureTask类,比如获取返回结果
15.1.2 借助Future接口

Future接口

  • 可以对具体Runnable、Callable任务的执行结果进行取消、查询是 否完成、获取结果等。
  • FutrueTask是Futrue接口的唯一的实现类
  • FutureTask 同时实现了Runnable, Future接口。它既可以作为 Runnable被线程执行 ,又可以作为Future得到Callable的返回值
15.1.3 列子
/**
 * 创建线程的方式三:实现Callable接口
 */
public class RunCallable {
    public static void main(String[] args) {
        // 第三步:创建callable接口的实现类的对象
        ThreadCallable threadCallable = new ThreadCallable();
        //  第四步:将threadCallable对象传入到FutureTask构造器中,创建futureTask对象
        //  【FutureTask 同时实现了Runnable, Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值】
        FutureTask futureTask = new FutureTask(threadCallable);

        // 第五步:将futureTask作为参数,传递到Thread类构造器中,创建Thread对象,并调用start方法
        Thread thread = new Thread(futureTask);
        // 第六步:开启线程
        thread.start();

        try {
            // 第七步:获取callable中cal方法的返回值
            // get方法获取返回值
            Object o = futureTask.get();
            System.out.println("总和为:"+o);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

    // 第一步:实现Callable接口
class ThreadCallable implements Callable{
    private int sum;
    // 第二步:重写run方法
    @Override
    public Object call() throws Exception {
        for (int i = 0; i <= 100; i++) {
            if (i % 2 == 0) {
                System.out.println(i);
            }
            sum += i;
        }
        /*返回值是Object类型的,但是为什么return sum没有报错?
        * 因为这里做了一个自动装箱操作,即把int型的sum,转为Integer,而Integer继承Object类,因此没有报错
        * */
        return sum;
    }
}

15.2 新增方式二:使用线程池

15.2.1 线程池简介
  • 背景:

    经常创建和销毁、使用量特别大的资源,比如并发情况下的线程, 对性能影响很大。

  • 思路:

    提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。

  • 好处:

    1. 提高响应速度(减少了创建新线程的时间)
    2. 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
    3. 便于线程管理
      • corePoolSize:核心池的大小
      • maximumPoolSize:最大线程数
      • keepAliveTime:线程没有任务时最多保持多长时间后会终止
15.2.2 线程池API

线程池相关API

  • JDK 5.0起提供了线程池相关API:ExecutorService 和 Executors

  • ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor

    1. void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执行 Runnable
    2. Future submit(Callable task):执行任务,有返回值,一般用来执行 Callable
    3. void shutdown() :关闭连接池
  • Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池

    1. Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池
    2. Executors.newFixedThreadPool(n); 创建一个可重用固定线程数的线程池
    3. Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池
    4. Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运 行命令或者定期地执行。
15.2.3 列子
public class ThreadPool {
    public static void main(String[] args) {
        // 第一步:提供指定线程数量的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(10);
//        executorService.submit();// 适合使用于callable
        // 第二步:执行指定的线程的操作,需要提供实现Runnable接口或Callable接口实现类的对象
        TestRunnable testRunnable = new TestRunnable();
        TestRunnable2 testRunnable2 = new TestRunnable2();
        executorService.execute(testRunnable);// 适合使用于runnable
        executorService.execute(testRunnable2);// 适合使用于runnable
        // 第三步:关闭线程池子
        executorService.shutdown();

    }
}

class TestRunnable implements Runnable{
    private int i = 0;
    @Override
    public void run() {
        while (true) {
            if (i < 101) {
                System.out.println(Thread.currentThread().getName()+"=======:"+i);
                i++;
            }else {
                break;
            }
        }
    }
}

class TestRunnable2 implements Runnable{
    private int i = 0;
    @Override
    public void run() {
        while (true) {
            if (i < 101) {
                System.out.println(Thread.currentThread().getName()+"&&&&&&&&"+i);
                i++;
            }else {
                break;
            }
        }
    }
}

posted @ 2020-09-19 16:03  宇宙砍柴人  阅读(133)  评论(0编辑  收藏  举报