Java 多线程基础

1. 多线程概述

1.1 多线程

cpu在处理数据时,同时处理多个任务,就是多线程

1.2 并发和并行

  • 并发:同一时段内,交替执行多个任务
  • 并行:同一时刻.同时执行多个任务

1.3 进程和线程

  • 进程:正在运行的一个软件或程序
  • 线程:正在运行的一个程序内的一个执行单元
  • 单线程:一个进程中只有一个线程
  • 多线程:一个进程内有多个线程

main方法执行也是需要线程的,由JVM虚拟机自动创建

2. 多线程的三种实现方式

2.1 继承Thread

  1. 创建一个Thread线程对象,并继承Thread
  2. 重写父类的run方法
  3. run方法内写线程运行的代码
  4. 创建Thread对象
  5. 调用start()方法,开启线程
Thread t = new Thread;
t.start();
t.run();
t.start()是开启一个新的线程,在新的线程中执行run方法
t.run()是在当前线程中执行run方法

2.2 实现Runnable接口

  • 每一个Runnable接口实现类对象代表一个线程任务
new Thread(Runnable对象).start
  • 用Thread的start()方法开启线程

2.3 实现callable接口

  • 重写callable的call方法
FutureTask ft = new FutrueTask(Call对象);
Thread t = new Thread(ft);
t.start();

ft.get();//得到线程执行结果
创建FutureTask对象
创建线程对象
启动线程
get()方法可以获得线程的执行结果

3. Thread的常用方法

3.1 设置获取名字

Thread(String name) / void setName(String name)//设置名字
String getName()//获取名字

3.2 获取线程对象

currentThread()//获取当前线程对象
谁执行就获取谁

3.3 睡眠方法

sleep(毫秒值)//睡眠/休眠
谁执行,谁就休眠

3.4 线程的优先级

  • 调配制度
    • 分时调度:每个线程抢到的cpu执行的时间片段相同
    • 抢占式调度:根据优先级抢占cpu的使用权
  • 线程优先级的范围是1~10
  • 默认优先级为5

3.5 守护线程

  • 守护线程和普通线程的区别
    • 普通线程:把线程执行完毕就退出
    • 只要所有普通线程执行完毕,就退出
void setDaemon(boolean false)

4. 线程安全问题

4.1 出现的原因

  1. 多线程程序
  2. 多个线程必须要共享资源
  3. 多个线程对共享资源进行增删改操作

4.2 同步代码块

对代码进行加锁

synchronized(锁对象){
	//代码
}
  • 要求
  1. 锁对象可以使任意的java对象
  2. 多线程拿到的锁是同一把锁
  • 在锁期间运行时,其他线程无法争抢执行权

4.3 锁对象唯一

4.4 同步方法

用synchronized对方法进行加锁

public [static] synchronized 返回值类型  方法名(){

}

  • 非静态同步方法的锁是this
  • 静态同步方法锁是类名.class某个类的字节码文件描述对象在一次程序的运行过程中,只会有一个
  • 加锁会造成代码执行效率的下降:因为多个线程只能争抢执行相同的代码,并且会有频繁的加锁释放锁动作,都会造成效率下降

4.5 lock锁

  • 实例化锁对象
Lock lock = new ReenTrantLock()
  • 加锁方式
lock对象.lock()
  • 解锁方式
lock对象.unlock()
  • 优势:更灵活,程序员可以灵活的控制加锁和释放锁的动作

4.6 死锁

由于锁的嵌套,导致A线程等待B线程持有的锁,而B线程也在等待A线程持有的锁。

5. 等待唤醒机制

一个线程休眠,等待另一个线程唤醒后才会继续执行

void wait()//等待
void notify()//唤醒程序(随机唤醒一个)
oid notifyAll()//唤醒所有程序
调用者是锁对象

6. 阻塞队列

创建一个队列,线程只有满足条件才会执行

  • 程序去完成一个功能时,由于某种原因,现在无法完成,程序会停住(后面的代码就不能再执行了),直到该功能完成为止!!!

6.1 创建方法

BlockingQueue:
         ArrayBlockingQueue(int capacity)
         LinkedBlockingQueue();

6.2 添加元素

put(元素)

6.3 获取元素

take()

6.4 阻塞队列实现等待唤醒机制

  • 不需要加锁,阻塞队列底层就是用:锁+wait+notify

7. 线程状态

	NEW:新建,Thread对象刚new,就处于这个状态
	RUNNABLE:可执行状态,调用start方法开启线程,就会处于这个状态
	BLOCKED:阻塞状态,抢锁没抢到
	WAITING:等待状态,它不会抢锁!
	TIMED-WAITING:限时等待
	TERMANITED:终止或者死亡状态  
一个线程从创建到销毁的过程

8. 线程池

8.1 基本原理

  • 线程池:装线程(Thread)的池子(数组/集合)
  • 解决的问题:
    1. 如果没有线程池:
      每次都需要创建新的线程,线程执行完线程任务后,销毁线程。会带来资源和时间的浪费
    2. 如果有线程池:
      一次性的创建多个线程放入到池子中,如果要使用线程,从池子中拿,使用完毕后,归还池子。从 而提高效率

8.2 Executors默认线程池

Executors:
	static ExecutorService newCachedThreadPool();

ExecutorService:
	submit(Runable r);//添加线程(Runnable接口)
	submit(Callable c);//添加线程(Callable接口)
	shutdown();//销毁线程
没有空闲线程会创建新的线程

8.3 Executors创建指定上限的线程池

ExecutorService newFixedThreadPool(int nTheads)

8.4 ThreadPoolExecutor创建线程池(常用)

public ThreadPoolExecutor(    
    						  int corePoolSize,  //核心线程数量
                              int maximumPoolSize, //最大线程数量
                              long keepAliveTime, //最大空闲时间
                              TimeUnit unit, //时间单位
                              BlockingQueue<Runnable> workQueue, //阻塞队列,存储任务
                              ThreadFactory threadFactory, //线程工厂,用来创建线程
                              RejectedExecutionHandler handler //拒绝策略
                              
                              )
参数有:
	核心线程数
	最大线程数
	最大空闲时间
	时间单位
	阻塞队列
	线程工厂
	拒绝策略

8.5 详细参数

  • 向线程池提交任务,如果 最大线程+阻塞队列容量 < 短时间内提交的任务数量 则会被拒绝
  • 默认的拒绝策略:丢弃任务,抛出异常
ThreadPoolExecutor pool = new ThreadPoolExecutor(
                2,//核心线程数量
                5,//最大线程数量
                2,//最大空闲时间
                TimeUnit.SECONDS,//时间单位
                new ArrayBlockingQueue<>(10),//阻塞队列,存储任务
                Executors.defaultThreadFactory(),//线程工厂,用来创建线程
                new ThreadPoolExecutor.AbortPolicy()//拒绝策略
        );
       while(true){
       //任务被拒绝,则重复提交任务
           try {
               pool.submit(new Runnable() {
                   @Override
                   public void run() {
                       System.out.println("Helo");
                   }
               });

               break;
           } catch (Exception e) {
               //任务被拒绝了
               //重新提交任务
               continue;
           }
       }
认为被拒绝会报错,重复提交任务,直到任务执行完毕
  • 处理规则:
    1. 先创建核心线程对象执行任务;
    2. 如果不够用,则往阻塞队列中存放;
    3. 如果阻塞队列存满了,再创建临时线程;
    4. 如果临时线程也不够用了,则拒绝;

9. volatile

9.1 多线程存在的线程安全问题

  • 高速缓存(cache)带来内存不可见问题,线程修改内存的动作没有被其他线程发现。

9.2 volatile解决方法

  • 如果一个变量被volatile关键字修饰,jvm使用时发现这是一个值易发生变化的量,因此这样的变量,jvm每次都会去内存中读取.

9.3 synchronized解决

  • synchronized会每次清空高速缓存中的内容,这样也可以达到每次都从内存中读取数据的效果

10. 原子性

10.1 概述

一个整体,不可分割!不可以被别的线程打断

10.2 count++是否具有原子性?

不具有;
一个count++,count++被编程成.class文件后,对应着有三个class指令,它代表了三个动作;其他线程就可以打断这三个操作;

10.3 count++的原子性保证

  • volatile:不能保证
  • synchronized:可以保证

10.4 AtomicInteger

AtomicInteger();
int get();//获取值
int getAndIncrement();//先获取再自增
int incrementAndGet();//先自增再获取
int addAndGet(int delta);//先添加一个值再获取
int getAndSet(int delta);//先获取再添加一个值

10.5 AtomicInteger-内存解析

  • CAS算法的原理
  • CAS: compare and swap----- 比较和交换
    while(true){
        1.读取内存的值Value,赋值给oValue:  oValue = mValue;
        2.对oValue加1,赋值给nValue: nValue = oValue+1;
        3.比较 oValue和mValue是否相等
            if(oValue == mValue){
                把nValue赋值给mValue;
                break;跳出循环
            }else{
                有其他线程把内存值mValue已经篡改了,继续重新循环再做一次
                 continue}
        
    }
    
 旧值-oValue     新值-nValue    内存值-mValue

10.6 悲观锁和乐观锁

  • 悲观锁:synchronized
    • 一上来认为别人就会修改,加锁
  • 乐观锁: CAS
    • 一上来认为别人不会改,万一别人修改,自旋

11. 并发工具类

11.1 HashTable

每个操作表的方法上都加了synchronized。

11.2 ConcurrentHashMap(jdk1.7)

ConcurrentHashMap值扩容小数组,并且每次只锁表的一小部分,哈希表永远只有16个位置

11.3 ConcurrentHashMap(jdk1.8)

ConcurrentHashMap存储数据时只锁住一部分;

如果要把一个元素存储到哈希表数组指定索引处:

  1. 该索引处是null:
    cas算法往进存
  2. 该索引处不是null:
    使用悲观锁,锁住整个链表
  • 每次只锁一小部分,而且哈希表可以扩容,单个位置采用链表的形式,元素大于等于八个时,自动转成红黑树.

11.4 CountDownLatch

使用场景:
一个线程任务可能要依赖其他多个线程任务的结果才能继续时。

	public CountDownLatch(int count)public void await():
	public void countDown():

11.5 Semaphore

使用场景:
在多线程争抢相同资源时进行限流

    public Semaphore(int permits)
    public void acquire()
    public void release()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Aming_sth

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值