线程是指进程中的一个执行流程,一个进程中可以运行多个线程。线程总是属于某个进程,进程中的多个线程共享进程的内存。
线程总体分两类:用户线程和守护线程。
守护线程概念:后台线程主要是为其它线程(相对可以称之为前台线程)提供服务,或”守护线程”。如JVM中的垃圾回收线程。 生命周期:当所有的前台线程都进入死亡状态时,后台线程会自动死亡
新建状态(NEW): 当线程对象创建后即进入了新建状态。 就绪状态(Runnable): 当调用线程对象的Start()方法,线程即进入就绪状态。处于就绪状态的线程只是说明此线程已经做好了准备,随机等待CPU调度执行,并不是说执行了Start()此线程立即就会执行。 运行状态(Running): 当CPU开始调度处于就绪状态的线程时,此线程才真正执行,即进入运行状态。 阻塞状态(Blocked): 处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态才有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种: 1、等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态。 2、同步阻塞:线程在获取Synchronized同步锁失败(因为锁被其它线程锁占用),它会进入同步阻塞状态。 3、其它阻塞:通过调用线程的Sleep()或join()或发出了I/O请求时,线程会进入阻塞状态。当Sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。 死亡状态(Dead): 线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
1、继承Tread类,重写该类的run()方法。 2、实现Runnable接口,并重写该接口的run()方法。 3、使用Callable和Future接口创建线程,具体是创建Callable接口的实现类并实现call()方法。并使用FutureTask类来包装Callable实现类的对象。
同步方法
即有sychronized关键字修饰的方法,由于java的每个对象都有一个内置锁,当用此关键字修饰方法时内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。
同步代码块
即有sychronized关键字修饰的语句块,被该关键字修饰的语句块会自动被加上内置锁,从而实现同步。
使用特殊域变量(volatile)实现线程同步
volatile关键字为域变量的访问提供了一种免锁机制,使用volatile修饰域相当于告诉虚拟机该域可能会被其它线程更新,因此每次使用该域就要重新计算而不是使用寄存器中的值。volatile不会提供任何原子操作,它也不会用来修饰final类型的变量
使用重入锁实现线程同步
ReentrantLock类是可重入、互斥、实现了Lock接口的锁,它与使用synchonized方法和块具有相同的基本行为和语义,并且扩展了其能力。 ReentrantLock():创建一个ReentrantLock实例 Lock():获得锁 unLock():释放锁
使用局部变量实现线程同步
如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间互相独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其它线程产生影响。 ThreadLocal():创建一个线程本地变量 get():返回此线程局部变量的当前线程副本中的值 initialValue():返回此线程局部变量的当前线程的”初始值” set(T value):将此线程局部变量的当前线程副本中的值设置为value
使用阻塞队列实现线程同步
前面5种同步方式都是在底层实现的线程同步,但是我们在实际开发当中,应当尽量远离底层结构。使用java.util.concurrent包将有助于简化开发。 LinkedBlockingQueue是一个基于已连接节点的,范围任意的blocking queue.队列是先进先出的顺序(FIFO) put(E e):在队尾添加一个元素,如果队列满则阻塞 size():返回队列中的元素个数 take():移除并返回队头元素,如果队列空则阻塞
使用原子变量实现线程同步
需要使用线程同步的根本原因在于对普通变量的操作不是原子的。 原子操作就是指将读取变、修改变、保存变量值看成一个整体来操作,即这几种行为要么同时完成,要么都不完成。 Atomiclnteger表可以用原子方式更新int的值,可用在应用程序中(如以原子方式增加的计数器),但不能用于替换Integer;可扩展Number,允许那些处理机域数字类的工具和实用工具进行统一访问。 addAddGet(int dalta):以原子方式将给定值与当前值相加 get():获取当前值
新闻热点
疑难解答