1. 临界区:只希望防止多个线程同时访问方法内部的部分代码而不是正规方法,通过这种方式分离出来的代码段被称为临界区(critical section)。使用关键字synchronized关键字建立。这里synchronized被用来指定某个对象,此对象的锁被用来对花括号内的代码进行同步。
2. 代码格式:
synchronized(object){
//同步代码块
}
这也被称为同步控制块。在进入此段代码前,必须得到synchronized对象的锁。如果其它线程已经得到了这个锁,那么就得等到锁释放以后,才能进入临界区。
3. 使用同步控制块的好处:可以使多个任务访问对象的时间性能得到显著提高。
4. 例子1:
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()的同步控制将在实现的时候进行处理。
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实现临界区。
package
jiangning.c21;
import
java.util.concurrent.locks.Lock;
import
java.util.concurrent.locks.ReentrantLock;
class
ExplicitPairManager1
extends
PairManager{
private
Lock
lock
=
new
ReentrantLock();
public
synchronized
void
increment(){
lock
.lock();
try
{
p
.incrementX();
p
.incrementY();
store(getPair());
}
finally
{
lock
.unlock();
}
}
}
class
ExplicitPairManager2
extends
PairManager{
private
Lock
lock
=
new
ReentrantLock();
public
void
increment() {
Pair temp;
lock
.lock();
try
{
p
.incrementX();
p
.incrementY();
temp = getPair();
}
finally
{
lock
.unlock();
}
store(temp);
}
}
public
class
ExpliciticalSection {
public
static
void
main(String[] args) {
PairManager
pman1 =
new
ExplicitPairManager1(),
pman2 =
new
ExplicitPairManager2();
CriticalSection. testApproaches(pman1, pman2);
}
}
/**
pm1:
Pair:
x:
21,
y:
21
checkCounter
=
868
pm2:
Pair:
x:
22,
y:
22
checkCounter
=
1450417
*/
总结:
1)临界区实现方法有两种,一种是用synchronized,一种是用Lock显式锁实现。
2)有临界区是为了让更多的其它线程能够访问资源。