多线程(上)

一、进程

1.1 概述

-狭义定义:进程是正在运行的程序的实例
-广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。
-程序是指令、数据及其组织形式的描述,进程是程序的实体。

进程概念主要有2点:
第一:进程是一个实体。每一个进程都有自己的地址空间。分为为文本区域(text region)、数据区域(data region)、堆栈(stact region)
text region:存储处理器执行的代码;
data region:存储变量和进程执行期间使用的动态分配的内存。
stact region:存储这活动过程调用的指令和本地变量。
第二:进程是一个“执行中的程序”,程序本身是一个没有生命的实体,只有处理器富余程序生命时,它才能成为一个活动的实体,我们称其为进程。

1.2 操作系统引入进程的原因:

-从理论角度:是对正在运行的程序过程的抽象;
-从实现角度:是一种数据结构,目的在于有效管理和调度进入计算机系统主存储器运行的程序。

1.3 特点:

-动态性:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动态消亡的。
-并发性:任何进程都可以同其他进程一起并发执行
-独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位
-异步性:由于进程间的相互制约,使基础具有执行的间断性,
  即进程按各自独立的、不可预知的速度向前推进。
-结构特征:进程由 程序、数据和进程控制块三部分组成。

多个不同的进程可以包含相同的程序:一个程序在不同的数据集里就构成不同的进程,能得到不同的结果;但是执行过程中,程序不能发生改变。(不完全理解,有待提升)

1.4 状态:

进程执行时的间断性,决定了进程可能具有多种状态;

-就绪状态(Ready):进程已获得除处理器外的所需资源,等待分配处理器资源;
  1)启动条件:只要分配了处理器进程就可以执行。
  2)就绪进程可以按多个优先级来划分队列。
    低优先级队列:当进程是由于时间片用完而进入就绪状态则排入低优先级队列。
    高优先级队列:当进程由 I/O操作完成而进入就绪状态则排入高优先级队列。
-运行状态(Running):进程占用处理器资源;
    处于此状态的进程的数目小玉等于处理器的数目
      当计算机是单核时,那么处于此运行状态的进程就只有一个。
      当计算机是多核时,那么运行状态的进程就有小于等于核数。
-阻塞状态(Blocked):由于进程等待某种条件(如I/O操作或[进程同步])
   1)启动条件:满足条件之后继续执行。若没有满足某种条件,即使得到了处理器资源也无法启动。

1.5 进程的创建过程:

一旦操作系统发现了要求创建新进程的事件后,便调用进程创建原语create()按下述步骤创建一个新进程;

1)申请空白PCB。为新进程申请获得唯一的数字标识符,并从PCB集合中索取一个空白的PCB
2)为新进程分配资源。为新进程的程序和数据以及用户栈分配必要的内存空间。此时操作系统必须知道新进程所需要的内存大小(这个是怎么知道的呢?)
3)初始化进程控制块。PCB的初始化包括:
A.初始化标识信息,将系统分配的标识符和父进程标识符,填入新的PCB中。
B.初始化处理机状态信息,使程序计数器指向程序的入口地址,使栈指针指向栈顶。
C.初始化处理机控制信息,将进程的状态设置为就绪状态或静止就绪状态,对于优先级,通常是将它设置为最低优先级,除非用户以显式的方式提出高优先级的要求
4)将新进程插入就绪列表,如果进程就绪队列能够接纳新进程,便将新进程插入到就绪队列中。

1.6进程的终止过程:

如果系统发生了上述要求终止进程的某时间后,OS便调用进程终止原语,按下述过程去终止指定的进程。
1)根据被终止进程的标识符,从PCB集合中检索出该进程的PCB,从中读出该进程状态。
2)若被终止进程处于执行状态,应立即终止该进程的执行,并置调度标志为真。用于指示该进程被终止后应重新进行调度。
3)若该进程还有子孙进程,还应将其所有子孙进程予以终止,以防他们成为不可控的进程。
4)被终止的进程所拥有的全部资源,或者规划其父进程,或归还系统。
5)将被终止进程(他的PCB)从所在队列(或链表)中移除。等待其他程序来搜集信息。

1.7 多进程存在的意义:

-提高CPU使用率

二、线程

2.1 概述:

-通常在一个进程中可以包含若干个线程,这些线程可以利用进程所拥有的资源
-通常把进程作为分配资源的基本单位,而线程作为独立运行和独立调度的基本单位
-线程,是程序的执行单元,执行路径
-单线程:如果程序只有一条执行路径
-多线程:如果程序有多条执行路径

2.2 并发和并行:

-并行:是在逻辑上同时发生,指在某一个时间内同时运行多个程序
-并发:是在物理上同时发生,指在某一个时间点同时运行多个程序。
-实现:多个CPU的情况下可以实现,不过得知道如何调度和控制他们。

2.3 多线程存在的意义:

-为了提高应用程序的使用率;
-同一时刻,CPU资源分配给某个进程,而此进程中有多条执行路
  径,所以同一时刻,说不定是那条线程在执行,即线程的执行具有随机性
-多线程共享同一个进程的资源(堆内存和方法区),但是栈内存是独立存在的。一个线程一个栈(有待考察)

2.4 Java程序运行原理:

-由Java命令启动JVM,JVM启动就相当于启动了一个进程。
-然后该进程创建了一个主线程去调用main方法。

2.5 思考:

2.5.1 jvm是单线程还是多线程,为什么?

多线程;因为垃圾回收线程也最在最早时候开启,否则容易内存溢出。已知最少要启动的线程就是2个:主线程和垃圾回收线程。

2.5.2 在Java开发中是如何实现多线程的呢?

由于线程是依赖进程的,而进程是有系统启动的,所以开启线程应先由系统功能开启进程从而开启线程。Java程序是不可以直接调用系统功能的,但是Java程序可以调用C/C++,而C/C++可以调用系统功能开启进程,所以Java中提供一个类来方便我们调用C/C++从而调用系统功能。

进程和线程之间的区别:

三、学习Thread

3.1 概述:

-空线是一个线程在执行一个程序。(API1.8)
-java虚拟机的应用程序可以有多个同时执行的线程。

3.2 构造:

方法 说明
Thread() 分配一个新的 Thread对象。此时线程会个默认的名字,规则是“Thread-”+nullm,null是整数
Thread(String name) 分配一个新的 Thread对象。参数是线程的名字
Thread(Runnable target) 分配一个新的 Thread对象。
Thread(Runnable target,String name) 分配一个新的 Thread对象。

3.3 方法:

方法 说明
void run() 如果该线程是使用一个单独的 Runnable运行对象的构造,然后 Runnable对象的 run方法被调用;否则,该方法不返回。
void start() 导致该线程开始执行;java虚拟机调用这个线程的 run方法。
void setName(String name) 改变该线程的名称等于参数 name。
String getName() 返回此线程的名称。
public static Thread currentThread() 返回当前正在执行的线程对象的引用。
public final void setPriority(int newPriority) 更改此线程的优先级。
public final int getPriority() 返回此线程的优先级。
public static void sleep(long millis) 前正在执行的线程休眠(暂停执行)为指定的毫秒数加指定毫秒数,线程不失去任何监视器的所有权。
public final void join() 等待改线程死亡,即此线程先执行完了,才能执行其他线程,必须放在start之后
public static void yield() 暂停当前线程,让其他线程先执行,但意味着保证一人一次
public final void setDaemon(boolean on) 标志着该线程是 daemon线程或用户线程。当运行的线程都是后台线程时,java虚拟机退出。此方法必须在开始线程之前调用。
public final void stop() 停止线程,过时了,这个不太安全,调用这个方法后,后面的代码都不执行了,除了try的finally,demo中有尝试;
public static boolean interrupted() 中断线程,把线程的状态终止并抛出InterruptedException异常,执行完run中代码。返回值:true表示中断成功。1.6的版本此方法返回类型是void。
ThreadGroup getThreadGroup() 返回此线程所属的线程组。

3.4 使用:

3.4.1 开启线程的方式:
1)方式1:声明类继承Thread类
    步骤:
      A.声明一个类(MyThread)是继承Thread类
      B.此类(MyThread)重写Thread的run()方法
      C.创建此类对象
      D.开启线程
2)方式2:声明类实现Runnable接口
    步骤:
      A.声明一个类(MyRunnable)是实现Runnable类
      B.此类(MyRunnable)实现Runnable的run()方法
      C.创建此类对象(myRunnable)
      D.将此类对象(myRunnable)作为参数创建Thread对象(newThead)
      E.通过Thread对象(newThead)开启线程
3)方式3:声明类实现Callable<T>接口
    说明:此方式依赖线程池存在的。
                用法与Runnable ,不同的是Callable是带泛型的,泛型是其方法call的返回值。

工作中有时会使用匿名内部的方式,书写查看demo

3.4.2 关于线程名字:

方法:
void setName(String name) 改变该线程的名称等于参数 name。
String getName() 返回此线程的名称。

3.4.2.1 nulln为啥是整数呢?

默认的线程名称规则是"Thread-"+nulln,null是整数;

void setName(String name) 改变该线程的名称等于参数 name。
String getName() 返回此线程的名称。nulln为啥是整数呢?源码查看name,创建默认名称的过程。

public class Thread implements Runnable{
    //无惨构造,name默认格式
    public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null);
    }
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc) {

        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }
        //1.6版本由于name是数组,所以这里是this.name = name.toCharArray();
        this.name = name;
        //内部代码都省略了
    }

    //默认值是0,
    private static int threadInitNumber;
    private static synchronized int nextThreadNum() {
        //第一调用时,返回0后++
        return threadInitNumber++;
    }
    //此1.8版本;1.6版本此对象是个char类型数组,private char name[];
    private volatile String name;
    public final String getName() {
        return name;
    }
    //setName()在1.6版本没有加锁的
    public final synchronized void setName(String name) {
        checkAccess();
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name;
        //1.6版本没有这句
        if (threadStatus != 0) {
            setNativeName(name);
        }
    }
}
3.4.2.1 name的赋值

方式1:setName(String name)
关于 setName与start的前后书写顺序的影响在demo中

  MyThread myThread = new MyThread();
  myThread.setName("你好111");
  myThread.start()

方式2:Thread的构造方法
这个有点麻烦,因为Thread的子类还需要重写父类的构造,看下列代码

    public MyThread(String name){
        super(name);
    }
    public MyThread(){}
3.4.3 线程的调度:

方法:
public final void setPriority(int newPriority)|更改此线程的优先级。
public final int getPriority() 返回此线程的优先级。

线程调度有两种模型:

分时调度模型:所有的线程轮流使用CPU的使用权,评价分配每个线程占用CPU的时间片;

抢占式调度模型:优先优先级高的线程使用CPU;若线程优先级相同,则随机选取;优先级高的线程获取的CPU时间片相对多一些;

Java使用的是抢占式调度模型;代码中使用的方法时public final void setPriority(int newPriority) 更改此线程的优先级。参数并不是随意输入的int值。而是Thread中的字段,值分别为:MAX_PRIORITY(10),NORM_PRIORITY(5)MIN_PRIORITY(1);线程的优先级默认的是NORM_PRIORITY;setPriority()调用在start()之前(API上没有强制要求必须在start之前调用);

3.4.4 线程控制的几个方法学习:

方法:
1)public static void sleep(long millis) |前正在执行的线程休眠(暂停执行)为指定的毫秒数加指定毫秒数,线程不失去任何监视器的所有权。
2)public final void join() |等待改线程死亡,即此线程先执行完了,才能执行其他线程,必须放在start之后。
3)public static void yield() |暂停当前线程,让其他线程先执行,但意味着保证一人一次。
4)public final void setDaemon(boolean on)|标志着该线程是 daemon线程或用户线程。当运行的线程都是后台线程时,java虚拟机退出。此方法必须在开始线程之前调用。
5)public final void stop() |停止线程,过时了,这个不太安全,调用这个方法后,后面的代码都不执行了,除了try的finally,demo中有尝试。
6)public static boolean interrupted()|中断线程,把线程的状态终止并抛出InterruptedException异常,执行完run中代码。返回值:true表示中断成功。1.6的版本此方法返回类型是void。
查看demo

3.4.5 线程生命周期:
线程的声明周期.png

3.5 思考:

3.5.1 为什么要重写run方法?

类(MyThread)中不是所有的代码都需要在子线程中执行的,那么那些需要在子线程执行的代码放在那里呢?此时run()的作用就出来的,将需要在子线程执行的耗时的操作封装在run方法中。run()封装了需要被子线程执行的代码

3.5.2 run()与start()的区别?

run () :封装了需要被子线程执行的代码,直接调用就是普通方法
start():查看API,start()有2个操作:1,启动线程;2JVM调用这个线程的run方法

3.5.3 若我想开启2个子线程代码需要怎么操作,start()调用2次吗?

不是,start方法只能调用一次,否则出现IllegalThreadStateException异常(非法的线程状态,当线程已经开启状态下再次调用start方法时出现的);开启2个子线程应该是创建2个Thread对象,分别start;

3.5.4 在主线程中获取当前前程的名称?

public static Thread currentThread() 返回当前正在执行的线程对象的引用。

  String name = Thread.currentThread().getName();
  System.out.println(name);
3.5.5 为什么会有2种线程的开启方式呢?

A.方式2可以避免由于Java单继承带来的局限性;一个子类需要多线程,但是他有父类了。
B.适合多个相同程序的代码去处理同一个资源,把线程同程序代码、数据有效的分离,较好的提现了面向对象的设计思想(多个线程,同一个成员变量,方式1,创建了2个Thread对象则创建了2个成员变量,而方式2则吃创建了1此在实现Runnable类中)

3.5.6 sleep()与wait()的区别?

sleep () :指定了时间;不释放锁
wait():时间不固定;释放锁

衍生:

修饰符:native,volatile(了解)

volatile:在查看Thread的关于name的源码时遇到的,修饰Thread name的;相关连接:http://www.importnew.com/18126.html
一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
2)禁止进行指令重排序。

native:在查看Thread的关于name的源码时遇到的,修饰setNativeName(String name)方法的。相关连接:http://blog.csdn.net/funneies/article/details/8949660
native关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如C和C++)实现的文件中。Java语言本身不能对操作系统底层进行访问和操作,但是可以通过JNI接口调用其他语言来实现对底层的访问。

文章相关支持连接:
https://baike.baidu.com/item/进程/382503?fr=aladdin
此为个人笔记,如有错误,欢迎指教

参考书籍视频:
-学习视频:黑马程序员_Java基础视频-深入浅出精华版
-阅读书籍:Java核心技术 卷I(机械工业出版社)
          API1.8
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,470评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,393评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,577评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,176评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,189评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,155评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,041评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,903评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,319评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,539评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,703评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,417评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,013评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,664评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,818评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,711评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,601评论 2 353

推荐阅读更多精彩内容