《JavaEE》第七周day4学习笔记-Thread1

一、多线程

(一)并发与并行


  • 并发:指两个或多个事件在同一个时间段内发生。
  • 并行:指两个或多个事件在同一时刻发生(同时发生)。
    在这里插入图片描述

在操作系统中,安装了多个程序,并发指的是在一段时间内宏观上有多个程序同时运行,这在单 CPU 系统中,每一时刻只能有一道程序执行,即微观上这些程序是分时的交替运行,只不过是给人的感觉是同时运行,那是因为分时交替运行的时间是非常短的。而在多个 CPU 系统中,则这些可以并发执行的程序便可以分配到多个处理器上(CPU),实现多任务并行执行,即利用每个处理器来处理一个可以并发执行的程序,这样多个程序便可以同时执行。目前电脑市场上说的多核 CPU,便是多核处理器,核越多,并行处理的程序越多,能大大的提高电脑运行的效率。

(二)进程与线程


  • 进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。

  • 线程线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。

    简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程

二、创建线程

(一)Thread类


我们已经可以完成最基本的线程开启,那么在我们完成操作过程中用到了 java.lang.Thread 类,
API中该类中定义了有关线程的一些方法,具体如下:
构造方法
public Thread():分配一个新的线程对象。
public Thread(String name):分配一个指定名字的新的线程对象。
public Thread(Runnable target):分配一个带有指定目标新的线程对象。
public Thread(Runnable target,String name):分配一个带有指定目标新的线程对象并指定名字。
常用方法
public String getName():获取当前线程名称。
public void start():导致此线程开始执行; Java虚拟机调用此线程的run方法。
public void run():此线程要执行的任务在此处定义代码。
public static void sleep(long millis):使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
public static Thread currentThread():返回对当前正在执行的线程对象的引用。

(二)创建方式1


Java使用java.lang.Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。每个线程的作用是完成一定的任务,实际上就是执行一段程序流即一段顺序执行的代码。Java使用线程执行体来代表这段程序流。Java中通过继承Thread类来创建启动多线程
步骤如下:

  1. 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。
  2. 创建Thread子类的实例,即创建了线程对象
  3. 调用线程对象的start()方法来启动该线程

(三)创建方式2


采用 java.lang.Runnable 也是非常常见的一种,我们只需要重写run方法即可。
步骤如下:

  1. 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
  2. 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正
    的线程对象。
  3. 调用线程对象的start()方法来启动线程。

(四)Thread和Runnable的区别


如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。
实现Runnable接口比继承Thread类所具有的优势

  1. 适合多个相同的程序代码的线程去共享同一个资源。
  2. 可以避免java中的单继承的局限性。
  3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
  4. 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。

(五)匿名内部类


使用线程的内匿名内部类方式,可以方便的实现每个线程执行不同的线程任务操作。
使用匿名内部类的方式实现Runnable接口,重新Runnable接口中的run方法:

 public class NoNameInnerClassThread {
   public static void main(String[] args) {            
        Runnable r = new Runnable(){
            public void run(){
                for (int i = 0; i < 20; i++) {
                   System.out.println("zhangsan:"+i);  
                }
            } 
        };
        new Thread(r).start();
       for (int i = 0; i < 20; i++) {
           System.out.println("lisi:"+i);  
        }
   } 
}

三、常用方法

(一)优先级


  • 可以通过传递参数给线程的 setPriority() 来设置线程的优先级别

  • 调整线程优先级:Java线程有优先级,优先级高的线程会获得较多的运行机会。优先级 : 只能反映 线程 的 中或者是 紧急程度 , 不能决定 是否一定先执行

Java线程的优先级用整数表示,取值范围是1~10,Thread类有以下三个静态常量:

static int MAX_PRIORITY
          线程可以具有的最高优先级,取值为10static int MIN_PRIORITY
          线程可以具有的最低优先级,取值为1static int NORM_PRIORITY
          分配给线程的默认优先级,取值为5

Thread类的setPriority()和getPriority()方法分别用来设置和获取线程的优先级。

(二)休眠


  • 使用线程的 sleep() 可以使线程休眠指定的毫秒数,在休眠结束的时候继续执行线程。

(三)礼让


  • Thread.yield() 方法作用是:暂停当前正在执行的线程对象(及放弃当前拥有的cup资源),并执行其他线程。
  • yield() 做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用 yield()
    目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证 yield() 达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。

(四)合并


  • Thread 中,join()方法的作用是调用线程等待该线程完成后,才能继续往下运行。

  • join是Thread类的一个方法,启动线程后直接调用,即join()的作用是:“等待该线程终止”,这里需要理解的就是该线程是指的主线程等待子线程的终止。也就是在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行。

为什么要用join()方法?

在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法了。

(五)中断


  • 在以前的jdk版本中,我们使用stop()方法中断线程,但是现在的jdk版本中已经不再推荐使用该方法了。
  • 每个线程都一个状态位用于标识当前线程对象是否是中断状态。isInterrupted() 是一个实例方法,主要用于判断当前线程对象的中断标志位是否被标记了,如果被标记了则返回 true 表示当前已经被中断,否则返回 false
  • 如果线程处于运行状态,那么该线程的状态就是 RUNNABLE,但是不一定所有处于 RUNNABLE 状态的线程都能获得CPU运行,在某个时间段,只能由一个线程占用CPU,那么其余的线程虽然状态是 RUNNABLE ,但是都没有处于运行状态。而我们处于 RUNNABLE 状态的线程在遭遇中断操作的时候只会设置该线程的中断标志位,并不会让线程实际中断,想要发现本线程已经被要求中断了则需要用程序去判断。
  • interrupt()是一个实例方法,该方法用于设置当前线程对象的中断标识位。

(六)临界资源


  • 多个线程间共享的数据称为临界资源(Critical Resource),由于是线程调度器负责线程的调度,程序员无法精确控制多线程的交替顺序.因此,多线程对临界资源的访问有时会导致数据的不一致.

临界资源:多个线程间共享的数据称为临界资源

临界区:
每个进程中访问临界资源的那段代码称为临界区(criticalsection),每次只允许一个进程进入临界区,进入后,不允许其他进程进入。不论是硬件临界资源还是软件临界资源,多个进程必须互斥的对它进行访问。多个进程涉及到同一个临界资源的的临界区称为相关临界区。使用临界区时,一般不允许其运行时间过长,只要运行在临界区的线程还没有离开,其他所有进入此临界区的线程都会被挂起而进入等待状态,并在一定程度上影响程序的运行性能。

四、生命周期

(一)线程的五种状态


1.新建状态(New)

当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();

2.就绪状态(Runnable)

当调用线程对象的start()方法t.start(),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;

3.运行状态(Running)

当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就 绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;

4.阻塞状态(Blocked)

处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:

  • 1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
  • 2.同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
  • 3.其他阻塞 – 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

5.死亡状态(Dead)

线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
在这里插入图片描述

(二)Java多线程的状态转换


  • 就绪状态转换为运行状态:当此线程得到处理器资源;

  • 运行状态转换为就绪状态:当此线程主动调用yield()方法或在运行过程中失去处理器资源。

  • 运行状态转换为死亡状态:当此线程线程执行体执行完毕或发生了异常。

需要特别注意的是:当调用线程的yield()方法时,线程从运行状态转换为就绪状态,但接下来CPU调度就绪状态中的哪个线程具有一定的随机性,因此,可能会出现A线程调用了yield()方法后,接下来CPU仍然调度了A线程的情况。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值