进程和线程(要关注哦)

Posted get棒棒

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了进程和线程(要关注哦)相关的知识,希望对你有一定的参考价值。

👌 棒棒有言:生活充满了起起落落。关键在于,在顶端时好好享受;在低谷时不失勇气。与其在风雨中逃避,不如在雷电中舞蹈,即便淋得透湿,也是领略生命的快意。我们最终都要远行,最终都要跟稚嫩的自己告别。也许路途有点艰辛,有点孤独,但熬过了痛苦,我们才能得以成长。

👌 本章简介:1.进程:一个进程 是 一个包含有自身地址的程序,每个独立执行的程序都称为进程。比如: 谷歌游览器 是一个进程,微信也是一个进程,Evernote 也是一个进程,正在操作系统中运行的 “。exe”(正在运行的应用程序)都可以理解为一个进程。

2.线程:进程中独立运行的子任务就是一个线程。像WeChat.exe运行的时候就有很多子任务在运行,比如聊天线程、好友视频线程、下载文件线程等等。

3.多线程:在程序中执行的多个线程,每个线程完成一个功能,并于其他的线程并发执行,这就是多线程系统可以分配每个进程一段有限的使用CPU的时间(CPU 时间片),CPU在短时间中执行某个进程,然后下一个时间片跳到另一个进程中去执行。由于CPU转换比较快,所以是的每一个进程好像是同时执行一样。

👍  作者:get棒棒

👍  重要:请给个关注哦!

目录

一.认识进程和线程

1. 进程

2. 线程

3. 多线程的优势

二.编写线程类

1. 使用Thread类创建线程

2. 使用Runnable接口创建线程

三.线程状态和线程调度

一、线程的状态

1. 新生状态(New Thread)

2. 可运行状态(Runnable)

3. 阻塞状态(Blocked)

4. 死亡状态(Dead)

二、线程调度

1. 线程优先级

2. 实现线程调度的方法

四. 线程同步和线程间的通信

一、线程同步的必要性

二、 实现线程同步

1. 同步方法

2. 同步代码块

3. 死锁

四、实现线程间通信

五.注解与多线程章节总结


一.认识进程和线程

(1)计算机的操作系统大多采用多任务分时设计多任务是指在一个操作系统中可以同时运行

多个程序。

(2)例如在使用QQ聊天的同时听音乐,即有多个独立运行的任务,每个任务对应一个进程,每个

进程又可以产生多个线程

1. 进程

(1)程序(Program)是对数据描述与操作的代码的集合,如Office中的Word、暴风影音等应用

程序。

(2)进程(Process)是程序的一次动态执行过程,它对应了从代码加载、执行至执行完毕的一

个完成过程,这个过程也是进程本身从产生、发展至消亡的过程。

(3)操作系统同时管理一个计算机系统中的多个进程,让计算机系统中的多个进程轮流使用CPU

资源,或者共享操作系统的其它资源。

(4)进程有如下特点:

-->进程是系统运行程序的基本单位。

-->每一个进程都有自己独立的一块内存空间、一组系统资源。

-->每一个进程的内部数据和状态都是完全独立的。

2. 线程

(1)线程是进程中执行运算的最小单位,一个进程在其执行过程中可以产生多个线程,而线程必

须在某个进程内执行。

(2)线程是进程内部的一个执行单元,是可完成一个独立任务的顺序控制流程,如果在一个进程

中同时运行了多个线程,用来完成不同的工作,则称之为多线程。

(3)线程按处理级别可以分为核心级线程和用户级线程。

--》核心级线程

   -->核心级线程是和系统任务相关的线程,它负责处理不同进程之间的多个线程。

    -->允许不同进程中的线程按照同一相对优先调度方法对线程进行调度,使它们有条不紊地工

作,可以发挥多处理器的并发优势,以充分利用计算机的软/硬件资源。

--》用户级线程

 -->在开发程序时,由于程序的需要而编写的线程即用户级线程,这些线程的创建、执行和消亡都

是编写在应用程序时进行控制的。

    -->对于用户级线程的切换,通常发生在一个应用程序的诸多线程之间,如迅雷中的多线程下载

就属于用户线程。

-->多线程可以改善用户体验。具有多个线程的进程能更好地表达和解决现实世界的具体问题,多

线程是计算机应用开发和程序设计的一项重要的实用技术。

(4)线程和进程既有联系又有区别:
 

--》一个进程中至少要有一个线程。

--》资源分配给进程,同一进程的所有线程共享该进程的所有资源。

--》处理机分配给线程,即真正在处理机上运行的是线程。

3. 多线程的优势

--》多线程程序可以带来更好的用户体验,避免因程序执行过慢而导致计算机出现计算机死机或者

白屏的情况。

--》多线程程序可以最大限度地提高计算机系统的利用效率。如迅雷的多线程下载。

二.编写线程类

(1)每个程序至少自动拥有一个线程,称为主线程。

(2)当程序加载到内存时启动主线程

(3)Java程序中的public static void main()方法是主线程的入口,运行Java程序时,会先执行这

个方法。

(4)开发中,用户编写的线程一般都是指除了主线程之外的其他线程。

(5)使用一个线程的过程可以分为以下4个步骤:
 

第一步:定义一个线程,同时指明这个线程所要执行的代码,即期望完成的功能。

第二步:创建线程对象。

第三步:启动线程

第四部:终止线程。

public class MyThread extends Thread
    //重写run()方法
	public void run()
		for(int i=1;i<100;i++)			System.out.println(
        Thread.currentThread().getName()+":"+i);




public static void main(String[] args) 
		MyThread thread = new MyThread();
		thread.start(); //启动线程

(6)定义一个线程类通常有两种方法,分别是继承java.lang.Thread类和实现java.lang.Runnable

接口。

1. 使用Thread类创建线程

--》Java提供了java.lang.Thread类支持多线程编程,该类提供了大量的方法来控制和操作线程,

常用方法如下:

 --》创建线程时继承Thread类并重写Thread类中的run()方法。

--》Thread类的run()方法是线程要执行操作任务的方法,所以线程要执行的操作代码都需要写在

run()方法中,并通过调用start()方法来启动线程。

2. 使用Runnable接口创建线程

--》使用继承Thread类的方式创建线程简单明了,符合大家的习惯,但它有一个缺点,如果定义的

类已经继承了其他类则无法再继承Thread类。使用Runnable接口创建线程的方式可以解决上述问

题。

--》Runnable接口中声明了一个run()方法,即public void run()。

   --》一个类可以通过实现Runnable接口并实现run()方法完成线程的所有活动,已实现的run()方法

称为该对象的线程体。

--》任何一个实现Runnable接口的对象都可以作为一个线程的目标对象。

(7)两种创建线程的方式有各自的特点和应用领域:

--》直接继承Thread类的方式编写简单,可以直接操作线程,适用于单重继承的情况;

--》实现Runnable接口的方式,当一个线程继承了另一个类时,就只能用实现Runnable接口的方

法来创建线程,

而且这种方式还可以使多个线程之间使用同一个Runnable对象。

三.线程状态和线程调度

一、线程的状态

(1)线程的生命周期可以分为4个阶段,即线程的4种状态,分别为新生状态、可运行状态、阻塞

状态和死亡状态。

(2)一个具有生命的线程,总是处于上述4种状态之一。

1. 新生状态(New Thread)

-->创建线程对象之后,尚未调用其start()方法之前,这个线程就有了生命,此时线程仅仅是一个空

对象,系统没有为其分配资源。此时只能启动和终止线程,任何其它操作都会引发异常。

2. 可运行状态(Runnable)

-->当调用了start()方法启动线程之后,系统为该线程分配除CPU外的所需资源,这个线程就有了运

行的机会,线程处于可运行的状态,在这个状态当中,该线程对象可能正在运行,也可能尚未运

行。

-->对于只有一个CPU的机器而言,任何时刻只能有一个处于可运行状态的线程占用处理机,获得

CPU资源,此时系统真正运行线程的run()方法。

public class MyRunnable implements Runnable
	public void run()
		for(int i=1;i<100;i++)			System.out.println(
        Thread.currentThread().getName()+":"+i);




public static void main(String[] args) 
		MyRunnable myRunnable = new MyRunnable();
		Thread myThread = new Thread(myRunnable);	
		thread.start(); //启动线程


3. 阻塞状态(Blocked)

-->一个正在运行的线程因某种原因不能继续运行时,进入阻塞状态。

-->阻塞状态是一种“不可运行”的状态,而处于这种状态的线程在得到一个特定的事件之后会转回可

运行状态。

-->导致一个线程被阻塞有以下原因:

--》调用了Thread类的静态方法sleep()。

--》一个线程执行到一个I/O操作时,如果I/O操作尚未完成,则线程将被阻塞。

--》如果一个线程的执行需要用一个对象的锁,而这个对象的锁正被别的线程占用,那么此线程被

阻塞。

--》线程的suspend()方法被调用而使线程被挂起时,线程进入阻塞状态。但suspend()容易导致死

锁,已经被JDK列为过期方法,基本不再使用。

-->处于阻塞状态的线程可以转回到可运行状态,例如,在调用sleep()方法之后,这个线程的睡眠

时间已经达到了指定的间隔,那么它就有可能重新回到可运行状态。或当一个线程等待的锁变得可

用的时候,那么这个线程也会从被阻塞的状态转入可运行状态。

4. 死亡状态(Dead)

-->一个线程的run()方法运行完毕、stop()方法被调用或者在运行过程中出现未捕获的异常时,线程

进入死亡状态。

二、线程调度

(1)当同一时刻有多个线程处于可运行状态,它们需要排队等待CPU资源,每个线程会自动获得

一个线程的优先级(Priority),优先级的高低反映线程的重要或紧急程度。
(2)可运行的线程按优先级排队,线程调度依据建立在优先级基础上的“先到先服务”原则。

(3)线程调度管理器负责线程排队和在线程间分配CPU,并按线程调度算法进行调度。当线程调

度管理器选中某个线程时,该线程获得CPU资源进入运行状态。

(4)线程调度是抢占式调度,即在当前线程执行过程中如果有一个更高优先级的线程进入可运行

状态,则这个更高优先级的线程立即被调度执行。

1. 线程优先级

-->线程的优先级用1~10表示,10表示优先级最高,默认值是5。

-->每个优先级对应一个Thread类的公用静态常量。

--》public static final int NORM_PRIORITY=5;

--》public static final int MIN_PRIORITY=1;

--》public static final int MAX_PRIORITY=10;

-->每个线程的优先级都介于Thread.MIN_PRIORITY和Thread.MAX_PRIORITY之间

-->线程的优先级可以通过setPriority(int grade)方法更改,此方法的参数表示要设置的优先级,它

必须是一个1-10之间的整数。

2. 实现线程调度的方法

-->join()方法

    join()方法使当前线程暂停执行,等待调用该方法的线程结束后再继续执行本线程。它有3种重载

形式: 

--》public final void join()

--》public final void join(long mills)

--》public final void join(long mills,int nanos)

public static void main(String[] args) 
      Thread temp = new Thread(new MyThread());
      temp.start();
      for(int i=0;i<20;i++)
           if(i==5)				
           try 
	temp.join();
            catch (InterruptedException e) 
	e.printStackTrace(); 			
     System.out.println(Thread.currentThread().getName()+"运行:"+i);
       
      //省略代码…

-->sleep()方法

    sleep()方法会让当前线程睡眠(停止执行)millis毫秒,线程由运行中的状态进入不可运行状

态,睡眠时间过后线程会再次进入可运行状态。语法结构如下: 

--》public static void sleep(long millis)

-->yield()方法

    yield()方法可让当前线程暂停执行,允许其它线程执行,但该线程仍处于可运行状态,并不变为

阻塞状态。此时,系统选择其他相同或更高优先级线程执行,若无其它相同或更高优先级线程,则

该线程继续执行。  

public class MyThread implements Runnable
     public void run()
          for(int i=0;i<5;i++)
    	System.out.println(Thread.currentThread().
              getName()+"正在运行:"+i);
    	 if(i==3)
    	    System.out.print("线程礼让:");
	    Thread.yield();	
                      

-->sleep()方法和yield()方法的区别

四. 线程同步和线程间的通信

一、线程同步的必要性

前面说的线程都是独立的,而且异步执行,也就是说每个线程都包含了运行时所需要的数据或方

法,而不需要外部资源或方法,也不必关心其它线程的状态或行为。 

但是经常有一些同时运行的线程需要共享数据,此时就需要考虑其他线程的状态和行为,否则就不

能保证程序运行结果的正确性。 

二、 实现线程同步

(1)当两个线程或多个线程需要访问同一资源时,需要以某种顺序来确保该资源在某一时刻只能

被一个线程使用的方式称为线程同步。 

(2)采用同步来控制线程的执行有两种方式,即同步方法和同步代码块。这两种方式都使用

synchronized关键字实现。 

1. 同步方法

-->通过在方法声明中加入synchronized关键字来声明同步方法。

-->使用synchronized修饰的方法控制对类成员变量的访问。每个类实例对应一把锁,方法一旦执

行,就独占该锁,直到该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可

执 行状态。 

-->这种机制确保了同一时刻对应每一个实例,其所有声明为synchronized的方法只能有一个处于

可执行状态,从而有效的避免了类成员变量的访问冲突。 

-->同步方法的语法格式如下:

访问修饰符 synchronized 返回类型 方法名()  或者 synchronized 访问修饰符 返回类型 方法名()

--》synchronized是同步关键字

--》访问修饰符是指public、private等。

2. 同步代码块

-->同步代码块的语法格式如下:

synchronized()

//需要同步访问控制的代码

-->synchronized块中的代码必须获得对象syncObject的锁,具体实现机制与同步方法一样。

-->由于同步代码块可以针对任意代码块,且可任意指定上锁的对象,故灵活性比较高。

3. 死锁

-->多线程在使用同步机制时,存在“死锁”的潜在危险。如果多个线程都处于等待状态而无法唤醒

时,就构成了死锁(DeadLock),此时处于等待状态的多个线程占用系统资源,但无法运行,因

此不会释放自身的资源。  

-->在编程时应注意死锁问题,避免死锁的有效方法是:

线程因某个条件未满足而受阻,不能让其继续占有资源;

如果有多个对象需要互斥访问,应确定线程获得锁的顺序,并保证整个程序以相反的顺序释放锁。
 

三、线程间通信的必要性

在前面的介绍中,了解了多线程编程中使用同步机制的重要性,并介绍了如何通过同步来正确地访

问共享资源。这些线程之间是相互独立的,并不存在任何的依赖关系。 

它们各自竞争CPU资源,互不相让,并且还无条件地阻止其他线程对共享资源的异步访问。然而,

有很多现实问题要求不仅要同步地访问同一共享资源,而且线程间还被彼此牵制,相互通信。 


四、实现线程间通信

(1)Java提供了如下3个方法实现线程之间的通信:

--》wait()方法:调用wait()方法会挂起当前线程,并释放共享资源的锁。

--》notify()方法:调用任意对象的notify()方法会在因调用该对象的wait()而阻塞的线程中随机选择

一个线程解除阻塞,但要等到获得锁后才真正执行。 

--》notifyAll()方法:调用了notifyAll()方法会将因调用该对象的wait()方法而阻塞的所有线程一次性

全部解除阻塞。 

(2)wait()、notify()和notifyAll()这3个方法都是Object类中final方法,被所有的类继承且不允许重

写。这3个方法只能在同步方法或者同步代码块中使用,否则会抛出异常。 

五.注解与多线程章节总结

1、 java.lang包中提供了3种标准的注解类型,称为内建注解,分别是@Override注解、@Deprecated注解以及@SuppressWarnings注解

2、 java.lang.annotation包提供了4种元注解,用来修饰其他的注解定义。分别是@Target注解、@Retention注解、@Documented注解以及@Inherited注解。

3、 线程是进程中执行运算的最小单位。一个进程在其执行过程中可以产生多个线程,而线程必须在某个进程内执行。

4、 定义一个线程类通常由两种方法,分别是继承java.lang.Thread类实现java.lang.Runnable接口。

5、 线程有新生、可运行、阻塞、死亡4种状态。

6、 线程的优先级用1~10表示,10表示优先级最高,默认值是5。每个优先级对应一个Thread类的公用静态常量。

7、 使用join()方法、sleep()方法、yield()方法可以改变线程的状态。

8、 线程同步有两种方式,即同步方法同步代码块。这两种方式都使用synchronized关键字来实现。

9、 Java提供了3个方法来实现线程之间的通信,即wait()方法、notify()方法notifyAll()方法。

 

并发任务

并发类库

在关注并发前,我们需要了解一些相关概念。

线程与进程

运行在系统上的每个程序都是一个进程。一个进程可包含多个线程。进程和线程都表示一个逻辑控制流,即一种计算过程。进程独立占用管理物理资源,线程共享同一个进程中的物理资源和数据。可以采用多进程来实现程序的并发。CPU资源是固定的,CPU通过多线程的切换实现并发。即线程轮流执行一个时间片,实现多个任务同时执行。

多线程使用要考虑用户交互与计算资源。对于计算密集型程序,计算机线程切换带来的损失要小于多线程带来的好处。对于用户交互频繁的程序,利用多线程减少串行计算用户的等待时间,但这样会加大线程切换的损失。

 

内核线程:每个线程都由操作系统内核支持,系统开销大,充分发挥计算机并行计算的优势。

轻量级进程:建立在内核之上并由内核支持的用户线程,它是内核线程的高度抽象,与内核线程是一对一的线程模型,各种操作都需要系统调度,代价高,需要频繁在用户态和内核态切换。每个轻量级进程是独立的调度单元,不会影响整个进程的工作。

用户线程:用户线程存在于单一的进程之上。各种操作不需要内核的帮助,消耗低、高效。

 

Java线程的实现

Windowslinux版,JDK采用一条java线程映射到一条轻量级进程之中。

 

线程安全

当前线程操作的同时不会因为其它线程操作而产生不同的结果。

 

多线程任务

编写多线程程序一般有三种方法,Thread,Runnable,Callable.

java.util.concurrent
接口 Callable<V>

类型参数:

V - call 方法的结果类型

所有已知子接口:

JavaCompiler.CompilationTask

 

public interface Callable<V>

返回结果并且可能抛出异常的任务。实现者定义了一个不带任何参数的叫做 call 的方法。

Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。

Executors 类包含一些从其他普通形式转换成 Callable 类的实用方法。


RunnableCallable的区别是,
(1)Callable规定的方法是call(),Runnable规定的方法是run().
(2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值得
(3)call方法可以抛出异常,run方法不可以

(4)运行Callable任务可以拿到一个Future对象,Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,如果线程没有执行完,Future.get()方法可能会阻塞当前线程的执行;如果线程出现异常,Future.get()throws InterruptedException或者ExecutionException;如果线程已经取消,会跑出CancellationException。取消由cancel 方法来执行。isDone确定任务是正常完成还是被取消了。一旦计算完成,就不能再取消计算。如果为了可取消性而使用 Future 但又不提供可用的结果,则可以声明Future<?> 形式类型、并返回 null 作为底层任务的结果。Future接口的定义如下:

 

 

java.util.concurrent
接口 Future<V>

类型参数:

V - 此 Future 的 get 方法所返回的结果类型

所有已知子接口:

Response<T>, RunnableFuture<V>, RunnableScheduledFuture<V>, ScheduledFuture<V>

所有已知实现类:

FutureTask, SwingWorker

 

public interface Future<V>
			

Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,如有必要,计算完成前可以阻塞此方法。取消则由 cancel 方法来执行。还提供了其他方法,以确定任务是正常完成还是被取消了。一旦计算完成,就不能再取消计算。如果为了可取消性而使用 Future 但又不提供可用的结果,则可以声明 Future<?> 形式类型、并返回 null 作为底层任务的结果。

 

 

  • 以runnable方式运行:

  • 线程池方式

以上是关于进程和线程(要关注哦)的主要内容,如果未能解决你的问题,请参考以下文章

HarmonyOS JS应用开发需要关注哪些线程?官方解析来啦~

HarmonyOS JS应用开发需要关注哪些线程?官方解析来啦~

Java线程及多线程技术及应用

Java的线程模型

Java 线程 面试题

Java的线程模型