锁对象

有两种机制防止代码块受到并发的干扰:

1.一种是Synchronized关键字,自动提供一个锁和相关的条件对象。

2.jdk 5.0引入了Reentrantlock类。

Reentrantlock的用法:

this.banklock=new ReentrantLock();//获取锁对象[/align]banklock.lock();//获得锁try{//业务代码HashMap<String, Account> accounts = totalAccount.getAccounts();}finally{banklock.unlock();//释放锁}

使用RentrantLock构建的是一个可重入的锁,对于重入的概念不是非常的理解

锁的升级与对比

Java1.6为了减少获得锁和释放锁带来的消耗,引入了“偏向锁”和“轻量级锁”,

Java1.6中一共有4中锁状态,基本从低到高依次是:无锁状态,偏向锁状态,轻量级锁状态,重量级锁状态。这几个锁会随着竞争情况逐步升级,锁可以升级当时不能降级。

偏向锁为什么要使用锁:

在多线程应用中,如果有两个或两个以上线程需要同时去修改一个共享的数据,这种情况就有可能产生讹误的对象,这个情况通常称为:竞争条件。

假设:线程1正在执行修改操作,但是这个操作还没有完成,它被线程调度器剥夺了运行权,这个时候第2个线程修改了数据,然后第1个线程被唤醒继续执行,这个操作将会擦除第2个线程的更新操作。

如果在这一系列的操作上加上锁机制,那么就不会出现这种数据讹误的情况,加上锁后将会这么执行:线程1开始启动,并且拿到锁,在执行结束前被停止,假定第2个线程启动了,由于第2个线程没有获得锁,将在调用lock方法的时候被阻塞,必须等待第1个线程执行完毕释放锁,才能正常执行。

为什么需要在finally里释放锁:

这是至关重要的,如果在临界区的代码抛出异常可以保证锁必须被释放,否则其他线程将永远阻塞。

条件对象

为什么使用条件:

假设一个线程获得了锁,但是这个线程并不满足某些条件,不能够有效的完成所有的工作,这个时候我们就需要手动的让线程等待,并释放锁,由其他满足条件的线程执行有效的工作。

如何使用条件:

//返回一个与该锁相关的条件对象
Condition newCondition = banklock.newCondition();this.banklock=new ReentrantLock();//获取锁对象banklock.lock();//获得锁try{//业务代码iIf(xxx=false){newCondition .wait();//控制线程等待}HashMap<String, Account> accounts = totalAccount.getAccounts();newCondition.signalAll();//解除所有等待线程的阻塞状态}finally{banklock.unlock();//释放锁}

一个线程调用wait方法,它没有办法激活自身,只能等待其他线程调用signaAll方法,来激活。如果没有其他线程来激活它,将会出现死锁现象。如果所有的线程被阻塞,做最后一个线程在解除其他线程阻塞状态前调用了wait方法,那么这个程序就挂起了。

SignalAll并不能立即激活线程,它仅仅解除线程的阻塞状态,以便在当前线程退出后,通过竞争来实现对象的访问。

另一个方法signal,是随机解除一个等待线程,这比解除所有线程跟有效,但是也很危险,如果被唤醒的线程不满足条件,再次进入等待,并且没有其他线程可以调用signal,系统就死锁了。

Synchronized关键字

用法:

public synchronized void transferoff(){
If()
Wait()
notiifyAll()
}

将方法申明为synchroized(可以申明静态方法),要调用这个方法线程必须获取内部的对象锁,内部对象锁只有一个相关的条件对象,wait方法添加一个线程到等待集中,notifyAll/notfiy方法,解除等待线程的阻塞状态。

内部锁和条件的局限性:

1. 不能中断一个正在试图获得锁的线程

2. 试图获得锁时不能设定超时

3. 每个锁仅有一个单一条件,可能不够

在代码中应该使用那种机制?Lock和condition对象还是同步方法(synchrionized)?

用java.util.concurrent包中的一种机制,它会为你处理所有的枷锁,你会看到如何使用阻塞队列来同步完成一个共同任务。

4. 如果synchronized适合你的程序,那么尽量使用它,可以减少代码,减少出错几率。

5. 如果特别需要lock和condition结构提供的独有特性时,才使用。

锁和条件的关键之处:

1. 锁用来保护代码片段,任何时候只能由一个线程访问被保护的代码。

2. 锁可以管理试图进入被保护代码的线程

3. 锁可以拥有一个或多个条件对象

4. 每个条件对象管理已经进入被保护代码但不能运行的线程。

同步阻塞

Object lock=New Object();

每一个java对象有一个锁,通过synchronized(lock) 进入一个同步阻塞,获得Object的锁,有时候程序员使用对象的锁来实现原子性,实际上称为客户端锁定,客户端锁定非常脆弱不推荐使用。

监视器概念

没有什么实质性的作用

Volatile域

Volatile为实例域提供一种免锁机制,如果申明一个域为volatile,那么编译器和虚拟机就知道该域是有可能被另一个线程并发跟新,volatile不能提供原子性,实例域有可能在方法中被从新赋值。

同步格言:“如果向一个变量写入值,而这个变量接下来可能会被另外一个线程读取, 或者,从一个变量读值,而这个变量可能是之前被另外一个线程写入的,此时必须同步”

Final变量

除非使用锁和volatile,否则无法从多个线程安全的读取一个域,使用final变量申明一个实例域,就可以保证其他线程获取的值不会为null

原子性

要对共享变量进行原子性修饰。

死锁

所有的线程进入阻塞状态,并且没有其他线程可以唤醒,出现死锁,程序挂起。

避免死锁的几种方式:

1. 避免一个线程同时获取多个锁。

2. 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。

3. 尝试使用定时锁,lock.tryLock(timeout),这种锁可以打破死锁。

4. 对于数据库锁,加锁和解锁必须在一个数据库链接里,否则会出现解锁失败得情况。

线程局部变量

如果需要避免共享变量,可以使用ThreadLocal为每个线程提供各自的实例域,可以为单独的线程提供生成器。

锁测试与超时

使用trylock方法试图申请一个锁,如果成功获得锁则返回true,否则立即返回false,而且当前线程可以立即离去做其他事情。

如果使用带有超时参数的trylock,线程在等待期间中断,将抛出InterruptedException异常,这是一个非常有用的特性,它允许打破死锁。

读 / 写锁

使用ReentrantReadWriteLock类可以获取一个读写锁对象,

使用RW.readlock()得到一个可以被多个读操作公用的读锁,但会排斥写操作。

使用RW.writeLock()得到一个写锁排斥所有其他读操作与写操作

为什么弃用stop和suspend方法

Stop方法天生不安全,suspend方法经常导致死锁。