多线程的目的
提到多线程就只有一个目录,更好的利用CPU资源,我们让CPU同时处理多个任务,缩短计算和处理时间。
在实现多线程之前,首先了解一个下多线程的一些概念:
多线程:在一个进程中,运行不止一个线程
并行:多个CPU 实例或者多台计算机同时运行一段处理逻辑,该中情况是真正的在时间上同时值行哦
并发:通过CPU的调度算法,让用户看上去好像是在并行,但是实际上CPU并不是真正的同时,下面引用网友一张图:
线程安全:线程安全一般是对变量或者一段代码而言,指在并发的情况下,该代码被多线程使用,但是线程的调度顺序并不会应用最终结果,我们编写程序只需要考虑系统的内存和CPU是否够用即可,反过来,非线程安全即使线程的先后值行可能造成不一样的结果
同步:在Java中的同步指的是通过人为的控制程序值行,保证共同资源来对多线程访问是线程安全的,一般同步有多种实现比如 synchronized 关键字还有CAS算法等
线程的状态
线程从开始到结果经过了如下几种状态:
NEW:新建一个线程,该线程还没有开始运行
RUNNABLE:线程在JAVA虚拟机中运行,但是在等待操作系统资源,比如CPU资源
BLOCKED:线程被阻塞,在等待监视器锁的释放进入同步方法
WAITING:线程等待,该状态是当前线程等待另一个线程值行一个特定的程序
TIMED_WAITING:和WAITING类型,不过该等待会指定一个特定时间
TERMINATED:进程运行完成
以上状态可以参考JAVA 源码中对进程状态的描述:Thread.State
锁
JAVA中锁是监视器提供了必要的支持。逻辑上锁是对象内存堆中头部的一部分数据,JVM中每个对象都拥有一个锁(互斥锁),任何程序都可以使用它来协调对对象的访问,如果任何程序想访问该对象实例,则必须获取该对象的锁(在锁中有区域设置),其他线程试图访问该对象必须等到获取该锁的实例释放锁(改变锁的标记)
锁用来保护代码片段,任何时刻只能有一个线程执行被保护的代码,如果一个方法使用synchronized关键字声明,那么对象的锁将保护整个方法
监视器
监视器可以看作是一个特殊的房间,该房间中有一些数据和代码,但是一次只能有一个人(消费者)使用该房间,如果想进入该房间,必须到大厅(Entry Set)去排队,调度程序会使用某种标准比如FIFO,从大厅中选择一个消费者(线程)进入特殊房间,如果这个线程因为某种原因被挂起,它将会被调度程序安排的等待房间(Wait Set),并经过一段时间之后重新分配到特殊房间。
简单来说:监视器就是用来监视线程进入特殊房间,他确保同一时间只能有一个线程可以访问特殊房间中的数据和代码。
wait/notify必须存在于synchronized中,并且,这三个关键字针对同一个监视器
volatile 关键字
多线程的内存模式:主存(main memory、线程栈(working memory),处理数据时,线程会把值从主存laod到本地线程栈,完成操作后在save回去(volatie关键字就是针对该变量的操作都激发一次 load and save)
针对多线程使用的变量,如果不是volatile或者final修饰,很可能产生不可预知的结果(另外一个线程修改了值,但是其他线程看到的却是之前的值),从道理上讲,同一个实例的同一个属性本身只有一个副本,但是多线程会缓存该值(load到本地),volatile关键字的作用就是不去缓存,直接取值,一般情况下,多任务环境下的各项共享标志都应该添加volatile,当然也可以使用锁,但是将锁应用到一个变量上这个代码有点大。。。
需要注意的一点:volatile不保证原子性,所有可能会读取到脏数据。
如何创建一个线程
Java中创建线程有三种方式:Thread类,Runnable接口,Callable接口