第五周——线程

7不积跬步无以至千里7 7不积跬步无以至千里7     2022-09-20     428

关键词:

 

一、线程的概述

1.进程:

正在运行的程序称之为一个进程,进程负责了这个程序的内存空间分配,代表了内存中的执行区域。

问题:windows号称是多任务的操作系统,那么windows是同时运行多个应用程序吗?

从宏观的角度:windows确实是在同时运行多个应用程序。

从微观的角度:cpu是做了一个快速的切换执行的动作,由于速度太快,所以我们感觉不到他在进行切换。

单核的cpu在一个时间片中,只能执行一个应用程序;其实各个应用程序是在做cpu的资源争夺战而已,cpu做了快速的切换。

2线程:

就是在一个进程中负责一个执行路径。进程中有很多代码,线程负责执行进程中的代码。

3多线程:

就是在一个进程中多个执行路径同时执行。例如金山卫士中的垃圾清理,和木马查杀,同时运行,就是在一个进程中,有两个线程,同时在执行两个不同的代码。简单的说,就是在一个进程中有多个线程同时在执行不同的任务。进程中的代码是由线程来执行。

    与其说是进程在做cpu的争夺战 ,还不如说是线程在做cpu的资源的争夺战。

 

疑问1线程负责了代码的执行,我们之前没有学过线程,为什么代码可以执行呢?

   因为,任何一个java程序,jvm在运行的时候都会创建一个main线程执行main方法中的所有代码。

疑问2一个java应用程序至少有几个线程?

        一个java应用程序至少有两个线程,一个主线程负责main方法代码的执行,一个是垃圾回收器的线程,负责了回收垃圾。

 

4多线程的好处:

  1. 解决了一个进程里面可以同时运行多个任务(执行路径)。
  2. 提供资源的利用率,而不是提供效率。
  3. 降低了一个进程里面的线程的执行频率。
  4. 对线程进行管理要求额外的 CPU开销。线程的使用会给系统带来上下文切换的额外负担。
  5. 公有变量的同时读或写。当多个线程需要对公有变量进行写操作时,后一个线程往往会修改掉前一个线程存放的数据,发生线程安全问题。
  6. 线程的死锁。即较长时间的等待或资源竞争以及死锁等多线程症状。

5多线程的弊端:

二、线程的创建方式

 1 方式一

   1 自定义一个类继承Thread

   2.重写Thread类的run方法。

   3.创建Thread的子类对象,并且调用start方法开启线程。

注意:一个线程一旦开启,那么线程就会执行run方法中的代码,run方法千万不能直接调用,直接调用run方法就相当于调用了一个普通的方法而已,并没有开启线程。

   4. 疑问:重写run方法的目的是什么?

因为,每个线程都有自己的任务代码,jvm创建的主线程的任务代码就是main方法中的所有代码,自定义线程的任务代码就写在run方法中,自定义线程负责了run方法中的代码。

重写run方法的目的就是,为了把自定义线程的任务定义在run方法中。

 

三、线程的生命周期

线程的线程周期如下图:

 

 

 

 

四、线程的常用方法Thread

1 构造方法

Thread(String name)     初始化线程的名字

 

2 常用方法

2.1 getName()             返回线程的名字

 

2.2 setName(String name)    设置线程对象名

 

2.3 sleep()                 线程睡眠指定的毫秒数。

注意:Sleep()方法是静态的方法,哪个线程执行了sleep方法代码,那么就是哪个线程睡眠。

 

2.4currentThread()      返回CPU正在执行的线程的对象

注意:currentThread()方法是静态的方法,哪个线程执行了currentThread()方法代码,那么就返回哪个方法的对象。

 

2.5getPriority()             返回当前线程对象的优先级   默认线程的优先级是5

 

2.6setPriority(int newPriority) 设置线程的优先级    虽然设置了线程的优先级,但是具体的实现取决于底层的操作系统的实现(最大的优先级是10 ,最小的1 , 默认是5)。

五、线程安全问题

1.出现线程安全问题的根本原因:

   1.存在两个或者两个以上的线程对象,而且线程之间共享着一个资源。

   2.有多个语句操作了共享资源。

 

2.线程安全问题的解决方案——同步机制

1.Sun提供了线程同步机制让我们解决这类问题。

2.Java线程同步机制方案

           方式一:同步代码块

              同步代码块的格式:

         

     Synchronized(锁对象){

        

    需要被同步的代码......

}

           

 

 

 

 方式二:同步函数

同步函数就是使用synchronized修饰一个函数,也就是说,把一个方法使用Synchronized修饰。

       同步函数要注意的事项:

   1.如果是一个非静态的同步函数的锁  对象是this对象,如果静态的同步函数的锁   对象是当前函数所属的类的字节码文件(class对象)。

   2.同步函数的锁对象是固定的,不能有你来确定。

 

 

//静态的函数---->函数所属 的类的字节码文件对象--->BankThread.class  唯一的。

public static synchronized  void getMoney (){

 

}

 

 

JDK中,有一个class 类来描述字节码文件。

字节码文件是如何产生的?

 

 

 

3.同步代码块要注意的事项

     1.任意的一个对象都可以作为锁对象,因为,凡是对象内部都维护了一个状态的,java的同步机制就是使用了对象中的状态作为了锁的标识。

         State = 1   

         Stqate=0   

      这些状态是隐士维护的。

线程先进入到同步代码块时,首先先进入到“锁”,看看“锁”是否开启,若是没有人执行,“锁”默认是开启的,线程要执行“代码”,进入“锁”后,将“锁”的状态改变成关。只有当线程出了同步代码块后,就将“锁”打开。就是抢“锁”原理。

     2.在同步代码块中调用了sleep方法并不是释放“锁”对象。

     3.只有真正存在线程安全问题的时候,才会使用同步代码块,否则会点滴效率

     4.多线程操作的锁对象,必须是唯一共享的,否则无效。

 

推荐使用同步代码块:

原因:

     1. 同步代码块的锁对象可以由我们随意指定,方便控制。同步函数的代码块是固定的,不能由我们来指定。

     2. 同步代码块可以很方便控制需要被同步代码的范围,同步函数必须是整个函数的所有代码都被同步了。

 

六、同步函数、死锁问题

1死锁现象:

 

 

 

 

 

 

 

上述代码出现死锁的原因是因为:“张三”执行代码,执行到同步函数synchronized(“遥控器”),将“遥控器”对象“锁住”,与此同时,“狗娃”执行代码,执行到同步函数synchronized(“电池”),将“电池”对象“锁住。所以。。。。就会锁住啦,也有可能不锁。

2.死锁现象出现 的根本原因:

1. 存在两个或者两个以上的线程。

2. 存在两个或者两个以上的共享资源。

3.死锁现象的解决方案

 没有解决的方案,只能尽量的避免发生而已。

七、自定义线程的创建方式二

1.自定义线程的创建方式

 

方式一

1. 自定义一个类继承Thread类。

2. 重写Thread类的run方法,把自定义线程的任务代码写在run方法上。

3. 创建Thread的子类对象,并且调用start方法启动一个线程。

 

注意:千万不要直接调用run方法,调用start方法的时候线程就会开启,线程一旦开启就会执行run方法中代码,如果直接调用

run方法,那么就 相当于调用了一个普通的方法而已。

 

方式二:

1. 自定义一个类实现Runnable接口。

2. 实现Runnable接口 的run方法,把自定义线程的任务定义在run方法上。

3. 创建Runnable实现类对象。

4. 创建Thread类 的对象,并且把Runnable实现类的对象作为实参传递。

          

5. 调用Thread对象 的start方法开启一个线程。

 

 

 

问题1: 请问Runnable实现类的对象是线程对象吗?

Runnable实现类的对象并 不是一个线程对象,只不过是实现了Runnable接口 的对象而已。

只有是Thread或者是Thread的子类才是线程 对象。

 

问题2: 为什么要把Runnable实现类的对象作为实参传递给Thread对象呢?作用是什么?

作用就是把Runnable实现类的对象的run方法作为了线程的任务代码去执行了。

 

 Thread类 的run方法

 *  @Override

    public void run() {

        if (target != null) {

            target.run();  //就相当于Runnable实现类的对象的run方法作为了Thread对象的任务代码了。

        }

    }

就相当于Runnable实现类的对象的run方法作为了Thread对象的任务代码了

 

 

推荐使用: 第二种。 实现Runable接口的。

原因: 因为java单继承 ,多实现的。

八、线程通讯

线程通讯: 一个线程完成了自己的任务时,要通知另外一个线程去完成另外一个任务.

售票问题:

 

如上述代码,为什么num没有使用static代码,售票却没有出现问题呢,其原因是因为,在创建SaleTicket对象的时候,只创建了一次,所以,在内存中只维护一个num

九、生成者与消费者问题

1.wait():  等待

 如果线程执行了wait方法,那么该线程会进入等待的状态,等待状态下的线程必须要被其他线程调用notify方法才能唤醒。

Wait() :如果一个线程执行了wait方法,那么该线程就会进去一个以锁对象为标识符的线程池中等待。

Notify():如果一个线程执行了notify方法,那么就唤醒以锁对象为标识符的线程中等待线程其中一个。

2.notify(): 唤醒  

2.1 notify():  唤醒线程池等待线程其中的一个。

2.2 notifyAll() : 唤醒线程池所有等待 线程。

2.3waitnotify方法要注意的事项:

1. wait方法与notify方法是属于Object对象 的。

2. wait方法与notify方法必须要在同步代码块或者是同步函数中才能 使用。

3. wait方法与notify方法必需要由锁对象调用。

 

3生产者与消费者代码详解

 

 

 

 

 

4代码执行过程概述

首先,假设生产者拿到了cpu的执行权,对标志位flag进行判断,flagfalse状态,生产者进入其中,生产者生产苹果,6.5元,接着生产者继续拿到了cpu的执行权,代码向下执行,将标志位flag更改成truei++,执行p.notify()方法,由于此时p线程池中还没有在等待的线程,所以,代码继续执行。生产者回到最初的起点,对标志位进行判断,判断结果不符合,生产者执行wait()进行等待,在等待的过程中,jvm就会以“锁”对象为标识建立线程池,此时生产者线程进入线程池中进行等待。

由于生产者线程执行了wait方法,并且进入了线程池进行等待,此时,“锁”对象被生产者释放;此时消费者拿到了cpu的执行权,消费者首先对标志位flag进行判断,判定后进行消费,消费后将标志位更改为false,而后执行p.notify(),唤醒“锁”对象为标识符的线程中等待线程中的一个线程。接着消费者继续执行,首先进行标志位判定,发现判定失败,执行wait()方法,此时消费者进入线程池中进行等待,失去了cpu的执行权。此时生产者获得cpu的执行权,周而复始。如上所述。很可爱。

5.小问题

Q1waitnotify方法为什么必须要在同步代码块中使用?

  因为如果不存在着同步代码块或者同步函数,就没有“锁”的存在,而waitnotify方法,一定要由“锁”来进行调用,所以wait notify方法一定要在同步代码块或者同步函数中使用。

Q2waitnotify方法为什么必须要用锁对象调用?

因为wait()notify()方法要以“锁”对象为标识符建立线程池的,所有waitnotify方法必须要用“锁”对象调用,且调用的时候,所使用的“锁”必须是同一把锁。因为,不同的“锁”,建立的线程池并不相同。

Q3如果waitnotify方法一旦不用“锁”来进行调用那么一定会报错。

 

 

 

 

 

 

 

 

 

 

 

 

附:生产者与消费者代码

class Product{

String name;

double price;

boolean flag =false;

}

class Producer extends Thread{

Product p;

public Producer(Product p){

this.p=p;

}

public void run(){

int i=0;

while(true){

synchronized(p){

if(p.flag==false){

if(i%2==0){

p.name = "苹果";

p.price=6.5;

}else{

p.name ="香蕉";

p.price =2.0;

}

System.out.println("生产者生产出了:"+p.name+"价格是:"+ p.price);

p.flag =true;

i++;

p.notifyAll();        

}else{

try {

p.wait();

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

}

}

 

}

     class Custormer extends Thread{

      Product p;

      public Custormer (Product p){

      this.p=p;

      }

      public void run(){

     while (true){

      synchronized (p){

      if(p.flag==true){

      System.out.println("消费者消费了"+p.name+"价格:"+p.price);

      p.flag =false;

      p.notifyAll();

      }else{

      try {

     p.wait(); //消费者也等待了...

     } catch (InterruptedException e) {

     e.printStackTrace();

     }

      }

      }

      }

      }

     }

public class Demo6 {

    public static void main(String[] args){

Product p = new Product();

    Producer pro =new Producer(p);

    Custormer cus = new Custormer(p);

    pro.start();

cus.start();

         

}

}

十、线程的停止

1.停止一个线程一般都会通过一个变量去控制。

2.如果需要停止一个处于等待状态下的线程,那么我们需要通过变量配合notify方法或者interrupt()来使用。

十一、守护线程

守护线程(后台线程):在一个进程中如果只剩下了守护线程,那么守护线程也会死亡。

需求:模拟QQ下载更新包。

一个线程默认都不是守护线程。

十二、join语句

John()加入,一个线程如果执行了john语句,那么就有新的线程加入,执行该语句的线程必须要让步给新加入的线程完成任务,然后才能继续执行。

 

第五周

查看详情

第五周笔记150206124

查看详情

学习进度条——第五周

 第五周所花时间(包括上课)上课:200分钟看书:145分钟编程:8.5小时代码量(行)143行博客量(篇)0篇了解到的知识点1、团队的模式以及开发流程2、FDD、Scrum、XP、TDD 查看详情

第五周学习记录

1 查看详情

20165331第五周学习总结

20165331《Java程序设计》第5周学习总结代码托管 查看详情

第五周笔记150206201

  查看详情

第五周随堂笔记

查看详情

第五周--例行报告

Ⅰ本周PSPⅡ本周进度条Ⅲ累积折线统计图Ⅳ本周PSP饼状图  查看详情

java第五周学习

  查看详情

第五周psp

本周psp psp饼图:随笔字数折线图: 代码行折线图:   查看详情

第五周作业1

PSP扇形图进度条折线图代码行数 博文字数  查看详情

第五周---例行报告

1.本周PSP2.本周进度条3.代码累积折线图 4.博文字数累积折线图5.PSP饼状图  查看详情

第五周总结报告

学习时间7h代码量 250行博客量1篇学习内容地铁线路后台开发一点点 查看详情

第五周数据结构

代码  结果   查看详情

第五周数据结构

代码  结果   查看详情

20165329第五周学习总结

20165329第五周学习总结教材学习内容总结第七章异常类与内部类异常类异常对象可以调用如下方法得到或输出有关异常信息publicStringgetMessage();publicvoidprintStackTrace();publicStringtoString();try-catch语句如果在try-catch语句中执行了return语句... 查看详情

第五周作业

第五周作业1、显示当前系统上root、fedora或user1用户的默认shell;[[email protected] ~]#  grep -E ‘^(root|fedora|user1)>‘ /etc/passwd | cut -d: -f1,7root:/bin/bashfe 查看详情

高级编程技术作业第五周

9-19-29-39-59-710-310-410-510-610-7  查看详情