什么是线程

  • 说到线程我们应该先了解下什么是进程,下面这个图片大家应该都比较熟悉吧。



    我们看到的这些单独运行的程序就是一个独立的进程,进程之间是相互独立存在的。我们上面图中的360浏览器、百度云盘等等都是独立的进程。
  • 那么什么是线程呢?

线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。

上面这个定义是引入百度百科。看起来文绉绉的,说的都是啥玩意,一点也不好理解。那我们就说点人话吧。最大工厂富士康大家都知道吧,里面是不是有很多个生产车间,一个车间里面又有很多条流水线。我们可把一个进程当成一个车间,那么线程就是车间里面的一条条流水线。一个车间的工作过程是一个进程,一个流水线的工作过程是一个线程。进程是操作系统资源分配的最小单位(制造科8号车间负责生产100w个手机壳),线程是cpu调度的最小单位(8号车间里面的每一条生产线负责生手机壳这个具体的任务)。

为什么要使用多线程

  • 更快的响应时间

    这个比较好理解,上面的例子比如一个车间一条流水线1天两班倒(24小时工作)可以生产1w个手机壳。如果要生产100w个手机壳如果一个车间就只有一条生产线那是不是需要100天。100天等你生产出来这个手机壳都过时了。那如果一个车间有50条生产线并行生产,那是不是生产100w手机壳2天就完工了。
  • 更多的处理器核心

    线程是大多数操作系统调度的基本单元,一个程序作为一个进程来运行,程序运行过程中能够创建多个线程,而一个线程在一个时刻只能运行在一个处理器核心上。
  • 更好的编程模型

    java为多线程编程提供了良好。考究并且一致的编程模型,使开发人员能够更加专注于问题解决,即为所遇到的问题建立适合的模型,而不是绞尽脑汁地考虑如何将其多线程化。一旦开发人员建立好了模型,稍作修改总是能够方便地映射到Java提供的多线程编程模型上。

多线程创建的方式

  • 第一种,通过继承Thread类创建线程类
package com.workit.demo.thread;

public class ExtendsThread extends Thread {
public static void main(String[] args) {
for(int i = 0;i<10;i++){
//创建并启动线程
new ExtendsThread().start();
}
}
@Override
public void run() {
System.out.println(this.getName());
}
}
  • 第二种,通过实现Runnable接口创建线程类
package com.workit.demo.thread;

public class RunnableThread implements Runnable {
public static void main(String[] args) {
for(int i = 0;i<10;i++){
//创建并启动线程
new Thread(new RunnableThread()).start();
}
} @Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}

网上还有其他跟多版本的实现比如说,通过CallableFuture接口创建,但是这些都无非是对以上的封装,个人认为并不能当成实现方式。oracle官网也有说到创建线程的实现方式只有两种。



感兴趣的同 学可以去看下创建线程的两种方式

怎么保证线程安全

比如车间生产手机壳是不是需要物料,领物料是不是要到仓库去领取,如果每个流水线都派一个人去领。大家都蜂拥而上乱哄哄的去抢物料。流水线A把本属于流水线B的物料给领了,流水线C又把属于D的给干没了。最后有些流水线就没了物料。那为了解决这个问题应该怎么办呢?仓库设置了一个规定,一次只能有一个人来领取物料,大家排队领取。这样子秩序是好多了,但是突然有一天一个大胖子见有人领完了,他直接插对到对头领取(传说中的非公平锁)。排在后面的人敢怒不敢言宝宝心里那个苦啊。



后面仓库又指定了个个规矩,必须按照先来后到的顺序,如有插队就罚款100元。这样大家都按照先来后到的顺序,领物料的时候先看看有没有人排队没人排队就不用排队了直接去领物料,有人排队就自觉的排到队尾去。(传说的公平锁)

哎说了一大堆废话还是回归正题吧。线程安全在三个方面体现

  • 原子性:

提供了互斥访问,同一时刻只能有一个线程对它进行操作。

流水线工人到仓库领物料只同一时刻只能有一个人领取。

  • 可见性:

一个线程对主内存的修改可以及时的被其他线程观察到。

前一个流水线工人领完了10w的物料仓库总共还剩多少物料是对后面领取物料的工人可见的。

有序性:

程序的执行顺序按照代码顺序执行,在单线程环境下,程序的执行都是有序的,但是在多线程环境下,JMM 为了性能优化,编译器和处理器会对指令进行重排,程序的执行会变成无序。

  • 比如到仓库领物料的时候,要分三个步骤,第一步先确认信息核实你是哪个车间哪个产线的,第二步填写物料领取单子,第三步到物料处领取物料。如果仓库为了提高工作效率这几个步骤有时候可以互换。可以是2->1->3的顺序。也可以3->2->1的顺序。

    下面通过代码来演示下吧怎么正确的获取物料。100条流水线每条流水线领取1000个物料最终物料应该剩余0.

  • synchronized关键字

package com.workit.demo.thread;

import jdk.nashorn.internal.runtime.options.LoggingOption;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope; import java.util.concurrent.CountDownLatch; public class RunnableThread implements Runnable {
private final static Logger LOG = LoggerFactory.getLogger(RunnableThread.class);
static CountDownLatch countDownLatch = new CountDownLatch(100);
public static void main(String[] args) throws InterruptedException {
RunnableThread runnableThread = new RunnableThread();
for(int i = 0;i<100;i++){
//创建并启动线程
Thread thread = new Thread(runnableThread);
// 流水线
thread.setName("流水线:"+i);
thread.start();
}
countDownLatch.await();
System.out.println("仓库剩余物料:"+runnableThread.count); } // 物料总数 volatile
private int count =100000; @Override
public void run() {
int remainCount =get();
countDownLatch.countDown();
LOG.info(Thread.currentThread().getName()+":领取了物料,仓库还剩于物料:"+remainCount);
} public synchronized int get(){
for(int i =0;i<1000;i++){
count--;
}
return count;
}
}

synchronized为什么可以保证线程安全呢?

Synchronized 使用 Monitor(监视锁)保证资源在多线程环境下阻塞互斥访问,是JVM层面的锁。对了它还是非公平锁(就是上面插对的那个胖子)。这里就不详细介绍了后续后转门写一个文章介绍下它。

  • 使用lock锁保证线程安全。

    Lock 也是 java.util.concurrent 包下的一个接口,定义了一系列的锁操作方法。Lock 接口主要有 ReentrantLock。它可以是公平锁或者非公平锁。通过构造 函数设置。
package com.workit.demo.thread;

import jdk.nashorn.internal.runtime.options.LoggingOption;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class RunnableThread implements Runnable {
private final static Logger LOG = LoggerFactory.getLogger(RunnableThread.class);
static CountDownLatch countDownLatch = new CountDownLatch(100);
static Lock lock = new ReentrantLock(); public static void main(String[] args) throws InterruptedException {
RunnableThread runnableThread = new RunnableThread();
for (int i = 0; i < 100; i++) {
//创建并启动线程
Thread thread = new Thread(runnableThread);
// 流水线
thread.setName("流水线:" + i);
thread.start();
}
countDownLatch.await();
System.out.println("仓库剩余物料:" + runnableThread.count); } // 物料总数volatile
private int count = 100000; @Override
public void run() {
int remainCount = getByLock();
countDownLatch.countDown();
LOG.info(Thread.currentThread().getName() + ":领取了物料,仓库还剩于物料:" + remainCount);
} public int getByLock() {
lock.lock();
try {
for (int i = 0; i < 1000; i++) {
count--;
}
return count;
} finally {
lock.unlock();
}
}
}

总结

  • 出现线程安全问题的原因?

在多线程并发环境下,多个线程共同访问同一共享内存资源时,其中一个线程对资源进行写操作的中途(写入已经开始,但还没结束),其他线程对这个写了一半的资源进了读操作,或者对这个写了一半的资源进了写操作,导致此资源出现数据错误。

  • 如何避免线程安全问题?

保证共享资源在同一时间只能由一个线程进行操作(原子性,有序性)。

将线程操作的结果及时刷新,保证其他线程可以立即获取到修改后的最新数据(可见性)。

结束

  • 由于自己才疏学浅,难免会有纰漏,假如你发现了错误的地方,还望留言给我指出来,我会对其加以修正。
  • 如果你觉得文章还不错,你的转发、分享、赞赏、点赞、留言就是对我最大的鼓励。
  • 感谢您的阅读,十分欢迎并感谢您的关注。



    参考

    https://zhuanlan.zhihu.com/p/73899015

    《java并发编程艺术》

大白话Java多线程,小白都能看的懂的哦的更多相关文章

  1. 搭建分布式事务组件 seata 的Server 端和Client 端详解(小白都能看懂)

    一,server 端的存储模式为:Server 端 存 储 模 式 (store-mode) 支 持 三 种 : file: ( 默 认 ) 单 机 模 式 , 全 局 事 务 会 话 信 息 内 存 ...

  2. 干货!上古神器 sed 教程详解,小白也能看的懂

    目录: 介绍工作原理正则表达式基本语法数字定址和正则定址基本子命令实战练习 介绍 熟悉 Linux 的同学一定知道大名鼎鼎的 Linux 三剑客,它们是 grep.awk.sed,我们今天要聊的主角就 ...

  3. 小白都能看懂的Spring源码揭秘之IOC容器源码分析

    目录 前言 IOC 只是一个 Map 集合 IOC 三大核心接口 IOC 初始化三大步骤 定位 加载 注册 总结 前言 在 Spring 框架中,大家耳熟能详的无非就是 IOC,DI,Spring M ...

  4. log4j漏洞的产生原因和解决方案,小白都能看懂!!!!

    核弹级bug Log4j,相信很多人都有所耳闻了,这两天很多读者都在问我关于这个bug的原理等一些问题,今天咱们就专门写一篇文章,一起聊一聊这个核弹级别的bug的产生原理以及怎么防止 产生原因 其实这 ...

  5. 小白都能看明白的VLAN原理解释

    为什么需要VLAN 1. 什么是VLAN? VLAN(Virtual LAN),翻译成中文是“虚拟局域网”.LAN可以是由少数几台家用计算机构成的网络,也可以是数以百计的计算机构成的企业网络.VLAN ...

  6. Unity 打包发布Android新手教学 (小白都能看懂的教学 ) [转]

    版权声明:本文为Aries原创文章,转载请标明出处.如有不足之处欢迎提出意见或建议,联系QQ531193915 扫码关注微信公众号,获取最新资源 最近在Unity的有些交流群里,发现好多Unity开发 ...

  7. 小白都能看懂的 Spring 源码揭秘之依赖注入(DI)源码分析

    目录 前言 依赖注入的入口方法 依赖注入流程分析 AbstractBeanFactory#getBean AbstractBeanFactory#doGetBean AbstractAutowireC ...

  8. 小白都能看懂的tcp三次握手

    众所周知,TCP在建立连接时需要经过三次握手.许多初学者经常对这个过程感到混乱:SYN是干什么的,怎么一会儿是1一会儿是0?怎么既有大写的ACK又有小写的ack?为什么ACK在第二次握手才开始出现?初 ...

  9. 小白都能看懂的 Spring 源码揭秘之Spring MVC

    目录 前言 Spring MVC 请求流程 Spring MVC 两大阶段 初始化 HttpServletBean#init() FrameworkServlet#initServletBean Fr ...

随机推荐

  1. C#.NET 强大的LINQ

    LINQ 是 Language INtegrated Query 单词的首字母缩写,翻译过来是语言集成查询.它为查询跨各种数据源和格式的数据提供了一致的模型,所以叫集成查询.由于这种查询并没有制造新的 ...

  2. day2(RESTful风格API)

    1.RESTful风格API  详情查看博客地址:https://www.cnblogs.com/xiaonq/p/10053234.html 1.1 什么是RESTful REST与技术无关,代表的 ...

  3. 老猿Python博客文章目录索引

    本目录提供老猿Python所有相关博文的一级目录汇总,带星号的为收费专栏: 一.专栏列表 本部分为老猿所有专栏的列表,每个专栏都有该专栏置顶的博文目录: 专栏:Python基础教程目录 专栏:* 使用 ...

  4. PyQt(Python+Qt)学习随笔:QListView的resizeMode属性

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 QListView的resizeMode属性用于控制调整视图大小时是否再次排列视图中的数据项,其类型 ...

  5. (四)一个bug的生命周期

    Bug的属性 Bug重现环境 这个应该是我们重现BUG的一个前提,如果没有这个前提,我们可能会无法重现问题,或者根本就无从下手. • 操作系统 这个是一般软件运行的一大前提,基本上所有的软件都依赖于操 ...

  6. Elements-of-Python_03_LanguageFeature

    (内容包括Python语法概述,流程控制,条件表达式) 1 Python语法 1.1 Python的特点 Python是一种完全面向对象的.解释性的.可移植的.开源的脚本编程高级语言,具有丰富的库,允 ...

  7. Android10_原理机制系列_PMS的启动及应用的安装过程

    概述 这里主要介绍 PackageManagerService(简称PMS)的启动 和 一个应用的安装过程.这里只是大致总结,供参考,不少地方同样需要进一步深入了解学习的. 该篇相关代码也是基于And ...

  8. 推荐系统实践 0x0e LS-PLM

    在之前介绍的几个模型中,存在这些问题: LR不能捕捉非线性,只能进行一次的回归预测 GBDT+LR虽然能够产生非线性特征组合,但是树模型不适用于超高维稀疏数据 FM利用二阶信息来产生变量之间的相关性, ...

  9. AcWing 361. 观光奶牛

    01规划 设答案为 \(ans\). 二分答案,设当前二分值为 \(mid\). 设一个环 \(S\) 的边权为 \(t_1, t_2, t_3...\),点权为 \(f_1, f_2, f_3... ...

  10. SpringBoot快速入门(实战篇一)

    SpringBoot快速入门(一) 一SpringBoot简介 1.spring开发经历的阶段 Spring 诞生时是 Java 企业版(Java Enterprise Edition,JEE,也称 ...