Java线程总结(二)
一.线程同步
(一).同步代码块:
1.概述:
由于run方法的方法体不具有同步安全性,所以,当有两个并发线程一起修改同一个文件时,可能会出现异常,所以引入同步监视器来解决。
2.语法格式:
synchronized(obj)
{
//同步代码块
}
obj相当于同步监视器
线程开始执行同步代码块之前,必须先获得对同步监视器的锁定
任何时候只能有一条线程可以获得对同步监视器的锁定,当同步代码块执行结束后,该线程释放对同步监视器的锁定,这样可以保证并发线程在任何时候,只有一条线程可以进入修改共享资源的代码区,保证了线程的安全性。
(二).同步方法:
1.定义:
被关键字synchronized修饰的方法,成为同步方法。不需要指定同步监视器,其监视器是this,也就是其对象本身。
synchronized关键字可以修饰方法,可以修饰代码块,但不能修饰构造器,属性等
2.同步方法的使用可以将类变成线程安全的类,其特征为:
可以被多个线程安全访问
每个线程调用该对象的任意方法后,均可得到正确的结果
每个线程调用该方法的任意方法之后,该对象的状态依然保持合理
(三).释放同步监视器的锁定
程序无法显示释放对同步监视器的锁定,线程会在如下几种情况释放对同步监视器的锁定:
当线程的同步方法,同步代码块执行结束后,当前线程即释放同步监视器
当线程在同步代码块,同步方法中遇到break,return终止改代码块,该方法的执行,当前线程即释放同步监视器
当线程在同步代码块,同步方法中出现了未处理的Error或Exception,导致其异常结束,会释放
当线程执行同步代码块或同步方法时,程序执行同步监视器对象的wait()方法,则当前线程暂停,并释放同步监视器
当遇到下面的情况,线程不会释放同步监视器:
执行同步代码块或同步方法时,程序调用Thread.sleep(),Thread.yield()方法来暂停当前程序的执行,当前程序不会释放同步监视器
线程执行同步代码块时,其他线程调用了该线程的suspend方法将该线程挂起,该线程不会释放同步监视器。
二.同步锁
Lock是控制多个线程对共享资源进行访问的工具。通常,锁提供了对公共资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。
通常使用ReentrantLock,可以显示地加锁,释放锁。
在使用Lock对象进行同步时,锁定和释放锁出现在不同的范围时,一般用finally块来确保在必要时释放锁。
同步方法或同步代码块使用与竞争资源相关的,隐式的同步监视器,并且强制要求加锁和释放锁出现在一个块结构中,当获取多个锁时,其必须以相反顺序释放,且在于所有锁被获取时相同范围内释放所有锁
死锁
当两个线程相互等待对方释放同步监视器时会发生死锁,当出现死锁时,整个程序不会发生异常,也不会给出任何提示,只是所有线程处于阻塞状态,无法继续。
三.线程通信
(一).线程的协调运行
wait():导致当前线程等待,直到其他线程调用该同步监视器里的notify()方法,或notifyAll()方法来唤醒线程。该方法无时间参数时,会一直等待,直到其他线程通知;有参数的会在指定时间内苏醒
notify():唤醒在此同步监视器上等待的单个线程,如果所有线程均处于等待状态,则会随机选择一个,前提是放弃对同步监视器的锁定
notifyAll():唤醒在此同步监视器上等待的所有线程,前提是放弃对同步监视器的锁定
调用这三个方法时,必须使用synchronized后括号里的对象,来调用
(二).使用条件变量控制协调
当使用Lock对象来保证同步时,Java提供Condition类来保持协调,可以让那些已经得到Lock对象却无法继续执行的线程释放Lock对象,也可以唤醒其他处于等待的线程。
Condition实例实质上被绑定在Lock对象上,要获得该实例调用Lock对象newCondition()方法即可。Condition类提供如下三个方法:
await():类似于隐式同步监视器上的wait()方法,等待signal()方法,或signalAll()方法来唤醒该线程。
signal():唤醒在此同步监视器上等待的单个线程,如果所有线程均处于等待状态,则会随机选择一个,前提是放弃对同步监视器的锁定
signalAll():唤醒在此同步监视器上等待的所有线程,前提是放弃对同步监视器的锁定
(三).使用管道流
管道流存在三种形式:PipedInputStream和PipedOutStream,PipedReader和PipedWriter以及Pipe.SinkChannel和Pipe.SourceChannel,他们分别是管道字节流,管道字符流,和新IO的管道Channel。
使用管道流实现多线程通信可按照如下步骤:
1.使用new操作符分别创建管道输入流和管道输出流
2.使用管道输入流或管道输出流的connect方法把两个输入输出流连接起来
3.将管道输入流,管道输出流分别传入两个线程
4.两个线程可以分别依赖各自的管道输入流,管道输出流进行通信