Java-多线程详解

Java是通过java.lang.Thread类来代表线程的

多线程的创建

继承Thread类

实现步骤:
  1. 定义一个子类MyThread继承线程类java.lang.Thread,重写run()方法

  1. 创建MyThread类的对象

  1. 调用线程对象的start()方法启动线程(启动后还是执行run方法的)

public class ThreadDemo {
    public static void main(String[] args) {
        Thread t = new MyThread();
        t.start();

        for (int i = 0; i < 5; i++) {
            System.out.println("主线程:" + i);
        }
    }
}
class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("子线程:" + i);
        }
    }
}
优缺点:
  • 优点:编码简单

  • 缺点:线程类已经继承Thread,无法继承其他类,不利于扩展

1.为什么不直接调用run()方法,而是调用start启动线程
直接调用run方法会当成普通方法执行,此时相当于还是单线程运行
只有调用start方法才是启动一个新的线程执行

实现Runnable接口

实现步骤
  1. 定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法

  1. 创建MyRunnable任务对象

  1. 把MyRunnable任务对象交给Thread处理

  1. 调用线程对象的start()方法启动线程

public class ThreadDemo {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        new Thread(myRunnable).start();

        for (int i = 0; i < 5; i++) {
            System.out.println("主线程:" + i);
        }
    }
}

class MyRunnable implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("子线程:" + i);
        }
    }
}
优缺点
  • 优点:线程任务类实现接口,可以继续继承类和实现接口,扩展性强

  • 缺点:编程多一层对象包装,如果线程有执行结果不可以直接返回

利用Callable、FutureTask接口实现

实现步骤
  1. 得到任务对象

  • 定义类实现Callable接口,重写call方法,封装要做的事情

  • 用FutureTask把Callable对象封装成线程任务对象

  1. 把线程任务对象交给Thread处理

  1. 调用Thread的start方法启动线程,执行任务

  1. 线程执行完毕后,通过FutureTask的get方法取获取任务执行结果

public class ThreadDemo2 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<String> call1 = new MyCallable(100);
        FutureTask<String> task1 = new FutureTask<>(call1);
        new Thread(task1).start();

        Callable<String> call2 = new MyCallable(200);
        FutureTask<String> task2 = new FutureTask<>(call2);
        new Thread(task2).start();

        System.out.println("第一个结果:" + task1.get());
        System.out.println("第二个结果:" + task2.get());
    }
}

class MyCallable implements Callable<String>{
    private int n;

    public MyCallable(int n) {
        this.n = n;
    }

    @Override
    public String call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= n; i++) {
            sum += i;
        }
        return "子线程执行结果:" + sum;
    }
}
优缺点
  • 优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强

可以在线程执行完毕后取获取线程执行的结果

  • 缺点:编码复杂

线程安全

多线程同时操作同一个共享资源的时候可能会出现业务安全问题,称为线程安全问题线程同步

线程同步

核心思想:

加锁,把共享资源进行上锁,每次只能一个线程进入访问完毕以后解锁,然后其他线程才能进来

同步代码块

作用:把出现线程安全问题的核心代码给上锁

原理:每次只能一个线程进入,执行完毕后自动解锁,其他线程才可以进来执行

格式:

synchronized(同步锁对象){

操作共享资源的代码(核心代码)

}

锁对象的规范要求:

  • 规范上:建议使用共享资源作为锁对象

  • 对于实例方法建议使用this作为锁对象

  • 对于静态方法建议使用字节码(类名.class)对象作为锁对象

同步方法

作用:把出现线程安全问题的核心方法给上锁

原理:每次只能一个线程进入,执行完毕后自动解锁,其他线程才可以进来执行

格式:

修饰符 synchronized 返回值类型 方法名称(形参列表){

操作共享资源代码

}

同步方法底层原理:

  • 同步方法其实底层也是有隐式锁对象的,只是锁的范围是整个方法代码

  • 如果实施方法:同步方法默认使用this作为锁对象,但是代码要高度面向对象

  • 如果是静态方法:同步方法默认使用类名.class作为锁对象

线程池(重点)

线程池概述

线程池就是一个可以复用线程的技术

不使用线程池的问题:

  • 如果用户每发起一次请求,后台就创建一个新线程来处理,下次新任务来了又要创建新线程,而创建新线程的开销是很大的,这样会严重影响系统性能

线程池实现

  • JDK5.0起提供了代表线程池的接口:ExecutorService

如何得到线程池对象:

方式一:使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象

方式二:使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象

ThreadPoolExecutor构造器的参数说明
public ThreadPoolExecutor(int corePloolSize,
                          int maxxinumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)
  • 参数一:指定线程池的线程数量(核心线程)----->不能小于0

  • 参数二:指定线程池可支持的最大线程数----->最大数量>=核心线程数量

  • 参数三:指定临时线程的最大存活时间----->不能小于0

  • 参数四:指定存活时间的单位(秒、分、时、天)----->时间单位

  • 参数五:指定任务队列----->不能为null

  • 参数六:指定用哪个线程工厂创建线程----->不能为null

  • 参数七:指定线程忙,任务满的时候,新任务来了怎么办----->不能为null

临时线程什么时候创建?

新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程

什么时候会开始拒绝任务?

核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始拒绝任务

线程池处理Runnable任务

  • 使用ExecutorService的方法:

  • void execute(Runnable target)

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println(Thread.currentThread().getName()+ "输出了:HelloWord====>" + i);
        }
        try {
            System.out.println(Thread.currentThread().getName() + "本任务与线程绑定,线程进入休眠");
            Thread.sleep(1000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
 public static void main(String[] args) {
        ExecutorService pool = new ThreadPoolExecutor(2,3,6,TimeUnit.SECONDS
                                                ,new ArrayBlockingQueue<>(4)
                                                ,Executors.defaultThreadFactory()
                                                ,new ThreadPoolExecutor.AbortPolicy());

        Runnable task = new MyRunnable();
        //两个核心线程处理
        pool.execute(task);
        pool.execute(task);
        //任务队列
        pool.execute(task);
        pool.execute(task);
        pool.execute(task);
        pool.execute(task);
        //创建临时线程
        pool.execute(task);
        //不创建,拒绝策略被触发(上面设置的是抛出异常)
        pool.execute(task);
        pool.execute(task);

        //关闭线程池
        pool.shutdownNow(); //立即关闭,即使任务没有完成,丢失任务
        pool.shutdown(); //会等待全部任务执行完毕后再关闭
    }

线程池处理Callable任务

  • 使用ExecutorService的方法:

  • Future<T> submit(Callable<T> command)

public class MyCallable implements Callable<String>{
    private int n;


    public MyCallable(int n) {
        this.n = n;
    }

    @Override
    public String call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= n; i++) {
            sum += i;
        }
        return Thread.currentThread().getName() + "执行 1-" + n + "的和,结果:" + sum;
    }
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService pool = new ThreadPoolExecutor(2,3,6,TimeUnit.SECONDS
                                                ,new ArrayBlockingQueue<>(4),Executors.defaultThreadFactory(),
                                                new ThreadPoolExecutor.AbortPolicy());

        Future<String> f1 = pool.submit(new MyCallable(100));
        Future<String> f2 = pool.submit(new MyCallable(200));
        Future<String> f3 = pool.submit(new MyCallable(300));
        Future<String> f4 = pool.submit(new MyCallable(400));
        Future<String> f5 = pool.submit(new MyCallable(500));
        Future<String> f6 = pool.submit(new MyCallable(600));
        Future<String> f7 = pool.submit(new MyCallable(700));


        System.out.println(f1.get());
        System.out.println(f2.get());
        System.out.println(f3.get());
        System.out.println(f4.get());
        System.out.println(f5.get());
        System.out.println(f6.get());
        System.out.println(f7.get());

Executors工具类实现线程池

  • Executors:线程池的工具类通过调用方法返回不同类型的线程池对象

方法名称

说明

public static ExecutorService newCachedThreadPool()

线程数量随着任务增加而增加,如果线程任务执行完毕且空闲了一段时间则会被回收掉

public static ExecutorService newFixedThreadPool(int nThreads)

创建固定线程数量的线程池,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程

public static ExecutorService newSingleThreadExecutor()

创建只有一个线程的线程池对象,如果该线程出现异常而结束,那么线程池会补充一个新线程

public static SchedeledExecutorService newSchedeledThreadPool(int corePoolSize)

创建一个线程池,可以实现在给定的延迟后运行任务,或者定期执行任务

Executors的底层其实也是基于线程池的实现类ThreadPoolExecutor创建线程对象的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值