JavaHigh02-多线程

一、线程的状态和新建

1、线程的状态的5个阶段:

新生:这种情况指的是,通过New关键字创建了Thread类(或其子类)的对象
就绪:这种情况指的是Thread类的对象调用了start()方法
运行:这时的线程指的是获得CPU的RUNNABLE线程,RUNNING状态是所有线程都希望获得的状态
死亡:处于RUNNING状态的线程,在执行完run方法之后,就变成了DEAD状态了

阻塞:这种状态指的是处于RUNNING状态的线程,出于某种原因,比如调用了sleep方法、等待用户输入等而让出当前的CPU给其他的线程。当sleep的线程醒来(sleep的时间到了)、获得了用户的输入、调用了join的其他线程结束、获得了对象锁时,线程重新变成就绪状态
在这里插入图片描述

2、线程的方法调用

Thread.sleep(int milliseconds);
t.start();#启动线程
t.join();#强制执行t这个线程
t.wait();#一直等,直到有人通知
t.wait(long milliseconds);#到时间自动恢复执行,或有人通知立即执行
t.notify();#通知另一个人
t.notifyAll();#通知其他所有人

t.interrupt(); #在其他线程中使用,中断t线程
t.setDaemon(true);#在start方法前调用setDaemon,用来结束无限循环的线程

3、创建线程的方式

创建java.lang.Thread子类,重写Thread的run方法

public class Thread1 extends Thread{
    //创建线程子类
    @Override
    public void run() {
        setName("sub1");
        for (int i = 0; i <10 ; i++) {
            System.out.println(this.getName()+"\t"+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        new Thread1().start();
    }
}

匿名内部类创建Thread对象,重写run方法

public class Thread2 {
    public static void main(String[] args) {
        //创建匿名内部类
        Thread t = new Thread(){
            @Override
            public void run() {
                for (int i = 0; i <5 ; i++) {
                    System.out.println("sub2"+"\t"+i);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        t.start();
    }
}

创建Runnable接口对象,作为Thread类的构造参数传入

public class Thread2 {
    public static void main(String[] args) {
        //创建Runnable接口对象,作为java.lang.Thread的构造参数传入
        Runnable run1 = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i <5 ; i++) {
                    System.out.println("sub:"+"\t"+i);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        Thread t = new Thread(run1);
        t.start();
    }
}

开发中:优先选择:实现Runnable接口的方式
原因:1. 实现Runnable接口的方式没类的单继承性的局限性
2. 实现Runnable接口的方式更适合来处理多个线程共享数据的情况。
联系:public class Thread implements Runnable
三种方式都需要重写run()方法,将线程要执行的逻辑声明在run()中。启动线程,都是调用的Thread类中的start()。

通过创建一个实现Callable的实现类创建线程

class NumThread implements Callable{
    //2.实现call方法,将此线程需要执行的操作声明在call()中
    @Override
    public Object call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= 100; i++) {
            if(i % 2 == 0){
                System.out.println(i);
                sum += i;
            }
        }
        return sum;
    }
}


public class ThreadNew {
    public static void main(String[] args) {
        //3.创建Callable接口实现类的对象
        NumThread numThread = new NumThread();
        //4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
        FutureTask futureTask = new FutureTask(numThread);
        //5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
        new Thread(futureTask).start();

        try {
            //6.获取Callable中call方法的返回值
            //get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。
            Object sum = futureTask.get();
            System.out.println("总和为:" + sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

}

说明:

  • 如何理解实现Callable接口的方式创建多线程比实现Runnable接口创建多线程方式强大?
    1. call()可以返回值的。
    1. call()可以抛出异常,被外面的操作捕获,获取异常的信息
    1. Callable是支持泛型的

使用线程池

class NumberThread implements Runnable{

    @Override
    public void run() {
        for(int i = 0;i <= 100;i++){
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}

class NumberThread1 implements Runnable{

    @Override
    public void run() {
        for(int i = 0;i <= 100;i++){
            if(i % 2 != 0){
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}

public class ThreadPool {

    public static void main(String[] args) {
        //1. 提供指定线程数量的线程池
        ExecutorService service = Executors.newFixedThreadPool(10);
        ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
        //设置线程池的属性
//        System.out.println(service.getClass());
//        service1.setCorePoolSize(15);
//        service1.setKeepAliveTime();


        //2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
        service.execute(new NumberThread());//适合适用于Runnable
        service.execute(new NumberThread1());//适合适用于Runnable

//        service.submit(Callable callable);//适合使用于Callable
        //3.关闭连接池
        service.shutdown();
    }

}

好处:

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

二、线程池

程池内部维护了若干个线程,没有任务的时候,这些线程都处于等待状态。如果有新任务,就分配一个空闲线程执行。如果所有线程都处于忙碌状态,新任务要么放入队列等待,要么增加一个新线程进行处理。
Java标准库提供了ExecutorService接口表示线程池

1、固定线程池

固定线程数量,并发需求超出最大线程数,则需要等其他线程释放了才能拿到,相对安全

ExecutorService fixedPool = Executors.newFixedThreadPool(int size);

2、缓存线程池

不固定线程数,小项目用,保持最小容量,根据闲置时间关掉线程

ExecutorService cachedPool = Executors.newCachedThreadPool();

3、单线程池

只有一条线程,一般用来维护系统的核心业务

ExecutorService singlePool = Executors.newSingleThreadExecutor();

4、submit提交线程池任务

submit是ExecutorService接口中定义的一个方法,以Future的形式返回线程的执行结果

xxxPool.submit(Runnable task);
Future<T> future = xxxPool.submit(Callable<T> call);
String result = future.get();

5、手动创建线程池

  • 固定:nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue(),缺点为容易任务堆积造成OOM溢出
  • 单:1, 1,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue(),缺点为容易任务堆积造成OOM溢出
  • 缓存:0,Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue(),缺点为创建大量线程造成OOM溢出

ThreadPoolExecutor()方法
参数如下:
ThreadPoolExecutor参数如下:
corePoolSize:核心线程数
maximumPoolSize:最大线程数
keepAliveTime:当线程池线程数量大于corePoolSize时候,多出来的空闲线程的存活时间
unit:参数keepAliveTime的时间单位,TimeUnit枚举类有小时毫秒微秒纳秒7种可以选择。
workQueue:线程池使用的缓冲队列
threadFactory:线程工厂,主要用来创建线程
handler:拒绝策略,拒绝处理任务时的策略

   static class Task implements Runnable{
        @Override
        public void run() {
            try {
                System.out.println(100);
                Thread.sleep(1000);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 5,
                60L, TimeUnit.SECONDS, new LinkedBlockingDeque<>(5000));
        while(true){
            threadPool.execute(new Task());
        }
     }

三、计划线程池

ScheduledExecutorService是继承于ExecutorService的一个接口,ExecutorService是继承于Executor的一个接口,创建一个ScheduledThreadPool仍然是通过Executors类

ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(int size);

1、延迟多久执行一次(一次性)

scheduledPool.schedule(Runnable run,int delay,TimeUnit unit);

ScheduledFuture<T> future = scheduledPool.scheduled(Callable<T> run
,int delay,TimeUnit unit);

2、每隔多久执行一次

runnable是任务,initialdelay多长时间后首次执行,每隔period执行一次(不管任务有没有结束)

scheduledPool.scheduleAtFixedRate(Runnable run,int initialDelay,int period,TimeUnit unit);

3、执行完延迟多久执行下一次

runnable是任务,initialdelay多长时间后首次执行,任务结束后隔多久执行下一次

scheduledPool.scheduleWithFixedDelay(Runnable run,int initialDely,int delay,TimeUnit unit);

4、释放线程池

xxxPool.shutdown();
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值