(34)21.3.5 临界区---Java编程思想之并发笔记
2012-12-31 15:45
471 查看
1. 临界区:只希望防止多个线程同时访问方法内部的部分代码而不是正规方法,通过这种方式分离出来的代码段被称为临界区(critical section)。使用关键字synchronized关键字建立。这里synchronized被用来指定某个对象,此对象的锁被用来对花括号内的代码进行同步。
2. 代码格式:
synchronized(object){
//同步代码块
}
这也被称为同步控制块。在进入此段代码前,必须得到synchronized对象的锁。如果其它线程已经得到了这个锁,那么就得等到锁释放以后,才能进入临界区。
3. 使用同步控制块的好处:可以使多个任务访问对象的时间性能得到显著提高。
4. 例子1:
package jiangning.c21;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
class Pair{
private int x,y;
public Pair(int x, int y){
this.x = x;
this.y = y;
}
public Pair(){
this(0,0);
}
public int getX(){
return x;
}
public int getY(){
return y;
}
public void incrementX(){
x++;
}
public void incrementY(){
y++;
}
public String toString(){
return "x: " + x + ", y: " + y;
}
public class PairValuesNotEqualException extends RuntimeException{
public PairValuesNotEqualException(){
super("Pair value not equal:" + Pair.this);
}
}
public void checkState(){
if(x != y){
throw new PairValuesNotEqualException();
}
}
}
abstract class PairManager{
AtomicInteger checkCounter = new AtomicInteger(0);
protected Pair p = new Pair();
private List<Pair> storage = Collections.synchronizedList(new ArrayList<Pair>());
public synchronized Pair getPair(){
return new Pair(p.getX(),p.getY());
}
protected void store(Pair p){
storage.add(p);
try {
TimeUnit.MILLISECONDS.sleep(50);
} catch (InterruptedException e) {
}
}
public abstract void increment();
}
class PairManager1 extends PairManager{
public synchronized void increment(){
p.incrementX();
p.incrementY();
store(getPair());
}
}
class PairManager2 extends PairManager{
public void increment(){
Pair temp;
synchronized(this){
p.incrementX();
p.incrementY();
temp = getPair();
}
store(temp);
}
}
class PairManipulator implements Runnable{
private PairManager pm;
public PairManipulator(PairManager pm){
this.pm = pm;
}
public void run(){
while(true){
pm.increment();
}
}
public String toString(){
return "Pair: " + pm.getPair() +
" checkCounter = " + pm.checkCounter.get();
}
}
class PairChecker implements Runnable{
private PairManager pm;
public PairChecker(PairManager pm){
this.pm = pm;
}
public void run(){
while(true){
pm.checkCounter.incrementAndGet();
pm.getPair().checkState();
}
}
}
public class CriticalSection {
static void testApproaches(PairManager pman1, PairManager pman2){
ExecutorService exec = Executors.newCachedThreadPool();
PairManipulator
pm1 = new PairManipulator(pman1),
pm2 = new PairManipulator(pman2);
PairChecker
pchecker1 = new PairChecker(pman1),
pchecker2 = new PairChecker(pman2);
exec.execute(pm1);
exec.execute(pm2);
exec.execute(pchecker1);
exec.execute(pchecker2);
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
System.out.println("Sleep interrupted ");
}
System.out.println("pm1: " + pm1 +"\npm2: " + pm2);
System.exit(0);
}
public static void main(String[] args) {
PairManager
pman1 = new PairManager1(),
pman2 = new PairManager2();
testApproaches(pman1, pman2);
}
}
分析:
Pair不是线程安全的,因为它的约束条件需要两个变量维护成相同的值,此外自增操作不是线程安全的,并且没有任何方法标记为synchronized,所以不能保证一个Pair对象在多线程程序中不会被破坏。
解决方法:通过构建PairManager类实现这一点,PairManager类持有一个Pair对象,并控制对它的一切访问。注意唯一的public方法getPair,它是synchronized的,对抽象方法increment(),对increment()的同步控制将在实现的时候进行处理。
5. 使用同步块而不是同步整个方法的原因之一是:使得其他线程能够更多地访问资源。
6.例子用Lock实现临界区。
[align=left]package jiangning.c21;[/align]
[align=left]import java.util.concurrent.locks.Lock;[/align]
[align=left]import java.util.concurrent.locks.ReentrantLock;[/align]
[align=left]class ExplicitPairManager1 extends PairManager{[/align]
[align=left] private Lock lock = new ReentrantLock();[/align]
[align=left] public synchronized void increment(){[/align]
[align=left] lock.lock();[/align]
[align=left] try {[/align]
[align=left] p.incrementX();[/align]
[align=left] p.incrementY();[/align]
[align=left] store(getPair());[/align]
[align=left] } finally {[/align]
[align=left] lock.unlock();[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] }[/align]
[align=left]}[/align]
[align=left]class ExplicitPairManager2 extends PairManager{[/align]
[align=left] private Lock lock = new ReentrantLock();[/align]
public void increment()
{
[align=left] Pair temp;[/align]
[align=left] lock.lock();[/align]
[align=left] try {[/align]
[align=left] p.incrementX();[/align]
[align=left] p.incrementY();[/align]
[align=left] temp = getPair();[/align]
[align=left] } finally {[/align]
[align=left] lock.unlock();[/align]
[align=left] }[/align]
[align=left] store(temp);[/align]
[align=left] [/align]
[align=left] }[/align]
[align=left]}[/align]
public class ExpliciticalSection
{
public static void main(String[]
args) {
[align=left] PairManager[/align]
[align=left] pman1 = new ExplicitPairManager1(),[/align]
[align=left] pman2 = new ExplicitPairManager2();[/align]
CriticalSection. testApproaches(pman1,
pman2);
[align=left] }[/align]
[align=left]}[/align]
[align=left]/**[/align]
[align=left]pm1: Pair: x: 21, y: 21 checkCounter = 868[/align]
[align=left]pm2: Pair: x: 22, y: 22 checkCounter = 1450417[/align]
[align=left]*/[/align]
总结:
1)临界区实现方法有两种,一种是用synchronized,一种是用Lock显式锁实现。
2)有临界区是为了让更多的其它线程能够访问资源。
2. 代码格式:
synchronized(object){
//同步代码块
}
这也被称为同步控制块。在进入此段代码前,必须得到synchronized对象的锁。如果其它线程已经得到了这个锁,那么就得等到锁释放以后,才能进入临界区。
3. 使用同步控制块的好处:可以使多个任务访问对象的时间性能得到显著提高。
4. 例子1:
package jiangning.c21;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
class Pair{
private int x,y;
public Pair(int x, int y){
this.x = x;
this.y = y;
}
public Pair(){
this(0,0);
}
public int getX(){
return x;
}
public int getY(){
return y;
}
public void incrementX(){
x++;
}
public void incrementY(){
y++;
}
public String toString(){
return "x: " + x + ", y: " + y;
}
public class PairValuesNotEqualException extends RuntimeException{
public PairValuesNotEqualException(){
super("Pair value not equal:" + Pair.this);
}
}
public void checkState(){
if(x != y){
throw new PairValuesNotEqualException();
}
}
}
abstract class PairManager{
AtomicInteger checkCounter = new AtomicInteger(0);
protected Pair p = new Pair();
private List<Pair> storage = Collections.synchronizedList(new ArrayList<Pair>());
public synchronized Pair getPair(){
return new Pair(p.getX(),p.getY());
}
protected void store(Pair p){
storage.add(p);
try {
TimeUnit.MILLISECONDS.sleep(50);
} catch (InterruptedException e) {
}
}
public abstract void increment();
}
class PairManager1 extends PairManager{
public synchronized void increment(){
p.incrementX();
p.incrementY();
store(getPair());
}
}
class PairManager2 extends PairManager{
public void increment(){
Pair temp;
synchronized(this){
p.incrementX();
p.incrementY();
temp = getPair();
}
store(temp);
}
}
class PairManipulator implements Runnable{
private PairManager pm;
public PairManipulator(PairManager pm){
this.pm = pm;
}
public void run(){
while(true){
pm.increment();
}
}
public String toString(){
return "Pair: " + pm.getPair() +
" checkCounter = " + pm.checkCounter.get();
}
}
class PairChecker implements Runnable{
private PairManager pm;
public PairChecker(PairManager pm){
this.pm = pm;
}
public void run(){
while(true){
pm.checkCounter.incrementAndGet();
pm.getPair().checkState();
}
}
}
public class CriticalSection {
static void testApproaches(PairManager pman1, PairManager pman2){
ExecutorService exec = Executors.newCachedThreadPool();
PairManipulator
pm1 = new PairManipulator(pman1),
pm2 = new PairManipulator(pman2);
PairChecker
pchecker1 = new PairChecker(pman1),
pchecker2 = new PairChecker(pman2);
exec.execute(pm1);
exec.execute(pm2);
exec.execute(pchecker1);
exec.execute(pchecker2);
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
System.out.println("Sleep interrupted ");
}
System.out.println("pm1: " + pm1 +"\npm2: " + pm2);
System.exit(0);
}
public static void main(String[] args) {
PairManager
pman1 = new PairManager1(),
pman2 = new PairManager2();
testApproaches(pman1, pman2);
}
}
分析:
Pair不是线程安全的,因为它的约束条件需要两个变量维护成相同的值,此外自增操作不是线程安全的,并且没有任何方法标记为synchronized,所以不能保证一个Pair对象在多线程程序中不会被破坏。
解决方法:通过构建PairManager类实现这一点,PairManager类持有一个Pair对象,并控制对它的一切访问。注意唯一的public方法getPair,它是synchronized的,对抽象方法increment(),对increment()的同步控制将在实现的时候进行处理。
5. 使用同步块而不是同步整个方法的原因之一是:使得其他线程能够更多地访问资源。
6.例子用Lock实现临界区。
[align=left]package jiangning.c21;[/align]
[align=left]import java.util.concurrent.locks.Lock;[/align]
[align=left]import java.util.concurrent.locks.ReentrantLock;[/align]
[align=left]class ExplicitPairManager1 extends PairManager{[/align]
[align=left] private Lock lock = new ReentrantLock();[/align]
[align=left] public synchronized void increment(){[/align]
[align=left] lock.lock();[/align]
[align=left] try {[/align]
[align=left] p.incrementX();[/align]
[align=left] p.incrementY();[/align]
[align=left] store(getPair());[/align]
[align=left] } finally {[/align]
[align=left] lock.unlock();[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] }[/align]
[align=left]}[/align]
[align=left]class ExplicitPairManager2 extends PairManager{[/align]
[align=left] private Lock lock = new ReentrantLock();[/align]
public void increment()
{
[align=left] Pair temp;[/align]
[align=left] lock.lock();[/align]
[align=left] try {[/align]
[align=left] p.incrementX();[/align]
[align=left] p.incrementY();[/align]
[align=left] temp = getPair();[/align]
[align=left] } finally {[/align]
[align=left] lock.unlock();[/align]
[align=left] }[/align]
[align=left] store(temp);[/align]
[align=left] [/align]
[align=left] }[/align]
[align=left]}[/align]
public class ExpliciticalSection
{
public static void main(String[]
args) {
[align=left] PairManager[/align]
[align=left] pman1 = new ExplicitPairManager1(),[/align]
[align=left] pman2 = new ExplicitPairManager2();[/align]
CriticalSection. testApproaches(pman1,
pman2);
[align=left] }[/align]
[align=left]}[/align]
[align=left]/**[/align]
[align=left]pm1: Pair: x: 21, y: 21 checkCounter = 868[/align]
[align=left]pm2: Pair: x: 22, y: 22 checkCounter = 1450417[/align]
[align=left]*/[/align]
总结:
1)临界区实现方法有两种,一种是用synchronized,一种是用Lock显式锁实现。
2)有临界区是为了让更多的其它线程能够访问资源。
相关文章推荐
- (36)21.3.6 并发 练习 15---Java编程思想之并发笔记
- java编程思想笔记-并发之线程加入
- (37)21.3.6 并发 练习 16---Java编程思想之并发笔记
- java编程思想笔记-并发之并发锁(一)
- (38)21.3.8 线程本地存储---Java编程思想之并发笔记
- (39)21.4 终结任务---Java编程思想之并发笔记
- (40)21.4.1 装饰性花园---Java编程思想之并发笔记
- Java编程思想学习笔记_6(并发)
- (41)21.4.1 并发 练习 17---Java编程思想之并发笔记
- (42)21.4.2 在阻塞时终结---Java编程思想之并发笔记
- (43)21.4.3 中断---Java编程思想之并发笔记
- java编程思想笔记-并发之线程协作(三)
- (27)21.3.1 不正确的访问资源---Java编程思想之并发笔记
- (44)21.4.3 并发 练习 18---Java编程思想之并发笔记
- java编程思想学习笔记:并发
- java编程思想笔记---并发优先级
- (29)21.3.3 原子性与易变性---Java编程思想之并发笔记
- (45)21.4.3 并发 练习 19---Java编程思想之并发笔记
- (28)21.3.2 解决共享资源竞争---Java编程思想之并发笔记
- (46)21.4.3 并发 练习 20---Java编程思想之并发笔记