您的位置:首页 > 编程语言 > Java开发

(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)有临界区是为了让更多的其它线程能够访问资源。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: