Java多线程

来源:互联网 发布:好玩的网络手游 编辑:程序博客网 时间:2024/05/18 03:07

进程: 程序(任务)的执行过程,进程持有资源(共享内存、共享文件)和线程。

线程: 线程是系统中最小的执行单元,同一个进程中有多个线程,线程共享进程资源。

线程创建的两种方式:
1、继承Thread类;2、实现Runnable接口

两种方式的比较:

  • Runnable方式可以避免Thread方式由于Java单继承性带来的缺陷
  • Runnable的代码可以被多个线程共享(Thread实例),适合于多个线程处理同一个资源的情况

线程的生命周期:
这里写图片描述

创建: Thread t = new Thread()

就绪: 创建了线程对象后,调用了线程的start()方法。此时线程只是进入了线程队列,等待获取cpu服务,具备了运行条件,但并不一定已经开始运行了

运行: 处于就绪状态的线程,一旦获取了cpu资源,便进入了运行状态,开始执行run()方法里面的逻辑

终止: 线程的run()方法执行完毕,获取线程调用了stop()方法(不提倡这样停止线程,正确的停止方法是设置停止标识),线程便进入了终止状态

阻塞: 一个正在执行的线程在其他情况下,由于某种原因暂时让出了cpu资源,暂停了自己的执行,便进入了阻塞状态,如调用了sleep()方法

线程的种类:
1、用户线程:运行在前台,执行具体的任务,例如程序的主线程、连接网络的子线程
2、守护线程:运行在后台,为其他线程服务

守护线程:
特点:一旦所有用户线程都结束了,守护线程也会随着JVM结束工作
应用:数据库连接池的监测线程,JVM虚拟机启动后的监测线程
最常见的守护线程:垃圾回收线程
设置守护线程:通过调用Thread类的setDaemon(true)方法来设置当前的线程为守护线程
注意事项:1、setDaemon(true)必须在start()方法之前调用,否则会抛出IllegalThreadStateException异常;2、在守护线程中产生的新线程也是守护线程;3、不是所有的任务都可以分配给守护线程来执行,比如读写操作或者计算逻辑。

线程的争用条件:
当多个线程同时共享访问同一数据(内存区域)时,每个线程都尝试操作该数据,从而导致数据被破坏,这种现象称为争用条件(为了避免该问题可以设置共享对象)

线程的互斥与同步:
互斥:多个线程操作同一个资源,为保证线程在对资源的状态进行一些非原子性操作后,状态仍然是正确的。典型的例子是“售票厅售票应用”,对于互斥可以使用synchronized关键字
同步:在某些情况下,线程需要交替执行。比如一个线程向一个存储单元执行存放数据,而另一个操作执行取值操作,线程间同步完成这个存取任务,需要将这些线程同步。要解决线程交替执行但是又要保证共享资源安全,这需要使用到JAVA3个方法:wait()、notify()和nodifyAll()。

线程的可见性:
一个线程对共享变量(如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量)值的修改能够及时地被其他线程看到。

注意:所有的变量都存储在主内存中,每个内存都有自己独立的工作内存,里面保存该线程使用到的变量的副本(主内存中该变量的一份拷贝)。
这里写图片描述
线程对共享变量的操作都必须在自己的工作内存中进行,不能直接在主内存中读写。
不同线程之间无法直接访问其他线程工作内存中的变量,线程之间值的传递需要主内存来完成
线程a对共享变量的修改能够被线程2及时看到,必须经历两个步骤:
1、在线程a的工作内存中更新过的共享变量刷新到主内存中
2、将主内存中最新的共享变量的值更新到线程b的工作内存中

实现共享变量的可见性,必须保证两点:
1、线程修改后的共享变量的值能够及时从工作内存刷新到主内存中
2、其他线程能够及时把共享变量的最新值从主内存中更新到自己的工作内存中

Synchronized实现可见性:
JVM关于synchronized的规定:线程解锁前,必须把共享变量的最新值刷新到主内存中;线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新获取最新的值(加锁和解锁需要同一把锁);线程解锁前对共享变量的修改将在下次加锁时对其他线程可见。

线程执行互斥锁的过程:获得了互斥锁—>清空工作内存—>从主内存拷贝变量的最新值到工作内存—>执行diamagnetic—>将更改后的共享变量的值刷新到主内存—>解放互斥锁

导致共享变量在线程间不可见的原因:线程的交叉执行;重排序结合线程交叉执行;共享变量更新后的共享变量的值没有在工作内存与主内存中及时更新。synchronized是通过保证了锁内部代码的原子性以及可见性。

volatile关键字实现可见性:
能够保证volatile变量的可见性,不能保证volatile变量符合操作的原子性。

volatile实现内存可见性的方法:volatile变量每次被线程访问破坏时,都强迫从内存中重读该变量的值,而当该变量发生变化时,又会强迫线程将最新的值刷新到主内存中,这样任何时刻,不同线程总可以看到该变量的最新值。

线程写volatile变量的过程:
改变线程工作内存中volatile变量副本的值—>将改变后的副本的值从工作内存刷新到主内存
线程读volatile变量的过程:
从主内存读取volatile变量的最新值到线程的工作内存—>从工作内存中读取volatile变量的副本

volatile的使用场合:
1、对变量的写入操作不依赖其当前值
不满足:number++,count=count*4等
满足:boolean变量,记录温度变化的变量等
2、该变量没有包含在具有其他变量的不变式中
不满足:low < up

synchronized和volatile比较:
1、volatile不需要加锁,比synchronized更加轻量级,不会阻塞线程
2、从内存可见性讲,volatile读相当于加锁,volatile写相当于解锁
3、synchronized既能保证可见性,又能保证原子性,而volatile只能保证可见性,无法保证原子性。

原创粉丝点击