java之线程相关juc

本文详细介绍了线程的四种实现方式:继承Thread类、实现Runnable接口、Callable接口以及线程池创建。此外,涵盖了线程生命周期、锁原理、synchronized与ReentrantLock的使用,以及线程池的原理和常见类型如CachedThreadPool和ScheduledThreadPool。
摘要由CSDN通过智能技术生成

线程实现四种方式

  1. 继承Thread类

  2. 实现Runnable接口,重写Run方法

  3. 实现Callable接口,重写Call方法,此方式有返回值

  4. 利用线程池创建

如何启动线程

第一种方式,直接new对象,调用对象的start()方法

第二种方式,new 一个实现了Runnable接口的类,将这个对象作为Thread的有参构造方法的参数传入,调用Thread对象的start()方法

第三种方式, new 一个实现了Callable的接口的类,将这个对象作为FutureTask有参构造的参数传入,再把这个FutureTask对象作为Thread的有参构造方法的参数传入,调用Thread对象的start()方法

线程的生命周期:

1.新建状态:使用new之后,线程就处于新建状态

2.就绪状态:当调用了start方法之后,线程就处于就绪状态状态

3.运行状态:如果就绪状态的线程获取到cpu资源,就会处于运行状态,此时线程最为复杂,可以变为就绪状态,阻塞状态,死亡状态

4.阻塞状态:当一个线程执行了sleep,wait等方法,就处于阻塞状态

5.死亡状态:一个运行完的线程或其他终止 条件时,该线程就处于死亡状态

锁的问题

当线程开始多了起来就称为多线程,多线程一旦出现,那就出现线程安全问题,那么锁就用来解决线程安全问题

锁按照宏观分类,分为乐观锁和悲观锁。

乐观锁:指的是当一个线程执行时,他认为别的线程必定不会去执行,也就是说别的线程必定不会影响他,但是,他在最后会确认数据是否被修改。

举例:CAS ,Atomic,数据库可以设置version防止二类丢失更新,es的version

悲观锁:指的是一个线程运行时,他认为别的线程必定会执行,所以他会把其加锁,不让别的线程执行。

举例:synchronized ReentrantLock

synchronized用法:

在相应的代码块上加synchronized(){

}

()里面的参数可以是对象,也可以是字节码,无论是什么,都必须满足只有一份的情况。

()通常是this,或者是当前类的字节码

public class SyTest implements Runnable{

    private int a = 100;
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            synchronized (this) {
                if (a > 0) {
                    System.out.println(Thread.currentThread().getName() + "打印一手" + a--);
                }
            }
        }
    }

    public static void main(String[] args) {
        SyTest syTest = new SyTest();
        new Thread(syTest).start();
        new Thread(syTest).start();
        new Thread(syTest).start();
    }
}

ReentrantLock用法:

实列化lock对象,(程序中只有一份)在会产生线程安全的代码前加上锁,在finally代码块中解除锁

public class LockTest implements Runnable{

    private int a = 100;

    private ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try{
                lock.lock();
                if (a > 0) {
                    System.out.println(Thread.currentThread().getName() + "打印一手" + a--);
                }
            }finally {
               if(lock.isLocked()){
                   lock.unlock();
               }
            }
        }
    }

    public static void main(String[] args) {
        LockTest syTest = new LockTest();
        new Thread(syTest).start();
        new Thread(syTest).start();
        new Thread(syTest).start();
    }
}

atomic基于CAS乐观锁实现

public class AtomicTest implements Runnable{

    private AtomicInteger a = new AtomicInteger(100);



    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
                if (a.get()> 0) {
                    System.out.println(Thread.currentThread().getName() + "打印一手" + a.getAndDecrement());
                }
        }
    }

    public static void main(String[] args) {
        AtomicTest syTest = new AtomicTest();
        new Thread(syTest).start();
        new Thread(syTest).start();
        new Thread(syTest).start();
    }
}

利用ThreadLocal实现线程安全

public class ThreadLocalTest{


    public static void main(String[] args) {
        ThreadLocal<String> threadLocal = new ThreadLocal<>();
        //主线程设置值
        threadLocal.set("main-value");
        new Thread(()->{
            System.out.println("thread线程获取:"+threadLocal.get());    //打印null,新开的线程是获取不到主线程设置的值的
            threadLocal.set("thread-value");
            System.out.println("thread线程获取:"+threadLocal.get());    //打印thread-value
        }).start();
        //主线程设置值
        System.out.println("main线程获取:"+threadLocal.get());  //打印 main-value
    }
}

线程池

*线程池执行流程 :* 核心线程 => 等待队列 => 非核心线程 => 拒绝策略

PS: 线程池7个参数的构造器非常重要[重要]

CorePoolSize: 核心线程数,不会被销毁

MaximumPoolSize : 最大线程数 (核心+非核心) ,非核心线程数用完之后达到空闲时间会被销毁

KeepAliveTime: 非核心线程的最大空闲时间,到了这个空闲时间没被使用,非核心线程销毁

Unit: 空闲时间单位

WorkQueue:是一个BlockingQueue阻塞队列,超过核心线程数的任务会进入队列排队

SynchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务;

LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;

ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小

ThreadFactory:使用ThreadFactory创建新线程。 推荐使用Executors.defaultThreadFactory

Handler: 拒绝策略,任务超过 最大线程数+队列排队数 ,多出来的任务该如何处理取决于Handler

AbortPolicy丢弃任务并抛出RejectedExecutionException异常;

DiscardPolicy丢弃任务,但是不抛出异常;

DiscardOldestPolicy丢弃队列最前面的任务,然后重新尝试执行任务;

CallerRunsPolicy由调用线程处理该任务

可以定义和使用其他种类的RejectedExecutionHandler类来定义拒绝策略。、

常见线程池有四种

CachedThreadPool:可缓存

FixedThreadPool :固定长度

SingleThreadPool:单个

ScheduledThreadPool:可调度

1.CachedThreadPool

这种线程池内部没有核心线程,线程的数量是有限制的 最大是Integer最大值。

在创建任务时,若有空闲的线程时则复用空闲的线程(缓存线程),若没有则新建线程。

没有工作的线程(闲置状态)在超过了60S还不做事,就会销毁。

适用:执行很多短期异步的小程序或者负载较轻的服务器。

public class cachedThread {
    public static void main(String[] args) {
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        for (int i = 0; i < 100; i++) {
            cachedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"执行");
                }
            });
        }
    }
}

FixedThreadPool :固定长度

该线程池的最大线程数等于核心线程数,所以在默认情况下,该线程池的线程不会因为闲置状态超时而被销毁。

如果当前线程数小于核心线程数,并且也有闲置线程的时候提交了任务,这时也不会去复用之前的闲置线程,会创建新的线程去执行任务(必须达到最大核心数才会复用线程)。如果当前执行任务数大于了核心线程数,大于的部分就会进入队列等待。等着有闲置的线程来执行这个任务。

适用:执行长期的任务,性能好很多

public class FixedThread {
    public static void main(String[] args) {
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 100; i++) {
            fixedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"执行");
                }
            });
        }
    }
}

SingleThreadPool:单个

有且仅有一个工作线程执行任务

所有任务按照指定顺序执行,即遵循队列的入队出队规则。

适用:一个任务一个任务执行的场景。 如同队列

public class singleThread {
    public static void main(String[] args) {
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 100; i++) {
            singleThreadExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"执行");
                }
            });
        }
    }
}

ScheduledThreadPool:可调度

DEFAULT_KEEPALIVE_MILLIS就是默认10L,这里就是10秒。这个线程池有点像是CachedThreadPool和FixedThreadPool 结合了一下。

不仅设置了核心线程数,最大线程数也是Integer.MAX_VALUE。

这个线程池是上述4个中唯一一个有延迟执行和周期执行任务的线程池。

适用:周期性执行任务的场景(定期的同步数据)

public class ScheduledThread {
    public static void main(String[] args) {
        //带缓存的线程,线程复用,没有核心线程,线程的最大值是 Integer.MAX_VALUE
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
        //延迟 n 时间后,执行一次,延迟任务
//        executorService.schedule(new Runnable() {
//            @Override
//            public void run() {
//                System.out.println("延迟任务执行.....");
//            }
//        },10, TimeUnit.SECONDS);
        //定时任务,固定 N 时间执行一次 ,按照上一次任务的开始执行时间计算下一次任务开始时间
//        executorService.scheduleAtFixedRate(()->{
//            System.out.println("定时任务 scheduleAtFixedRate 执行 time:"+System.currentTimeMillis());
//            try {
//                Thread.sleep(1000);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//        },1,1, TimeUnit.SECONDS);

        //定时任务,固定 N 时间执行一次 ,按照上一次任务的结束时间计算下一次任务开始时间
        executorService.scheduleWithFixedDelay(()->{
            System.out.println("定时任务 scheduleWithFixedDelay 执行 time:"+System.currentTimeMillis());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },1,1,TimeUnit.SECONDS);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值