Java基础_线程

进程:

进程是具有一定独立功能的程序对于数据集合上的一次运行活动,是系统进行资源分配和调度的一个独立单位;进程在执行过程中拥有独立的内存单元;

链接: https://mp.weixin.qq.com/s/KOZytR49edcP564ekTQMzA

线程:

定义线程是进程的一个实体,是CUP调度和分配的基本单位;
同一个进程中的多个线程可以并发运行;
多个线程共享内存,从而极大地提高了程序的运行效率;
线程不分主次,主函数也是一个线程;
生命周期1,New:新建创建Thread类的一个实例,线程被创建,但未启动;
2,Runnable:就绪,线程已经启动,等到CPU资源;
3,Running:启动,线程已经获取到CPU资源,正在执行任务;
4,Blocked:阻塞,正在运行的线程让出CPU资源并暂停执行;
5,Dead:死亡,线程执行完成或者被其他线程杀死;
特性1,原子性:保证数据一致;
2,有序性:保证线程之间顺序执行;
3,可见性:对其他线程可见;
说明启动线程,是调用线程的start()方法,此时线程进入可执行状态;
run()方法只是thread的一个普通方法调用,调用线程的run()方法还是在主线程里执行;调用线程的start()方法会启动线程然后自动调用 run ()方法

线程实现之继承Thread类:

每个线程都独立,不共享资源;

代码路径:E:\IDEA_Study\Scattered_Study\Study\src\线程\ThreadTest.java

线程实现之实现Runnable接口:

每个线程共享了对象资源;

代码路径:E:\IDEA_Study\Scattered_Study\Study\src\线程\RunnableTest.java

线程池:

作用: 减少创建和销毁线程的次数,工作线程可以重复使用;可以根据系统承受能力调整线程池中执行线程的数量,防止消耗过多的内存;

工作流程: 当有新任务提交到线程池,判断是否达到corePoolSize,如果没有达到,创建一个新线程,并将该任务作为该线程的第一个任务,如果达到,则放入缓存队列;如果缓存队列已满,判断是否达到maximumPoolSize,如果没有达到,则创建创建非核心线程执行任务;如果达到,则采用线程拒绝策略处理;

代码路径:E:\IDEA_Study\Scattered_Study\Study\src\线程\ThreadPoolTest.java

链接: https://blog.csdn.net/HepBen/article/details/80088719

线程池创建方式:

Executors.newFixedThreadPool创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待;
可能导致内存溢出
Executors.newCachedThreadPool创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程;
可能导致CPU达到100%
Executors.newSingleThreadExecutor创建单个线程数的线程池,它可以保证先进先出的执行顺序;
可能导致内存溢出
Executors.newScheduledThreadPool创建一个可以执行延迟任务的线程池;
Executors.newSingleThreadScheduledExecutor创建一个单线程的可以执行延迟任务的线程池;
Executors.newWorkStealingPool创建一个抢占式执行的线程池(任务执行顺序不确定);【JDK 1.8 添加】

newCachedThreadPool:

 public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

newFixedThreadPool:

 public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

newSingleThreadExecutor:

 public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

线程池的核心参数:

 public ThreadPoolExecutor(int corePoolSize, //核心线程数
                              int maximumPoolSize,//最大线程数
                              long keepAliveTime,//线程存活时间
                              TimeUnit unit,//时间单位
                              BlockingQueue<Runnable> workQueue,//线程池队列,存放待执行任务
                              ThreadFactory threadFactory,//线程工程,线程在这里生成
                              RejectedExecutionHandler handler//拒绝策略,当线程池线程数已满,并且工作队列达到限制,新提交的任务使用拒绝策略处理 ) {
 ...

线程池的workQueue参数:

ArrayBlockingQueue一个由数组结构组成的有界阻塞队列
LinkedBlockingQueue一个由链表结构组成的有界阻塞队列
SynchronousQueue一个不存储元素的阻塞队列,即直接提交给线程不保持它们
PriorityBlockingQueue一个支持优先级排序的无界阻塞队列
DelayQueue一个使用优先级队列实现的无界阻塞队列,只有在延迟期满时才能从中提取元素
LinkedTransferQueue一个由链表结构组成的无界阻塞队列。与SynchronousQueue类似,还含有非阻塞方法
LinkedBlockingDeque一个由链表结构组成的双向阻塞队列

线程池拒绝策略:

AbortPolicy(默认丢弃任务并抛出RejectedExecutionException异常针对比较关键的业务,推荐使用此拒绝策略,这样子在系统不能承载更大的并发量的时候,能够及时的通过异常发现
DiscardPolicy丢弃任务,但是不抛出异常一些无关紧要的业务建议采用此策略
DiscardOldestPolicy抛弃队列头部(最旧)的一个任务,并执行当前任务
CallerRunsPolicy使用当前调用的线程来执行此任务

:如果我们想要根据实际业务场景需要,设置其他的线程池拒绝策略,可以通过ThreadPoolExecutor重载的构造方法进行设置;

线程池的状态:

在这里插入图片描述

RUNNING运行状态,该状态下线程池可以接受新的任务,也可以处理阻塞队列中的任务
执行 shutdown 方法可进入 SHUTDOWN 状态
执行 shutdownNow 方法可进入 STOP 状态
SHUTDOWN待关闭状态 ,不再接受新的任务,继续处理阻塞队列中的任务
当阻塞队列中的任务为空,并且工作线程数为0时,进入 TIDYING 状态
STOP停止状态,不接收新任务,也不处理阻塞队列中的任务,并且会尝试结束执行中的任务
当工作线程数为0时,进入 TIDYING 状态
TIDYING整理状态,此时任务都已经执行完毕,并且也没有工作线程
执行 terminated 方法后进入 TERMINATED 状态
TERMINATED终止状态,此时线程池完全终止了,并完成了所有资源的释放

线程池的原理:

在这里插入图片描述

execute方法:

execute方法源码分析:


public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
/*
 * clt记录着runState和workerCount
 */
    int c = ctl.get();
/*
 * workerCountOf方法取出低29位的值,表示当前活动的线程数;
 * 如果当前活动线程数小于corePoolSize,则新建一个线程放入线程池中;
 * 并把任务添加到该线程中。
 */
    if (workerCountOf(c) < corePoolSize) {
/*
 * addWorker中的第二个参数表示限制添加线程的数量是根据corePoolSize来判断还是maximumPoolSize来判断;
 * 如果为true,根据corePoolSize来判断;
 * 如果为false,则根据maximumPoolSize来判断
 */
        if (addWorker(command, true))
            return;
/*
 * 如果添加失败,则重新获取ctl值
 */
        c = ctl.get();
    }
/*
 * 如果当前线程池是运行状态并且任务添加到队列成功
 */
    if (isRunning(c) && workQueue.offer(command)) {
// 重新获取ctl值
        int recheck = ctl.get();
 // 再次判断线程池的运行状态,如果不是运行状态,由于之前已经把command添加到workQueue中了,
// 这时需要移除该command
// 执行过后通过handler使用拒绝策略对该任务进行处理,整个方法返回
        if (! isRunning(recheck) && remove(command))
            reject(command);
/*
 * 获取线程池中的有效线程数,如果数量是0,则执行addWorker方法
 * 这里传入的参数表示:
 * 1. 第一个参数为null,表示在线程池中创建一个线程,但不去启动;
 * 2. 第二个参数为false,将线程池的有限线程数量的上限设置为maximumPoolSize,添加线程时根据maximumPoolSize来判断;
 * 如果判断workerCount大于0,则直接返回,在workQueue中新增的command会在将来的某个时刻被执行。
 */
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
/*
 * 如果执行到这里,有两种情况:
 * 1. 线程池已经不是RUNNING状态;
 * 2. 线程池是RUNNING状态,但workerCount >= corePoolSize并且workQueue已满。
 * 这时,再次调用addWorker方法,但第二个参数传入为false,将线程池的有限线程数量的上限设置为maximumPoolSize;
 * 如果失败则拒绝该任务
 */
    else if (!addWorker(command, false))
        reject(command);
}

简单来说,在执行execute()方法时如果状态一直是RUNNING时,的执行过程如下:
1,如果workerCount < corePoolSize,则创建并启动一个线程来执行新提交的任务;
2,如果workerCount >= corePoolSize,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中;
3,如果workerCount >= corePoolSize && workerCount < maximumPoolSize,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务;
4,如果workerCount >= maximumPoolSize,并且线程池内的阻塞队列已满, 则根据拒绝策略来处理该任务, 默认的处理方式是直接抛异常。
这里要注意一下addWorker(null, false);,也就是创建一个线程,但并没有传入任务,因为任务已经被添加到workQueue中了,所以worker在执行的时候,会直接从workQueue中获取任务。所以,在workerCountOf(recheck) == 0时执行addWorker(null, false);也是为了保证线程池在RUNNING状态下必须要有一个线程来执行任务。

execute方法执行流程:

在这里插入图片描述

sleep和wait的区别:

sleep: 让出资源,但监控状态依然保持,指定时间到了之后自动恢复,即sleep过程中线程没有释放对象锁;sleep需要抛出异常;sleep是Thread类方法;Sleep可以在任何地方使用;

wait: 会释放对象锁,让其他线程可以获取对象锁,线程wait后必须等待其他线程调用notify或notifyAll方法唤醒该线程;wait是Object方法;wait只能在synchronized块或synchronized方法中使用;

并行和并发的区别:

并行: 多个任务同时执行;
并发: 根据虚拟机分配的时间片分时间运行不同的任务,同一时间只有一个任务在进行;

停止线程的方式:

1、使用退出标志,使线程正常退出,也就是当run方法完成后线程终止;

2、使用stop方法强制终止线程(不推荐使用);

3、使用interrupt方法中断线程,使线程处于阻塞状态;

守护线程:

Java的线程分为两种:User Thread(用户线程)、DaemonThread(守护线程);
当进程不存在或者主线程停止时,守护线程也会停止;使用setDaemon(true)方法设置为守护线程;

join方法:

让其他线程变为等待,只有当前线程执行完成后,等待的线程才会被释放;

Volatile关键字:

使变量在多个线程之间可见;volatile只能让被他修饰内容具有可见性,但不能保证它具有原子性;

synchronized关键字:

可以修饰代码块,方法,但是不能修饰构造器,成员变量;
任何线程进入同步代码块,同步方法之前,必须先获取对同步监视器的锁定;

volatile和synchronized的区别:

volatile修饰的变量不保留拷贝,直接访问主内存中的;
synchronized修饰的方法或者代码块时,能保留同一时刻最多一个线程访问该代码;

链接: https://blog.csdn.net/suifeng3051/article/details/52611233

Synchronized和Lock的区别:

Lock能完成Sychronized的全部功能;有更精确的线程语义和更好的性能,不强制要求一定要获取锁;
Sychronized会自动释放锁,但是Lock需要程序员手动释放,最好在finally中释放;

共享内存模型:

Java内存模型: 简称JMM,JMM决定一个线程对共享变量的写入何时对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化;

ThreadLocal:

ThreadLocal: 提供线程内的局部变量,为变量在每个线程中都创建了一个副本,这种变量在多线程环境下访问时能够保证各个线程里变量的独立性;

信号量:

Semaphore: 用来保护一个或者多个共享资源的访问,Semaphore内部维护了一个计数器,其值为可以访问的共享资源的个数。一个线程要访问共享资源,先获得信号量,如果信号量的计数器值大于1,意味着有共享资源可以访问,则使其计数器值减去1,再访问共享资源。如果计数器值为0,线程进入休眠。当某个线程使用完共享资源后,释放信号量,并将信号量内部的计数器加1,之前进入休眠的线程将被唤醒并再次试图获得信号量;

临界资源:

多个线程共享的资源;

同步代码块:

使用synchronized将run()方法里面的方法修改为同步代码块,同步监视器就是account对象,符合"加锁-修改-解锁"的逻辑,保证了并发线程中任一时刻只有一个线程在修改共享资源的代码区;

链接: https://www.cnblogs.com/lyy-2016/p/6264960.html

死锁:

线程之间资源相互等待;

代码路径:E:\IDEA_Study\Scattered_Study\Study\src\线程\DeadLockTest.java

释放同步监视器:

线程执行同步代码块或同步方法时,程序调用Thread.sleep(),Thread.yield()方法来暂停当前线程的执行,当前线程不会释放同步监视器;线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放同步监视器;

生产者与消费者设计模式:

代码路径:E:\IDEA_Study\Scattered_Study\Study\src\线程\StorqgeTest.java

链接: https://www.cnblogs.com/Ming8006/p/7243858.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值