Java 多线程基础-程序员宅基地

技术标签: 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()
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Aming_sth/article/details/115467238

智能推荐

初步了解SequoiaDB巨杉数据库-程序员宅基地

文章浏览阅读658次。1.SequoiaDB 简介2.整体架构3.数据库存储引擎4.核心特性。_sequoiadb

2020 年中国程序员薪资和生活现状调查报告_2020年薪酬数据报告-程序员宅基地

文章浏览阅读1.7k次,点赞6次,收藏3次。作者 | 程序员客栈来源 | ID:proginnwx根据中国互联网络信息中心(CNNIC)近日发布第 44 次《中国互联网络发展状况统计报告》。截至 2019 年 06 月,中国网民规模为 8.54 亿,较 2018 年底增加 2598 万。网上外卖用户规模达 4.21 亿,较 2018 年底增长 1516 万;网络视频用户规模达 7.59 亿,较 2018 年底增长 3391 万;我..._2020年薪酬数据报告

从 NASL 说开:低代码编程语言能饭否_网易nasl语言-程序员宅基地

文章浏览阅读1.3w次,点赞78次,收藏73次。轻舟低代码平台,NASL是其中的关键,也是最大的差异化。回头看Gartner的定义,也有LCAP(低代码平台)和CADP(无代码平台)之分,前者对开发完整性、应用独立性、逻辑完备性、可接入可集成等都有要求,所以说,NASL的实现使得轻舟低代码更符合这一理念。说到框架,大家首先想到肯定是是react,springboot这种被广泛使用的前后端框架,低代码框架本质上跟他们类似,差别在于,低代码框架通常支持从前后端到数据库的全栈模型实现,而且封装程度更高。_网易nasl语言

数字音频总线A2B开发详解三(A2B系统的Flash Program详解)_a2b技术学习-程序员宅基地

文章浏览阅读1.2k次。A2B系统的EEPROM自启动如何做,本文来告诉你_a2b技术学习

Code Blocks 安装后编译出现“编译器无效”问题。_codeblocks编译器无效-程序员宅基地

文章浏览阅读2.2k次。编写程序并编译后出现如上图报错,大致意思为:编译器安装无效,所以编译器无法运行编译器,请检查编译器路径有没有正确配置,并且给出了个修改步骤,最后说设置的路径没有找到编译器文件。。。解决方法:S1:首先我们要确定我们下载安装Code Blocks是带有编译器版本的,就是带有MinGW的版本:下载地址:http://www.codeblocks.org/downloads/26S2:然后给软件指定MinGW的所在路径:菜单栏Settings -> Compil..._codeblocks编译器无效

神操作!一行Python代码搞定一款游戏?给力!-程序员宅基地

文章浏览阅读475次。点击上方“Python大本营”,选择“置顶公众号”Python大本营 IT人的职业提升平台来源:pypl编程榜一直以来Python长期霸占编程语言排行榜前三位,其简洁,功能强大的特性..._python好游戏代码

随便推点

webview ERROR_UNSUPPORTED_SCHEME ,errorcode=-10问题处理-程序员宅基地

文章浏览阅读1.5k次。webView.setWebViewClient(new WebViewClient(){ @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { try{ if(url.startsWith("baidumap://")){ Intent _error_unsupported_scheme

embed标签-程序员宅基地

文章浏览阅读66次。Embed  (一)、基本语法:  embed src=url  说明:embed可以用来插入各种多媒体,格式可以是 Midi、Wav、AIFF、AU、MP3等等, Netscape及新版的IE 都支持。url为音频或视频文件及其路径,可以是相对路径或绝对路径。  示例:<embed src="your.mid">  (二)、属性设置:  1、自动播放...

php 上传文件漏洞,PHP -- 文件包含、文件上传漏洞-程序员宅基地

文章浏览阅读330次。PHP -- 文件包含、文件上传漏洞PHP -- 文件包含、文件上传漏洞文件包含文件引入漏洞,是由一个动态页面编译时引入另一个文件的操作。文件引入本身是没有问题,它是用于统一页面风格,减少代码冗余的一种技术。但是在特定的场景下就会形成漏洞jsp:include指令和include动作,其中include动作用于引入动态文件php:include(),include_once(),require()..._php文件上传4漏洞

配置NGINX同时运行 https 和 http_nginx 和 http无法同时启动-程序员宅基地

文章浏览阅读406次。SSL 是需要申请证书的,key和PEM文件要放到服务器路径。然后NGINX下要进行443端口和80端口的绑定。server { listen 80; server_name ietaiji.com www.ietaiji.com; root "D:/aaa/WWW/ietaiji"; index index.html_nginx 和 http无法同时启动

总结:linux之Service_linux service-程序员宅基地

文章浏览阅读1.3w次,点赞9次,收藏60次。service与systemctl关系梳理开机启动梳理_linux service

揭开数据中心光模块利润之源-程序员宅基地

文章浏览阅读194次。在数据中心里,光模块毫不起眼,当我们在高谈阔论各种数据中心高大上技术时,很少提及到光模块。不过,光模块却是数据中心的必需品,哪个数据中心也离不开光模块,而且需要的数量还不少,一块48端口网络设备就需要48个光模块,而一台框式网络设备通常有数百个端口,这些端口如果都使用上就需要数百个光模块,这样算起来数据中心需要的光模块数量是惊人的。数据中心在进行网络投资..._光模块 占 数据中心 成本