首页 > 其他分享 >多线程

多线程

时间:2024-01-14 18:00:18浏览次数:28  
标签:同步 Thread 死锁 监视器 线程 多线程 方法

01-程序、进程与线程

1. 程序、进程和线程的区分:
程序(program):为完成特定任务,用某种语言编写的`一组指令的集合`。即指一段静态的代码。

进程(process):程序的一次执行过程,或是正在内存中运行的应用程序。程序是静态的,进程是动态的。
              进程作为操作系统调度和分配资源的最小单位。

线程(thread):进程可进一步细化为线程,是程序内部的一条执行路径。
             线程作为CPU调度和执行的最小单位


2. 线程调度策略
分时调度:所有线程`轮流使用` CPU 的使用权,并且平均分配每个线程占用 CPU 的时间。

抢占式调度:让`优先级高`的线程以`较大的概率`优先使用 CPU。如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。


3. 了解
> 单核CPU与多核CPU
> 并行与并发

02-创建多线程的基本方式

1. 线程的创建方式一:继承Thread类
1.1 步骤:
① 创建一个继承于Thread类的子类
② 重写Thread类的run() --->将此线程要执行的操作,声明在此方法体中
③ 创建当前Thread的子类的对象
④ 通过对象调用start(): 1.启动线程 2.调用当前线程的run()


1.2 例题:创建一个分线程1,用于遍历100以内的偶数
【拓展】 再创建一个分线程2,用于遍历100以内的偶数


2. 线程的创建方式二:实现Runnable接口
2.1 步骤:
① 创建一个实现Runnable接口的类
② 实现接口中的run() -->将此线程要执行的操作,声明在此方法体中
③ 创建当前实现类的对象
④ 将此对象作为参数传递到Thread类的构造器中,创建Thread类的实例
⑤ Thread类的实例调用start():1.启动线程 2.调用当前线程的run()


2.2 例题:创建分线程遍历100以内的偶数


3. 对比两种方式?
   共同点:① 启动线程,使用的都是Thread类中定义的start()
         ② 创建的线程对象,都是Thread类或其子类的实例。

   不同点:一个是类的继承,一个是接口的实现。
        建议:建议使用实现Runnable接口的方式。
        Runnable方式的好处:① 实现的方式,避免的类的单继承的局限性 ② 更适合处理有共享数据的问题。
                          ③ 实现了代码和数据的分离。

   联系:public class Thread implements Runnable (代理模式)

03-线程的常用方法与生命周期

一、线程的常用结构
1. 线程中的构造器
- public Thread() :分配一个新的线程对象。
- public Thread(String name) :分配一个指定名字的新的线程对象。
- public Thread(Runnable target) :指定创建线程的目标对象,它实现了Runnable接口中的run方法
- public Thread(Runnable target,String name) :分配一个带有指定目标新的线程对象并指定名字。

2.线程中的常用方法:
> start():①启动线程 ②调用线程的run()
> run():将线程要执行的操作,声明在run()中。
> currentThread():获取当前执行代码对应的线程
> getName(): 获取线程名
> setName(): 设置线程名
> sleep(long millis):静态方法,调用时,可以使得当前线程睡眠指定的毫秒数
> yield():静态方法,一旦执行此方法,就释放CPU的执行权
> join(): 在线程a中通过线程b调用join(),意味着线程a进入阻塞状态,直到线程b执行结束,线程a才结束阻塞状态,继续执行。
> isAlive():判断当前线程是否存活

过时方法:
> stop():强行结束一个线程的执行,直接进入死亡状态。不建议使用
> void suspend() / void resume() :可能造成死锁,所以也不建议使用

3. 线程的优先级:
getPriority():获取线程的优先级
setPriority():设置线程的优先级。范围[1,10]


Thread类内部声明的三个常量:
- MAX_PRIORITY(10):最高优先级
- MIN _PRIORITY (1):最低优先级
- NORM_PRIORITY (5):普通优先级,默认情况下main线程具有普通优先级。

二、线程的生命周期

04-线程的同步机制

线程的安全问题与线程的同步机制

1. 多线程卖票,出现的问题:出现了重票和错票

2. 什么原因导致的?线程1操作ticket的过程中,尚未结束的情况下,其他线程也参与进来,对ticket进行操作。

3. 如何解决?必须保证一个线程a在操作ticket的过程中,其它线程必须等待,直到线程a操作ticket结束以后,其它线程才可以进来
           继续操作ticket。

4. Java是如何解决线程的安全问题的?使用线程的同步机制。

方式1:同步代码块

synchronized(同步监视器){
    //需要被同步的代码
}

说明:
> 需要被同步的代码,即为操作共享数据的代码。
> 共享数据:即多个线程都需要操作的数据。比如:ticket
> 需要被同步的代码,在被synchronized包裹以后,就使得一个线程在操作这些代码的过程中,其它线程必须等待。
> 同步监视器,俗称锁。哪个线程获取了锁,哪个线程就能执行需要被同步的代码。
> 同步监视器,可以使用任何一个类的对象充当。但是,多个线程必须共用同一个同步监视器。

注意:在实现Runnable接口的方式中,同步监视器可以考虑使用:this。
     在继承Thread类的方式中,同步监视器要慎用this,可以考虑使用:当前类.class。

方式2:同步方法

说明:
> 如果操作共享数据的代码完整的声明在了一个方法中,那么我们就可以将此方法声明为同步方法即可。
> 非静态的同步方法,默认同步监视器是this
  静态的同步方法,默认同步监视器是当前类本身。

5. synchronized好处:解决了线程的安全问题。

   弊端:在操作共享数据时,多线程其实是串行执行的,意味着性能低。

05-单例之懒汉式的线程安全问题

解决单例模式中的懒汉式的线程安全问题

> 饿汉式:不存在线程安全问题。
> 懒汉式:存在线程安全问题,(需要使用同步机制来处理)

06-死锁问题

线程的同步机制带来的问题:死锁

1. 如何看待死锁?
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。
我们编写程序时,要避免出现死锁。

2. 诱发死锁的原因?
- 互斥条件
- 占用且等待
- 不可抢夺(或不可抢占)
- 循环等待

以上4个条件,同时出现就会触发死锁。


3. 如何避免死锁?
针对条件1:互斥条件基本上无法被破坏。因为线程需要通过互斥解决安全问题。
针对条件2:可以考虑一次性申请所有所需的资源,这样就不存在等待的问题。
针对条件3:占用部分资源的线程在进一步申请其他资源时,如果申请不到,就主动释放掉已经占用的资源。
针对条件4:可以将资源改为线性顺序。申请资源时,先申请序号较小的,这样避免循环等待问题。

07-Lock的使用

除了使用synchronized同步机制处理线程安全问题之外,还可以使用jdk5.0提供的Lock锁的方式

1. 步骤:
步骤1. 创建Lock的实例,需要确保多个线程共用同一个Lock实例!需要考虑将此对象声明为static final
步骤2. 执行lock()方法,锁定对共享资源的调用
步骤3. unlock()的调用,释放对共享数据的锁定

2. 面试题:
synchronized同步的方式 与Lock的对比 ?

synchronized不管是同步代码块还是同步方法,都需要在结束一对{}之后,释放对同步监视器的调用。
Lock是通过两个方法控制需要被同步的代码,更灵活一些。
Lock作为接口,提供了多种实现类,适合更多更复杂的场景,效率更高。

08-线程的通信

1. 线程间通信的理解
当我们`需要多个线程`来共同完成一件任务,并且我们希望他们有规律的执行,那么多线程之间需要一些通信机制,
可以协调它们的工作,以此实现多线程共同操作一份数据。

2. 涉及到三个方法的使用:
wait():线程一旦执行此方法,就进入等待状态。同时,会释放对同步监视器的调用
notify():一旦执行此方法,就会唤醒被wait()的线程中优先级最高的那一个线程。(如果被wait()的多个线程的优先级相同,则
         随机唤醒一个)。被唤醒的线程从当初被wait的位置继续执行。
notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。


3. 注意点:
> 此三个方法的使用,必须是在同步代码块或同步方法中。
  (超纲:Lock需要配合Condition实现线程间的通信)
> 此三个方法的调用者,必须是同步监视器。否则,会报IllegalMonitorStateException异常
> 此三个方法声明在Object类中。

4. 案例:
案例1:使用两个线程打印 1-100。线程1, 线程2 交替打印

案例2:生产者&消费者
生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,店员一次只能持有
固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品
了再通知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来
取走产品。


5. wait() 和 sleep()的区别?
相同点:一旦执行,当前线程都会进入阻塞状态

不同点:
> 声明的位置:wait():声明在Object类中
            sleep():声明在Thread类中,静态的
> 使用的场景不同:wait():只能使用在同步代码块或同步方法中
               sleep():可以在任何需要使用的场景
> 使用在同步代码块或同步方法中:wait():一旦执行,会释放同步监视器
                          sleep():一旦执行,不会释放同步监视器
> 结束阻塞的方式:wait(): 到达指定时间自动结束阻塞 或 通过被notify唤醒,结束阻塞
               sleep(): 到达指定时间自动结束阻塞

09-新增两种创建线程的方式

1. 创建多线程的方式三:实现Callable (jdk5.0新增的)

与之前的方式的对比:与Runnable方式的对比的好处
> call()可以有返回值,更灵活
> call()可以使用throws的方式处理异常,更灵活
> Callable使用了泛型参数,可以指明具体的call()的返回值类型,更灵活

有缺点吗?如果在主线程中需要获取分线程call()的返回值,则此时的主线程是阻塞状态的。


2. 创建多线程的方式四:使用线程池
此方式的好处:
> 提高了程序执行的效率。(因为线程已经提前创建好了)
> 提高了资源的复用率。(因为执行完的线程并未销毁,而是可以继续执行其他的任务)
> 可以设置相关的参数,对线程池中的线程的使用进行管理

 

标签:同步,Thread,死锁,监视器,线程,多线程,方法
From: https://www.cnblogs.com/callbin/p/17963993

相关文章

  • Java多线程编程实战指南(设计模式篇)PDF
    随着CPU多核时代的到来,多线程编程在充分利用计算资源、提高软件服务质量方面扮演了越来越重要的角色。而解决多线程编程中频繁出现的普遍问题可以借鉴设计模式所提供的现成解决方案。然而,多线程编程相关的设计模式书籍多采用C++作为描述语言,且书中所举的例子多与应用开发人员的......
  • Java多线程编程实战指南(核心篇)PDF
    随着现代处理器的生产工艺从提升处理器主频频率转向多核化,即在一块芯片上集成多个处理器内核(Core),多核处理器(MulticoreProcessor)离我们越来越近了——如今就连智能手机这样的消费类设备都已配备了4核乃至8核的处理器,更何况商用系统!在此背景下,以往靠单个处理器自身处理能力的提升......
  • Java多线程编程实战指南(核心篇)PDF
    随着现代处理器的生产工艺从提升处理器主频频率转向多核化,即在一块芯片上集成多个处理器内核(Core),多核处理器(MulticoreProcessor)离我们越来越近了——如今就连智能手机这样的消费类设备都已配备了4核乃至8核的处理器,更何况商用系统!在此背景下,以往靠单个处理器自身处理能力的提升所......
  • 多线程(Java.Thread)学习
    多线程(Java.Thread)学习线程简介:1、线程是独立执行的路径2、程序运行时有很多后台进程比如主线程、young.gc、full.gc()3、main是主线程,系统入口,用于执行整个程序4、一个进程中、如果开辟多个线程,线程的运行由调度器安排调度、调度器的先后顺序不能人为干预5、对同一份资......
  • C++多线程并发(一)--- 线程创建与管理
    目录进程和线程的区别何为并发?C++11线程基本操作C++11新标准多线程支持库std::thread类成员函数std::thread的关键总结C++中多线程创建C++的多线程可以充分利用计算机资源,提高代码运行效率。在这里总结了一些多线程应用过程中的基本概念和用法。进程和线程的区别进程是一......
  • 详解Java多线程之循环栅栏技术CyclicBarrier
    第1章:引言大家好,我是小黑,工作中,咱们经常会遇到需要多个线程协同工作的情况。CyclicBarrier,直译过来就是“循环屏障”。它是Java中用于管理一组线程,并让它们在某个点上同步的工具。简单来说,咱们可以把一群线程想象成一队马拉雪橇的驯鹿,CyclicBarrier就像是一个指定的集合点,所有驯......
  • 多线程(Java.Thread)学习
    多线程(Java.Thread)学习线程简介:1、线程是独立执行的路径2、程序运行时有很多后台进程比如主线程、young.gc、full.gc()3、main是主线程,系统入口,用于执行整个程序4、一个进程中、如果开辟多个线程,线程的运行由调度器安排调度、调度器的先后顺序不能人为干预5、对同一份资......
  • Python多线程的限制与使用场景探讨
     Python是一种流行的编程语言,具有内置的多线程支持。然而,由于Python的全局解释器锁(GIL)机制等因素的存在,有人认为Python的多线程在某些情况下可能没有明显的性能优势。本文将探讨Python多线程的限制以及适用的使用场景。 1.GIL对多线程的影响 全局解释器锁(GIL)是Python解释器的......
  • (五十六)C#编程基础复习——C#多线程
    多线程就是多个线程同时工作的过程,我们可以将线程看作是程序的执行路径,每个线程都定义了一个独特的控制流,用来完成特定的任务。如果你的应用程序涉及到复杂且耗时的操作,那么使用多线程来执行是非常有益的。使用多线程可以节省CPU资源,同时提高应用程序的执行效率,例如现代操作系统对......
  • Node.js 多线程编程:优化性能的关键步骤
    在 Node.js多线程允许Node.js应用并行处理任务,以此来提高性能和响应速度。Node.js内置了如 worker_threads 这样的模块来支持多线程。通过分配任务给不同的线程,能够更好地利用多核处理器,缓解单线程应用可能出现的瓶颈。Node.js中多线程的适用场景在 Node.js 中,以下几种情......