什么是线程

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



    我们看到的这些单独运行的程序就是一个独立的进程,进程之间是相互独立存在的。我们上面图中的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. Spring源码解析之BeanFactoryPostProcessor(二)

    上一章,我们介绍了在AnnotationConfigApplicationContext初始化的时候,会创建AnnotatedBeanDefinitionReader和ClassPathBeanDef ...

  2. Docker一些基本操作

    1.停止所有的container,这样才能够删除其中的images: docker stop $(docker ps -a -q) 如果想要删除所有container的话再加一个指令: docker ...

  3. 虚拟机下Ubuntu共享文件夹不能显示的一种解决方法

    原文链接:https://blog.csdn.net/huyangzhilin/article/details/70666937

  4. C#(三)基础篇—方法,递归,条件分支,循环,三元操作符

    C# 本随笔为个人复习巩固知识用,多从书上总结与理解得来,如有错误麻烦指正 2020-12-03 1.方法 static void Main(string[] args) { float Sum(fl ...

  5. charles 常用功能 (六)抓包结果列表指展示关注的接口(focus on 功能)

    添加关注的接口 2.启用关注接口,添加过滤的地址 3.重新抓包结果 不在上一步配置中的接口,都会隐藏在other host中

  6. PyQt学习随笔:使用QPropertyAnimation开发简单动画

    QPropertyAnimation是PyQt5.QtCore模块提供的动画设计类,使用该类可以针对PyQt的界面对象进行动画播放,如果要针对一个指定对象进行动画播放,包括如下步骤: 一.创建动画对象 ...

  7. 蒲公英 · JELLY技术周刊 Vol.33: 前端基础课堂开课啦~

    蒲公英 · JELLY技术周刊 Vol.33 页面文件太大?图片过大了吧:页面加载白屏?很有可能是字体文件还没加载完:页面加载时间过长?多半是主进程被阻塞--该怎么办呢?快来小葵,咳咳,「蒲公英」前端 ...

  8. 学习笔记:Kruscal 重构树

    网上感觉没有什么很详细 + 证明的讲解啊) 前置:Kruskal 求最小生成树. 这个算法可以将一棵树 / 无向连通图重构成一颗有性质的新树. 算法可以解决一些树上瓶颈边权之类的问题,可以把需要持久化 ...

  9. Codeforces Edu Round 63 A-E

    A. Reverse a Substring 容易看出,只要符合递增顺序就符合\(NO\),否则则可以找到一组,每次记录最大值比较即可. #include <cstdio> #includ ...

  10. hiveSQL和MySQL区别

    1.hive支持按行分割,按字段分割,如按','分割: lateral view explode(split( , ',')) 2.hive不支持等值连接,即不支持where a.id = b.id的 ...