初步探讨线程问题

一、线程与进程

在了解线程前,首先我们先了解一下什么叫进程。

进程:进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。简单来说,一个应用程序的运行就可以被看做是一个进程。

   狭义定义:进程是正在运行的程序的实例(an instance of a computer program that is being executed)。

  广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。

  进程的概念主要有两点:第一,进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。

第二,进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时(操作系统执行之),它才能成为一个活动的实体,我们称其为进程。进程是操作系统中最基本、重要的概念。是多道程序系统出现后,为了刻画系统内部出现的动态情况,描述系统内部各道程序的活动规律引进的一个概念,所有多道程序设计操作系统都建立在进程的基础上。

线程:线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

  线程是独立调度和分派的基本单位。同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)。

  一个进程可以有很多线程,每条线程并行执行不同的任务。

注意点:

  线程是进程的组成部分,一个线程必须有一个父进程
  一个程序运行后至少有一个进程,一个进程至少要包含一个线程
  线程是操作系统进行调度的单位
  线程不拥有系统资源,多个线程共享父进程的资源
  单线程的程序,只有一个执行流
  多线程的程序,有多个执行流
  多线程可以让一个应用程序的多个顺序执行流同时执行(宏观)
  多个线程是相互独立的、并发执行的
  随机执行的
  使用java命令运行Java程序一次,就是在操作系统中启动一个JVM进程,Java程序的线程都在JVM进程中

二、并行和并发

  进程只是一个静态的概念,机器上的一个.class文件,机器上的一个.exe文件,这个叫做一个进程。程序的执行过程都是这样的:首先把程序的代码放到内存的代码区里面,代码放到代码区后并没有马上开始执行,但这时候说明了一个进程准备开始,进程已经产生了,但还没有开始执行,这就是进程,所以进程其实是一个静态的概念,它本身就不能动。平常所说的进程的执行指的是进程里面主线程开始执行了,也就是main()方法开始执行了。进程是一个静态的概念,在我们机器里面实际上运行的都是线程。

  Windows操作系统是支持多线程的,它可以同时执行很多个线程,也支持多进程,因此Windows操作系统是支持多线程多进程的操作系统。Linux和Uinux也是支持多线程和多进程的操作系统。DOS就不是支持多线程和多进程了,它只支持单进程,在同一个时间点只能有一个进程在执行,这就叫单线程

  CPU难道真的很神通广大,能够同时执行那么多程序吗?不是的,CPU的执行是这样的:CPU的速度很快,一秒钟可以算好几亿次,因此CPU把自己的时间分成一个个小时间片,我这个时间片执行你一会,下一个时间片执行他一会,再下一个时间片又执行其他人一会,虽然有几十个线程,但一样可以在很短的时间内把他们通通都执行一遍,但对我们人来说,CPU的执行速度太快了,因此看起来就像是在同时执行一样,但实际上在一个时间点上,CPU只有一个线程在运行。

学习线程首先要理清楚三个概念:

  1. 进程:进程是一个静态的概念
  2. 线程:一个进程里面有一个主线程叫main()方法,是一个程序里面的,一个进程里面不同的执行路径。
  3. 在同一个时间点上,一个CPU只能支持一个线程在执行。因为CPU运行的速度很快,因此我们看起来的感觉就像是多线程一样。

  什么才是真正的多线程?如果你的机器是双CPU,或者是双核,这确确实实是多线程。

注意点:

并行
  在同一时刻,同时执行
  多核CPU
并发
  在同一时间段,快速轮换、交替执行
  在某一时刻只有某一个或某几个在执行,在宏观上具有同时执行的效果
  单核、多核

三、创建和启动线程

  线程对象必须是Thread类或Thread子类的对象
  运行Java程序的main方法,会启动一个主线程,主线程的线程执行体就是main方法的方法体
  一个线程可以启动其他的线程
  主线程的名字默认是main
  其他线程的名字按创建顺序默认是Thread-0、Thread-1、。。。

3.1、Thread子类

  1.定义Thread类的子类,重写从Thread继承的run方法,
 在方法体中编写当前线程要完成的任务(要执行的一段代码),
 run方法的方法体叫做线程执行体
  2.创建Thread子类的对象,得到一个线程对象
  3.调用线程对象的start()方法,启动当前线程
 共享资源(对象)可以时静态的或通过构造方法接收

3.2、Runnable接口

  1.定义Runnable接口的实现类,重写Runnable接口的run方法,
 该run方法的方法体就是线程执行体
 Runnable 用于代表线程的执行体
  2.调用Thread类的构造方法,创建Thread对象时,传入Runnable接口类型的对象
  3.调用线程对象的start()方法,启动当前线程
 共享资源可以作为Runnable接口实现类的成员存在

四、Thread常用方法

String getName()  :返回当前线程的名字

void setName(String name):设置当前线程的名字

static Thread currentThread():返回当前正在运行的线程

static void sleep(long ms):让当前正在执行的线程暂停一段时间,并进入阻塞状态

static void yield():让当前正在执行的线程暂停,并进入就绪状态

void join():加入线程

让当前正在执行的线程等待,等待被加入的线程执行完
阻塞状态

五、线程的生命周期

1、新建状态:

  使用new关键字创建了线程对象还未启动,没调start()

2、就绪状态:

   已经调用了线程对象的start()方法  

  线程执行体还没执行,只是表示线程可以运行了,正在等待被(调度)执行
  由操作系统线程调度器决定什么时候运行
  只能用处在新建状态线程调用start(),否则IllegalThreadStateException

3、运行状态:

  线程被调度  获得了CPU使用权,开始执行线程执行体

4、阻塞状态:

  sleep(long ms) ,经过了指定的时间
  阻塞式IO,已经返回
  阻塞状态解除后,会重新进入就绪状态,重新等待线程调度器的再次调度,
  以便接着从停止的地方,继续执行
  获取正在被其他线程持有的同步监视器的锁,获得了锁
  正在等待某个通知,其他线程发出了通知

5、死亡状态

  线程执行体正常执行完

  线程执行体抛出了异常

六、线程优先级

优先级高的线程获得执行的机会较多,优先级低的线程获得执行的机会较少

主线程优先级默认是5,其他线程优先级默认与创建它的线程的优先级相同

  Thread
  void setPriority(int priority)
  int getPriority()

优先级的有效值1-10的整数

Thread类中定义的优先级常量:

  MIN_PRIORITY 1

  NORM_PRIORITY 5

  MAX_PRIORITY 10

七、线程同步

同步锁 Lock

7.1、线程安全问题

多个线程并发修改同一个共享资源,导致的不正确结果问题
线程不安全

7.2、同步代码块

synchronized(obj){
代码块
}
synchronized:
关键字
同步obj:
同步监视器
一个Java对象,每个Java对象中都有一把锁
通常使用要并发修改的共享资源作为同步监视器
一个线程要执行同步代码块,必须先获取同步监视器的锁,对同步监视器上锁
任意时刻,一个对象的锁只能被一个线程获得
一个线程执行完同步代码块,会自动释放占有对象锁
获取锁 --> 加锁 --> 执行 --> 释放锁

7.3、同步方法

修饰符 synchronized 返回值类型 方法名(形参列表){方法体}
在同一时间段内,只能有一个线程在执行同步方法,直至执行完
同步方法的同步监视器是this

7.4、线程会释放同步监视器的锁的情况

同步代码块、同步方法正常执行完

同步代码块、同步方法抛出了异常

同步代码块、同步方法中调用执行了同步监视器对象的wait方法

同步代码块、同步方法中调用执行了sleep、yield、join、阻塞式IO暂停当前线程的执行,并不会释放同步监视器的锁

7.5、死锁

两个线程相互等待对方释放同步监视器锁的现象
避免

八、线程的通信

必须由同步监视器对象来调用,否则java.lang.IllegalMonitorStateException
Object
void wait()
void wait(long ms)
void wait(long ms, int nanos)让当前正在执行的线程释放锁,并在当前监视器上等待,并进入阻塞状态
等待接收到对应监视器对象的通知或等待指定的时间后,进入锁池
void notify()
随机唤醒一个在当前同步监视器上等待的线程
void notifyAll()
唤醒所有在当前同步监视器上等待的线程

九、ThreadLocal<T>

线程本地变量

底层是使用当前线程id作为key的Map
用于隔离多个线程各自的资源,保证当前线程获取到的值一定是自己放进去的
获取线程自己放进去的值
T 要保存的值的类型
方法
void set(T value)
当正在运行的前线程放入一个值
一个线程只能放入一个值,再次放入,覆盖
T get()
获取当前线程放入的值
void remove()
删除当前线程放入的值

原文地址:https://www.cnblogs.com/maozhaoqin/p/10857895.html

时间: 2024-10-12 12:19:36

初步探讨线程问题的相关文章

虚拟桌面技术的初步探讨

[文章标题]: 虚拟桌面技术的初步探讨  [文章作者]:  newjueqi  [作者邮箱]: [email protected][作者QQ号]: 190678908[编写语言]:VC++6.0[操作平台]: XP-SP2[作者声明]:感觉到这是一种非常有意思的技术,这篇就当成是学习笔记吧!本人只是感兴趣,没有其它目的,失误之处敬请给位大侠原谅! 本文曾发表于看学论坛http://bbs.pediy.com/showthread.PHP?t=82537                      

初步探讨WPF的ListView控件(涉及模板、查找子控件)

本文结合模板的应用初步介绍ListView的应用 一.Xaml中如何建立数据资源 大部分数据都会来自于后台代码,如何Xaml同样的建立数据源呢?比如建立一个学生List: 首先引入命名空间: xmlns:c="clr-namespace:System.Collections;assembly=mscorlib" 然后代码如下:   <c:ArrayList x:Key="stuList">            <local:Student Id=

计算机程序的思维逻辑 (78) - 线程池

上节,我们初步探讨了Java并发包中的任务执行服务,实际中,任务执行服务的主要实现机制是线程池,本节,我们就来探讨线程池. 基本概念 线程池,顾名思义,就是一个线程的池子,里面有若干线程,它们的目的就是执行提交给线程池的任务,执行完一个任务后不会退出,而是继续等待或执行新任务.线程池主要由两个概念组成,一个是任务队列,另一个是工作者线程,工作者线程主体就是一个循环,循环从队列中接受任务并执行,任务队列保存待执行的任务. 线程池的概念类似于生活中的一些排队场景,比如在火车站排队购票.在医院排队挂号

关于Excel的攻击探讨

近些年来APT趋势增长,有更多的恶意样本流传于世,这篇文章便初步探讨下Excel的一些攻击.站在攻击者的视角查看一些利用手法,知己知彼方能百战不殆. 随着趋势的发展,office文件一套成为了办公的需要,但同时软件的安全,也成为了企业部门保护信息重症之地.许多组织机构对企业发起APT攻击,其中最为常见的手法有鱼叉钓鱼.水坑攻击等. 在市面上流传的Excel样本,大都数都是如下攻击手法: office宏攻击 DDE攻击 Power Query DDE (DDE的一种) office宏攻击(Macr

[JavaWeb基础] 022.线程安全(二)

上一篇我们讲解了线程安全的问题,那么要解决线程安全的问题,我们就必须用到线程同步,保证线程之间不互相影响而产生脏数据,下面我们来讲讲具体的实现吧. 首先我们看下例子,我们有个Outputter类,用于输出 /*** * * 输出类 * @author yangqingshan * */ public class Outputter { public void output(String name) throws InterruptedException { // 逐个输出名称 for(int i

Lucene NumericRangeQuery的初步理解

理解NumericRangeQuery, 首先需要理解Lucene是如何存储数值类型. 文本初步探讨了Int和Float两种数值类型在Lucene中的存储实现,数值类型的分词原理,  最后给出NumericRangeQuery的简单理解. Lucene最初设计是实现全文检索功能, 即只处理字符串. 因此, 在处理数值时, Lucene也是将数值编码为字符串. 将数值转换成字符串, Lucene-5.2.0对应的实现类为org.apache.lucene.util.NumericUtils. 其编

深入探讨WPF的ListView控件

接上一篇博客初步探讨WPF的ListView控件(涉及模板.查找子控件)  我们继续探讨ListView的用法      一.实现排序功能 需求是这样的:假如我们把学生的分数放入ListView,当我们单机ListView头部时候实现对按分数的排序,更改Xaml如下: <Window x:Class="ContentDataTemplate.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/p

Qt 学习之路:线程和 QObject

前面两个章节我们从事件循环和线程类库两个角度阐述有关线程的问题.本章我们将深入线程间得交互,探讨线程和QObject之间的关系.在某种程度上,这才是多线程编程真正需要注意的问题. 现在我们已经讨论过事件循环.我们说,每一个 Qt 应用程序至少有一个事件循环,就是调用了QCoreApplication::exec()的那个事件循环.不过,QThread也可以开启事件循环.只不过这是一个受限于线程内部的事件循环.因此我们将处于调用main()函数的那个线程,并且由QCoreApplication::

我国现阶段非按劳分配收入探讨

我国现阶段非按劳分配收入探讨 我国社会主义初级阶段的个人消费品分配,是通过多种分配方式实现的.除了占主体地位的按劳分配方式之外,还存在许多种类的非按劳分配方式.因此,在我国城乡居民个人收入总额中,除了一部分按劳分配收入之外,还包括多种多样的非按劳分配收入.目前,在有关非按劳分配收入的概念内涵.形式特点及其存在原因等问题上,人们的看法还存在着分岐.为此,本文拟就这些问题作些初步探讨. 一.非按劳分配收入概念的内涵 所谓非按劳分配收入,就是在我国居民个人消费品收入中除去按劳分配收入之外的各种合法收入