Java面向对象——多线程

进程(Program)

  1. 是对数据描述与操作的代码的集合。
  2. 是程序的一次动态执行过程,它对应了从代码加载。执行至执行完毕的一个完整过程。这个过程也是进程本身产生,发展至消亡的过程。

进程的特点:

  • 进程是系统运行程序的基本单位。
  • 每一个进程都有自己独立的一块内存空间、一组系统资源。
  • 每一个进程的内部数据和状态都是完全独立的。

在操作系统中可以有多个进程,这些进程包括系统进程(由操作系统内部建立的进程)和用户进程(有用户程序建立的进程)



线程

是进程中执行运算的最小单位,可完成一个独立的顺序控制流程。每个进程中,必须至少建立一个线程(这个线程为主线程)来作为这个程序运行的入口点。
多个线程共享相同的地址空间并且共同分享同一个进程,这些进程可以在操作系统的管理下并发执行。
在仅有一个CPU的计算机上不可能同时执行多个任务。而操作系统为了能提高程序的运行效率,将CPU的执行时间分成多个时间片,分配给不同的线程。当一个时间片执行完毕后,该线程就可能让出CPU使用权限交付给下一个时间片的其他线程执行。



多线程的好处

  1. 充分利用CPU的资源:执行单线程程序时,若程序发生阻塞,CPU可能会处于空闲状态,这将造成计算机资源浪费,而使用多线程可以在某个线程处理休眠或阻塞状态时运行其他线程,这样,大大提高了资源利用率。
  2. 简化编程模型:一个既长又复杂的进程可以考虑分为多个线程,成为几个独立的运行部分,如使用时、分、 秒来描述当前时间,如果写成单线程程序可能需要多重循环判断,而如果使用多线程,时、分、秒各使用一个线程控制,每个线程仅需实现简单的流程,简化了程序逻辑,这样更有助于开发人员对程序的理解和维护。
  3. 带来良好的用户体验:由于多个线程可以交替执行,减少或避免了因程序阻塞或意外情况造成的响应过慢现象,降低了用户等待的几率。



Thread类的常用方法

  • start() : 启动当前线程, 调用当前线程的run()方法
  • run() : 通常需要重写Thread类中的此方法, 将创建的线程要执行的操作声明在此方法中 【执行任务操作的方法】
  • currentThread() : 静态方法, 返回当前代码执行的线程
  • getName() : 获取当前线程的名字
  • setName() : 设置当前线程的名字
  • getPriority():返回线程的优先级
  • setPriority(int newPriority):更改线程的优先级
  • yield() : 释放当前CPU的执行权
  • join() : 在线程a中调用线程b的join(), 此时线程a进入阻塞状态, 直到线程b完全执行完以后, 线程a才结束阻塞状态【等待该线程终止】
  • stop() : 已过时. 当执行此方法时,强制结束当前线程.
  • sleep(long millis):在指定的毫秒数内让当前正在执行的线程休眠(暂停纸条)【方法】
  • sleep(long militime) : 让线程睡眠指定的毫秒数,在指定时间内,线程是阻塞状态
  • isAlive() :判断当前线程是否存活【Boolean类型】
  • Thread():分配新的Thread对象
  • Thread(Runnable target):分配新的Thread对象,target为run()方法被调用的对象。
  • Thread(Runnable target ,String name):分配新的Thread对象,target为run()方法被调用的对象,name为新线程的名称
  • static Thread currentThread():返回当前正在执行的线程对象的引用
  • interrupt():中断线程【方法】
  • yield():暂停当前正在执行的线程对象,并执行其他线程【方法】



主线程

在Java程序启动时,一个线程立刻运行,该线程通常称为程序的主线程。【每个进程都至少有一个主线程,程序开始时就执行的。】

主线程的重要性:

  • 它是产生其他子线程的线程。
  • 通常它必须最后完成执行,因为它执行各种关闭动作。
    尽管主线程在程序启动时自动创建,但它可以由一个Thread对象控制。为此,需要调用方法currentThread()获得它的一个引用,该方法时Thread类的公有的静态成员,通常形式为【static Thread currentThread()

使用一个线程的过程:

1.定义一个线程,同时指明这个线程所要执行的代码,及期望完成的功能。
2. 创建线程对象。
3. 启动线程。
4. 终止线程。



继承Thread类创建线程

步骤:

  1. 定义MyThread类继承Thread类。
  2. 重写run()方法,在run()方法中实现数据输出。
  3. 创建线程对象,调用start()方法启动线程。

在调用start()方法和直接调用run()方法的区别:
调用start()方法表示该线程处于启动状态。等待操作系统分配资源执行run()方法中的代码。在多个线程同时处于启动状态时,线程时交替执行的。
直接调用run()方法和调用实例方法没有本质上的区别,程序是按顺序执行的,属于单线程执行模式。

线程实例调用start()方法和调用run()方法有着天壤之别,前者是启动线程,后者是调用实例方法。



实现Runnable接口创建线程

该接口定义在java.lang包下,其中声明了抽象的**run()**方法。一个类可以通过实现该接口并实现run()方法完成线程的所有活动。已实现的run()方法称为该线程对象的线程体。任何实现该接口的对象都可以作为一个线程的目标对象,



线程的状态

  1. 创建状态
    创建一个线程对象后,此时已获取相应的资源,但还没有处于可运行的状态。这时可通过Thread类的方法来设置线程对象的属性【设置线程名setName(),设置线程优先级:setPriority()等】
  2. 就绪状态
    线程创建后,可通过调用start()方法启动线程。进入就绪状态。【在获取到CPU的资源时就具备了运行条件,除此外,仍不能真正执行】
  3. 运行状态
    就绪状态的线程获取到CPU的资源时,即可转入运行状态。执行run()方法。
  4. 阻塞状态
    正在运行的线程因某种原因不能继续运行时,为进入阻塞状态。
  5. 死亡状态
    线程run()方法执行完毕,线程进入死亡状态。处于该状态线程不具有继续执行的能力。

可能使线程暂停的条件:

  1. 由于线程优先级比较低,不能获取到CPU的资源。
  2. 使用sleep()方法使线程休眠
  3. 调用wait()方法,使线程等待
  4. 调用yieId()方法,线程显式出让CPU控制权。
  5. I/O事件阻塞【例如:Scanner控制台输入】。



线程的优先级

线程的三个静态常量:
NORM_PRIORITY = 5;
MAX_PRIORITY = 10;
MIN_PRIORITY = 1;



线程的休眠

语法:
public static void sleep(long millis);【单位为毫秒】
使用该方法会让线程由运行中的状态进入不可运行状态,事件过后线程将再次进入可运行状态,调用该方法需要处理InterruptedException异常或继续抛出异常。



线程的强制运行

join()方法使当前线程暂停执行,等待调用该方法的线程结束后在继续执行本线程。
三个重载方法:
public final void join();
public final void join(long mills);
public final void join(long mills,int nanos);
【与sleep()方法相同,使用时要处理异常】



线程的礼让

yieId()方法可以暂停当前执行的线程,允许其他拥有相同优先级的线程获取运行机会,该线程仍处于就绪状态,不转为阻塞状态。若存在相同或更高的优先级系统将执行相同或更高的优先级的线程,若无则该线程继续执行



线程同步的实现

当两个或者多个线程需要访问同一资源时,需要以某种顺序来确保该资源某一时刻只能被一个线程使用,这就是线程同步。
采用线程同步来控制线程的执行有两种方式,即同步代码方法和同步代码块。【使用关键字synchronized关键字】
1.同步方法
使用synchronized修饰的方法控制对类成员变量的访问。
语法:
访问修饰符 synchronized 返回类型 方法名(参数列表){}

synchronized 访问修饰符 返回类型 方法名(参数列表){}
synchronized是同步关键字。
访问修饰符是指public、private等。
缺陷:
运行时间比较长的方法声明成synchronized将会影响效率。
2.同步代码块
使用synchronized关键字修饰代码块,称为同步代码块。
语法:
synchronized(syncObject){}
synchronized块中的代码必须获得对象syncObject的锁才能执行,具体机制与同步方法一致。【由于可以针对任意代码块,并可以任意指定上锁对象,因此灵活性更高。】
在使用同步方法或同步代码块实现线程同步时,能够保证在同一时刻最多只有一个线程执行该代码。需注意:

  • 当多个并发线程访问同一个对象Object的synchronized(this)同步代码块时,同一时刻只能有一个线程得到执行,其他线程必须等待当前执行完毕之后才能执行该代码块。
  • 当一个线程访问object的一个synchronized(this)同步代码块时,其他线程的代码块都将被阻塞。
  • 当一个线程访问object的synchronized(this)同步代码块时,其他线程仍可以访问该object的非synchronized(this)同步代码块。



线程安全的类型

若所在的进程中有多个线程,而当这些线程同时运行时,每次运行结果和单线程时运行的结果相同,而其他变量的值也和预期的一样,这就是线程安全。
一个类在被多个线程访问时,不管运行时环境执行这些线程有什么样的时序安排,它必须是以固定的,一致的顺序执行。这样的类型称为线程安全的类型

对比Hashtable 和 HashMap
1.继承关系不同
两者都实现了Map接口,Hash table继承Dictionary类,HashMap继承Abstract Map类
2.是否线程安全
Hashtable类型是线程安全的,其中的方法是同步的,而HashMap中方法在缺省情况下是非同步的,在多线程并发的情况下,可以直接使用Hashtable,如果使用HashMap就要自行增加同步处理。
3.是否允许出现null值
在Hash Map中,null可以作为键。也可以作为值,而Hashtable中的键和值都不允许为null,否则会出现NullPointerException异常。
4.效率比较
由于Hashtable是线程安全的,其方法是同步的,而Hash Map是非线程安全的,重速度,轻安全。当只需要单线程时,使用HashMap执行速度要高过Hash table。

在这里插入图片描述

进程(Program)

  1. 是对数据描述与操作的代码的集合。
  2. 是程序的一次动态执行过程,它对应了从代码加载。执行至执行完毕的一个完整过程。这个过程也是进程本身产生,发展至消亡的过程。

进程的特点:

  • 进程是系统运行程序的基本单位。
  • 每一个进程都有自己独立的一块内存空间、一组系统资源。
  • 每一个进程的内部数据和状态都是完全独立的。

在操作系统中可以有多个进程,这些进程包括系统进程(由操作系统内部建立的进程)和用户进程(有用户程序建立的进程)

线程

是进程中执行运算的最小单位,可完成一个独立的顺序控制流程。每个进程中,必须至少建立一个线程(这个线程为主线程)来作为这个程序运行的入口点。
多个线程共享相同的地址空间并且共同分享同一个进程,这些进程可以在操作系统的管理下并发执行。
在仅有一个CPU的计算机上不可能同时执行多个任务。而操作系统为了能提高程序的运行效率,将CPU的执行时间分成多个时间片,分配给不同的线程。当一个时间片执行完毕后,该线程就可能让出CPU使用权限交付给下一个时间片的其他线程执行。

多线程的好处

  1. 充分利用CPU的资源:执行单线程程序时,若程序发生阻塞,CPU可能会处于空闲状态,这将造成计算机资源浪费,而使用多线程可以在某个线程处理休眠或阻塞状态时运行其他线程,这样,大大提高了资源利用率。
  2. 简化编程模型:一个既长又复杂的进程可以考虑分为多个线程,成为几个独立的运行部分,如使用时、分、 秒来描述当前时间,如果写成单线程程序可能需要多重循环判断,而如果使用多线程,时、分、秒各使用一个线程控制,每个线程仅需实现简单的流程,简化了程序逻辑,这样更有助于开发人员对程序的理解和维护。
  3. 带来良好的用户体验:由于多个线程可以交替执行,减少或避免了因程序阻塞或意外情况造成的响应过慢现象,降低了用户等待的几率。

Thread类的常用方法

  • start() : 启动当前线程, 调用当前线程的run()方法
  • run() : 通常需要重写Thread类中的此方法, 将创建的线程要执行的操作声明在此方法中 【执行任务操作的方法】
  • currentThread() : 静态方法, 返回当前代码执行的线程
  • getName() : 获取当前线程的名字
  • setName() : 设置当前线程的名字
  • getPriority():返回线程的优先级
  • setPriority(int newPriority):更改线程的优先级
  • yield() : 释放当前CPU的执行权
  • join() : 在线程a中调用线程b的join(), 此时线程a进入阻塞状态, 知道线程b完全执行完以后, 线程a才结束阻塞状态【等待该线程终止】
  • stop() : 已过时. 当执行此方法时,强制结束当前线程.
  • sleep(long millis):在指定的毫秒数内让当前正在执行的线程休眠(暂停纸条)【方法】
  • sleep(long militime) : 让线程睡眠指定的毫秒数,在指定时间内,线程是阻塞状态
  • isAlive() :判断当前线程是否存活【Boolean类型】
  • Thread():分配新的Thread对象
  • Thread(Runnable target):分配新的Thread对象,target为run()方法被调用的对象。
  • Thread(Runnable target ,String name):分配新的Thread对象,target为run()方法被调用的对象,name为新线程的名称
  • static Thread currentThread():返回当前正在执行的线程对象的引用
  • interrupt():中断线程【方法】
  • yield():暂停当前正在执行的线程对象,并执行其他线程【方法】

主线程

在Java程序启动时,一个线程立刻运行,该线程通常称为程序的主线程。【每个进程都至少有一个主线程,程序开始时就执行的。】

主线程的重要性:

  • 它是产生其他子线程的线程。
  • 通常它必须最后完成执行,因为它执行各种关闭动作。
    尽管主线程在程序启动时自动创建,但它可以由一个Thread对象控制。为此,需要调用方法currentThread()获得它的一个引用,该方法时Thread类的公有的静态成员,通常形式为【static Thread currentThread()

使用一个线程的过程:

1.定义一个线程,同时指明这个线程所要执行的代码,及期望完成的功能。
2. 创建线程对象。
3. 启动线程。
4. 终止线程。

继承Thread类创建线程

步骤:

  1. 定义MyThread类继承Thread类。
  2. 重写run()方法,在run()方法中实现数据输出。
  3. 创建线程对象,调用start()方法启动线程。

在调用start()方法和直接调用run()方法的区别:
调用start()方法表示该线程处于启动状态。等待操作系统分配资源执行run()方法中的代码。在多个线程同时处于启动状态时,线程时交替执行的。
直接调用run()方法和调用实例方法没有本质上的区别,程序是按顺序执行的,属于单线程执行模式。

线程实例调用start()方法和调用run()方法有着天壤之别,前者是启动线程,后者是调用实例方法。

实现Runnable接口创建线程

该接口定义在java.lang包下,其中声明了抽象的**run()**方法。一个类可以通过实现该接口并实现run()方法完成线程的所有活动。已实现的run()方法称为该线程对象的线程体。任何实现该接口的对象都可以作为一个线程的目标对象,

线程的状态

  1. 创建状态
    创建一个线程对象后,此时已获取相应的资源,但还没有处于可运行的状态。这时可通过Thread类的方法来设置线程对象的属性【设置线程名setName(),设置线程优先级:setPriority()等】
  2. 就绪状态
    线程创建后,可通过调用start()方法启动线程。进入就绪状态。【在获取到CPU的资源时就具备了运行条件,除此外,仍不能真正执行】
  3. 运行状态
    就绪状态的线程获取到CPU的资源时,即可转入运行状态。执行run()方法。
  4. 阻塞状态
    正在运行的线程因某种原因不能继续运行时,为进入阻塞状态。
  5. 死亡状态
    线程run()方法执行完毕,线程进入死亡状态。处于该状态线程不具有继续执行的能力。

可能使线程暂停的条件:

  1. 由于线程优先级比较低,不能获取到CPU的资源。
  2. 使用sleep()方法使线程休眠
  3. 调用wait()方法,使线程等待
  4. 调用yieId()方法,线程显式出让CPU控制权。
  5. I/O事件阻塞【例如:Scanner控制台输入】。

线程的优先级

线程的三个静态常量:
NORM_PRIORITY = 5;
MAX_PRIORITY = 10;
MIN_PRIORITY = 1;

线程的休眠

语法:
public static void sleep(long millis);【单位为毫秒】
使用该方法会让线程由运行中的状态进入不可运行状态,事件过后线程将再次进入可运行状态,调用该方法需要处理InterruptedException异常或继续抛出异常。

线程的强制运行

join()方法使当前线程暂停执行,等待调用该方法的线程结束后在继续执行本线程。
三个重载方法:
public final void join();
public final void join(long mills);
public final void join(long mills,int nanos);
【与sleep()方法相同,使用时要处理异常】

线程的礼让

yieId()方法可以暂停当前执行的线程,允许其他拥有相同优先级的线程获取运行机会,该线程仍处于就绪状态,不转为阻塞状态。若存在相同或更高的优先级系统将执行相同或更高的优先级的线程,若无则该线程继续执行

线程同步的实现

当两个或者多个线程需要访问同一资源时,需要以某种顺序来确保该资源某一时刻只能被一个线程使用,这就是线程同步。
采用线程同步来控制线程的执行有两种方式,即同步代码方法和同步代码块。【使用关键字synchronized关键字】
1.同步方法
使用synchronized修饰的方法控制对类成员变量的访问。
语法:
访问修饰符 synchronized 返回类型 方法名(参数列表){}

synchronized 访问修饰符 返回类型 方法名(参数列表){}
synchronized是同步关键字。
访问修饰符是指public、private等。
缺陷:
运行时间比较长的方法声明成synchronized将会影响效率。
2.同步代码块
使用synchronized关键字修饰代码块,称为同步代码块。
语法:
synchronized(syncObject){}
synchronized块中的代码必须获得对象syncObject的锁才能执行,具体机制与同步方法一致。【由于可以针对任意代码块,并可以任意指定上锁对象,因此灵活性更高。】
在使用同步方法或同步代码块实现线程同步时,能够保证在同一时刻最多只有一个线程执行该代码。需注意:

  • 当多个并发线程访问同一个对象Object的synchronized(this)同步代码块时,同一时刻只能有一个线程得到执行,其他线程必须等待当前执行完毕之后才能执行该代码块。
  • 当一个线程访问object的一个synchronized(this)同步代码块时,其他线程的代码块都将被阻塞。
  • 当一个线程访问object的synchronized(this)同步代码块时,其他线程仍可以访问该object的非synchronized(this)同步代码块。

线程安全的类型

若所在的进程中有多个线程,而当这些线程同时运行时,每次运行结果和单线程时运行的结果相同,而其他变量的值也和预期的一样,这就是线程安全。
一个类在被多个线程访问时,不管运行时环境执行这些线程有什么样的时序安排,它必须是以固定的,一致的顺序执行。这样的类型称为线程安全的类型

对比Hashtable 和 HashMap
1.继承关系不同
两者都实现了Map接口,Hash table继承Dictionary类,HashMap继承Abstract Map类
2.是否线程安全
Hashtable类型是线程安全的,其中的方法是同步的,而HashMap中方法在缺省情况下是非同步的,在多线程并发的情况下,可以直接使用Hashtable,如果使用HashMap就要自行增加同步处理。
3.是否允许出现null值
在Hash Map中,null可以作为键。也可以作为值,而Hashtable中的键和值都不允许为null,否则会出现NullPointerException异常。
4.效率比较
由于Hashtable是线程安全的,其方法是同步的,而Hash Map是非线程安全的,重速度,轻安全。当只需要单线程时,使用HashMap执行速度要高过Hash table。

在这里插入图片描述

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值