线程

作者: d24b5d9a8312 | 来源:发表于2019-09-27 01:26 被阅读0次

    线程和进程

    进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位,每个进程都有独立的代码和数据空间(进程上下文),进程切换的开销大。

    线程:轻量的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开销小。一个线程是一个程序内部的顺序控制流。

    多进程:同时运行多个任务(程序)。
    多线程:在同一应用程序中有多个顺序流同时执行。

    线程的概念模型

    ●虚拟的CPU,封装在 java. lang. Thread类中。

    ●CPU所执行的代码,传递给 Thread类。

    ●CPU所处理的数据,传递给 Thread类。

    ●Java的线程是通过 java. lang. Thread类来实现的。、

    ●每个线程都是通过某个特定 Thread对象的方法run()来完成其操作的方法run()称为线程体。

    构造线程的三种方法

    定义一个线程类,它继承类 Thread并重写其中的方法run();
    提供一个实现接口 Runnable的类作为线程的目标对象,在初始化;
    通过Callable和Future创建线程。

    一个 Thread类或者 Thread子类的线程对象时,把目标对象传递给这个线程实例,由该目标对象提供线程体run()。

    main线程已经执行完后,新线程才执行完,main方法调用 thread. start()方法启动新线程后并不等待其run方法返回就继续运行,线程的run方法在一边独自运行,不影响原来的main方法的运行。

    Runnable接口

    只有一个run()方法,Thread类实现了 Runnable接囗,便于多个线程共享资源.

    □Java不支持多继承,如果已经继承了某个基类,便需要

    现 Runnable接口来生成多线程

    口以实现 Runnable的对象为参数建立新的线程

    口sta方法启动线程就会运行run(方法

    多线程的同步控制

    ●有时线程之间彼此不独立、需要同步

    口线程间的互斥

    同时运行的几个线程需要共享一个(些)数据

    共享的数据,在某一时刻只允许一个线程对其进行操作

    “生产者/消费者”问题

    假设有一个线程负责往数据区写数据,另一个线程从同一数据

    区中读数据,两个线程可以并行执行

    如果数据区已满,生产者要等消费者取走一些数据后才能再写

    当数据区空时,消费者要等生产者写入一些数据后再取

    ●线程同步

    口互斥:许多线程在同一个共享数据上操作而互不干扰,同一时刻

    只能有一个线程访问该共享数据。因此有些方法或程序段在同

    时刻只能被一个线程执行,称之为监视区

    口协作:多个线程可以有条件地同时操作共享数据。执行监视区代

    码的线程在条件满足的情况下可以允许其它线程进入监视区

    synchronized-一线程同步关键字,实现互斥

    口用于指定需要同步的代码段或方法,也就是监视区

    口可实现与一个锁的交互。例如

    synchronized(对象)(代码段

    口 synchronized的功能是:首先判断对象的锁是否在,如果在就获得锁

    然后就可以执行紧随其后的代码段;如果对象的锁不在(已被其他

    线程拿走),就进入等待状态,直到获得锁

    口当被 synchronized限定的代码段执行完,就释放锁

    ●后台线程

    口也叫守护线程,通常是为了辅助其它线程而运行的线程

    口它不妨碍程序终止

    口一个进程中只要还有一个前台线程在运行,这个进程就不会结束;如

    果一个进程中的所有前台线程都已经结束,那么无论是否还有未结束

    的后台线程,这个进程都会结束

    口“垃圾回收”便是一个后台线程

    口如果对某个线程对象在启动(调用stat方法)之前调用了

    setDaemon(true方法,这个线程就变成了后台线程

    sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。 wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。

    诞生状态

    口线程刚刚被创建

    就绪状态

    口线程的 start方法已被执行

    口线程已准备好运行

    运行状态

    口处理机分配给了线程,线程正在运行

    阻塞状态( Blocked)

    口在线程发出输入/输出请求且必须等待其返回

    口遇到用 synchronized标记的方法而未获得锁

    口为等候一个条件变量,线程调用wat(方法

    ●休眠状态( Sleeping)

    口执行seep方法而进入休眠

    死亡状态

    口线程已完成或退出

    线程调度

    口在单CPU的系统中,多个线程需要共享CPU,在任何时间点

    上实际只能有一个线程在运行

    口控制多个线程在同一个CPU上以某种顺序运行称为线程调度

    Java虚拟机支持一种非常简单的、确定的调度算法,叫做固

    定优先级算法。这个算法基于线程的优先级对其进行调度

    考虑这些线程在运行时环境下的调度和交替执行,也

    不需要进行额外的同步,或者在调用方进行任何其他

    的协调操作,调用这个对象的行为都可以获得正确的

    结果,那这个对象是线程安全的。

    Java线程安全 互斥同步、非阻塞同步、无同步方案

    互斥同步

    ●同步的互斥实现方式:临界区( Critical Section),互斥量

    ( Mutex),信号量( Semaphore)

    ● Synchronized关键字:经过编译后,会在同步块前后形成

    monitorenter和 monitorexit两个字节码。

    口(1) synchronized同步块对自己是可重入的,不会将自己锁死;

    口(2)同步块在已进入的线程执行完之前,会阻塞后面其他线程的进入

    采用 synchronized,重入锁可实现:等待可中断、公平锁、锁

    可以绑定多个条件

    Synchronized表现为原生语法层面的互斥锁,而 RenentrantLock表

    现为API层面的互斥锁

    ●阻塞同步:互斥同步存在的问题是进行线程阻塞和唤醒所带来的性

    能问题,这种同步称为阻塞同步( Blocking Synchronization)。这是

    种悲观并发策略

    ●非阻塞同步:不同于悲观并发策略,而是使用基于冲突检测的乐观

    并发策略,就是先进行操作,如果没有其他线程征用共享数据,则

    操作成功;否则就是产生了冲突,采取不断重试直到成功为止的策

    种策略不需要把线程挂起,称为非阻塞同步

    ●使用硬件处理器指令进行不断重试策略(DK15以后

    口测试并设置( Test-and-set)

    口获取并增加( Fetch- and-Increment)

    口交换(Swap)

    口比较并交换( Compare-and-Swap,简称CAS)

    口加载链接,条件存储( Load-Linked, Store-conditional简称LLSC)

    例:java实现类 AtomicInteger, AtomicDouble等等。

    ●可重入代码:也叫纯代码。相对线程安全来说,可以保证线程安全。

    可以在代码执行过程中断它,转而去执行另一段代码,而在控制权

    返回后,原来的程序不会出现任何错误。

    ●线程本地存储:如果一段代码中所需要的数据必须与其他代码共享,

    那就看看这些共享数据的代码是否能保证在同一个线程中执行,如

    果能保证,就可以把共享数据的可见范围限定在同一个线程之内,

    这样无需同步也能保证线程之间不出现数据争用问题。

    锁优化(源自」DK6)

    ●自旋锁

    ●自适应锁

    ●锁消除

    ●锁粗化

    偏向锁

    ●互斥同步存在的问题:挂起线程和恢复线程都需要转入内核态中完

    成,这些操作给系统的并发性能带来很大的压力

    ●自旋锁:如果物理机器有一个以上的处理器能让两个或以上的线程

    同时并行执行,那就可以让后面请求锁的那个线程“稍等一会”,

    但不放弃处理器的执行时间,看看持有锁的线程是否很快就会释放

    锁。为了让线程等待,我们只需要让线程执行一个忙循环(自旋)

    这项技术就是自旋锁。Java中自旋次数默认10次

    自适应自旋

    ●自适应意味着锁自旋的时间不再固定,而是由前一次在同一个锁

    上的自旋时间及锁拥有者的状态来决定。如果在同一个锁对象上,

    自旋等待刚刚成功获得锁,并且持有锁的线程正在运行中,那么

    虚拟机就会认为这次自旋也很有可能再次成功,进而它允许自旋

    等待相对更长的一段时间。

    锁消除

    ●定义:JVM即时编译器在运行时,对一些代码上要求同步,但是

    被检测到不可能存在共享数据竞争的锁进行消除

    ●判定依据:如果判断在一段代码中,堆上的所有数据都不会逃逸

    出去从而被其他线程访问到,那就可以把它们当作栈上数据对待,

    认为他们是线程私有的,同步加锁自然无需进行。

    锁粗化

    通常我们的代码总是将同步块的作用范围限制得尽量小,只在共享数

    据的实际作用域中才进行同步,这样是为了使得同步操作的数量尽可

    能变小

    ●另一种情况是,如果一系列的连续操作都对同一个对象反复加锁,甚至加

    锁操作是出现在循环体中,那即使没有线程争用,频繁的进行互斥同步也

    会导致不必要的性能损耗,此时只需要将同步块范围扩大即可。即:锁粗

    偏向锁

    ●目的:消除数据无竟争情况下的同步原语,进一步提高程序运行的性

    能。偏向锁就是在无竟争的情况下把整个同步都消除掉,连CAS操作

    都不做

    ●偏向:意思是这个锁会偏向于第一个获得它的线程,如果在接下来的

    执行中,该锁没有被其他线程获取,则持有偏向所得线程永远不需要

    再进行同步

    相关文章

      网友评论

          本文标题:线程

          本文链接:https://www.haomeiwen.com/subject/ciamectx.html