多线程基础

学习资料:

 深入浅出多线程     JavaGuide 《并发编程的艺术》  百度233

1,进程与线程

1.1,进程:运行一个程序,就会创建进程。应用程序在内存中分配的空间。让操作系统实现并发。

        线程:一个程序(进程)的子任务。实现进程内部的并发。轻量级线程。

1.2,关系:

has a     进程可以包含多个线程,共享进程的资源。

is   a      线程是轻量级的进程

 

2,并发与并行:

并发:同一时间段内,多个任务都在指行(单CPU进程调度)看起来是并行的。

并行:同一时刻,多个任务同时执行,(多核CPU,真正意义上的并行)

3,为什么使用多线程

消耗资源小。线程切换和调度的成本远小于进程,

相应速度快。大量并发网络环境,需要多线程机制快速相应。

  • 多线程应用场景:Web服务器,专用服务器(游戏)。后台任务(群发邮件)。异步处理。分布式计算。

CPU利用率高。多核CPU时代,多个核心运行多个线程,减少饥饿。

  • 线程越多,利用率就越高?  多线程一定能提高CPU利用率?     

使用多线程之前CPU的空闲时间  大于  使用多线程后用于线程切换上下文的时间。两者之差才是提升CPU真正利用在计算上的时间。

  • 什么是上下文切换

线程/进程 调度 中时间片流转法,当一个线程时间片用完或者被中断阻塞了,要切换下一个就绪队列中的线程,要保存当前线程的‘现场’

,即线程状态信息,资源信息,程序计数器(记住线程执行到哪了,下次轮到时间片时接着执行)等。同时将要切换进来的线程保存的

‘现场’信息加载进来,接着他上次执行的地方接着执行。

4,线程的生命周期:NEW ,RUNNABLE,BLOCED,WAITING,TIME_WAITING,TERMINATED

①new一个线程的时候,处于NEW状态

②调用start之后处于RUNNABKE状态,可执行态,分为就绪态(等待CPU时间片)和运行态。

③BLOCED:当线程尝试获取锁失败被挂起阻塞时

④WAITING,Object.wait(),Thread.join(),后释放cpu和锁,等待被唤醒。Object.notify(),Object.notifyAll().

⑤TIME_WAITING,超时等待(定时等待)一定时间后回到可执行态。Thread.join(long),Object.wait(long),Thread.sleep(long)

⑥TERMINATED,线程run方法执行完成后。

 

5,创建多线程方法

继承Thread类,实现Runnable接口,实现Callable接口(有返回函数)

 

6,多线程带来的问题:

死锁,内存泄漏,线程不安全(数据不同步),

。。。

 

7,解决多线程同步问题:

既然多线程同一时刻有多个线程访问/修改同一个资源/数据,会出错。那就把资源/数据 锁住就像公共厕所,入坑关门反锁,排队(抢锁机制)入坑。

7.1,锁。    synchronized与Lock

锁分类:(不可不说的Java“锁”事

可重入锁-非可重入锁 , 公平锁-非公平锁 ,  读写锁-排他锁 , 悲观锁-乐观锁。

常用排他锁:synchronized  ,Reentrantlock

 

7.2,锁的结构:(个人理解,如有误导,多谢指正)

①锁状态(锁标识,)

            标识位,一般用一个volatile  int遍量 如   private volatile int state;(volatile后面再解释),规定好,state的值为多少对应什么状态

          上锁释放锁时对state怎么操作(+1,-1)

②阻塞队列+排队机制(抢锁机制根据锁状态)

 

③等待/通知机制(释放锁的时候通知其他人现在锁空了,赶紧来枪锁)(这是线程之间通信里的内容,放锁里一起理解,便于比较)

 

8,CAS操作:

CAS的全称是:比较并交换(Compare And Swap)。在CAS中,有这样三个值:

    • V:要更新的变量(var)
    • E:预期值(expected)
    • N:新值(new)

比较并交换的过程如下:

判断V是否等于E,如果等于,将V的值设置为N;如果不等,说明已经有其它线程更新了V,则当前线程放弃更新,什么都不做。

所以这里的预期值E本质上指的是“旧值”。

我们以一个简单的例子来解释这个过程:

    1. 如果有一个多个线程共享的变量i原本等于5,我现在在线程A中,想把它设置为新的值6;
    2. 我们使用CAS来做这个事情;
    3. 首先我们用i去与5对比,发现它等于5,说明没有被其它线程改过,那我就把它设置为新的值6,此次CAS成功,i的值被设置成了6;
    4. 如果不等于5,说明i被其它线程改过了(比如现在i的值为2),那么我就什么也不做,此次CAS失败,i的值仍然为2。

在这个例子中,i就是V,5就是E,6就是N。

那有没有可能我在判断了i为5之后,正准备更新它的新值的时候,被其它线程更改了i的值呢?

不会的。因为CAS是一种原子操作,它是一种系统原语,是一条CPU的原子指令,从CPU层面保证它的原子性

当多个线程同时使用CAS操作一个变量时,只有一个会胜出,并成功更新,其余均会失败,但失败的线程并不会被挂起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作

 

原文地址:https://www.cnblogs.com/wangpan8721/p/13782620.html