分享
 
 
 

Java如何共享资源

王朝java/jsp·作者佚名  2008-05-19
窄屏简体版  字體: |||超大  

对一种特殊的资源――对象中的内存――Java提供了内建的机制来防止它们的冲突。由于我们通常将数据元素设为从属于private(私有)类,然后只通过方法访问那些内存,所以只需将一个特定的方法设为synchronized(同步的),便可有效地防止冲突。在任何时刻,只可有一个线程调用特定对象的一个synchronized方法(尽管那个线程可以调用多个对象的同步方法)。下面列出简单的synchronized方法:

synchronized void f() { /* ... */ }

synchronized void g() { /* ... */ }

每个对象都包含了一把锁(也叫作“监视器”),它自动成为对象的一部分(不必为此写任何特殊的代码)。调用任何synchronized方法时,对象就会被锁定,不可再调用那个对象的其他任何synchronized方法,除非第一个方法完成了自己的工作,并解除锁定。在上面的例子中,如果为一个对象调用f(),便不能再为同样的对象调用g(),除非f()完成并解除锁定。因此,一个特定对象的所有synchronized方法都共享着一把锁,而且这把锁能防止多个方法对通用内存同时进行写操作(比如同时有多个线程)。

每个类也有自己的一把锁(作为类的Class对象的一部分),所以synchronized static方法可在一个类的范围内被相互间锁定起来,防止与static数据的接触。

注意如果想保护其他某些资源不被多个线程同时访问,可以强制通过synchronized方访问那些资源。

1. 计数器的同步

装备了这个新关键字后,我们能够采取的方案就更灵活了:可以只为TwoCounter中的方法简单地使用synchronized关键字。下面这个例子是对前例的改版,其中加入了新的关键字:

//: Sharing2.java

// Using the synchronized keyword to prevent

// multiple access to a particular resource.

import java.awt.*;

import java.awt.event.*;

import java.applet.*;

class TwoCounter2 extends Thread {

private boolean started = false;

private TextField

t1 = new TextField(5),

t2 = new TextField(5);

private Label l =

new Label("count1 == count2");

private int count1 = 0, count2 = 0;

public TwoCounter2(Container c) {

Panel p = new Panel();

p.add(t1);

p.add(t2);

p.add(l);

c.add(p);

}

public void start() {

if(!started) {

started = true;

super.start();

}

}

public synchronized void run() {

while (true) {

t1.setText(Integer.toString(count1++));

t2.setText(Integer.toString(count2++));

try {

sleep(500);

} catch (InterruptedException e){}

}

}

public synchronized void synchTest() {

Sharing2.incrementAccess();

if(count1 != count2)

l.setText("Unsynched");

}

}

class Watcher2 extends Thread {

private Sharing2 p;

public Watcher2(Sharing2 p) {

this.p = p;

start();

}

public void run() {

while(true) {

for(int i = 0; i

p.s[i].synchTest();

try {

sleep(500);

} catch (InterruptedException e){}

}

}

}

public class Sharing2 extends Applet {

TwoCounter2[] s;

private static int accessCount = 0;

private static TextField aCount =

new TextField("0", 10);

public static void incrementAccess() {

accessCount++;

aCount.setText(Integer.toString(accessCount));

}

private Button

start = new Button("Start"),

observer = new Button("Observe");

private boolean isApplet = true;

private int numCounters = 0;

private int numObservers = 0;

public void init() {

if(isApplet) {

numCounters =

Integer.parseInt(getParameter("size"));

numObservers =

Integer.parseInt(

getParameter("observers"));

}

s = new TwoCounter2[numCounters];

for(int i = 0; i

s[i] = new TwoCounter2(this);

Panel p = new Panel();

start.addActionListener(new StartL());

p.add(start);

observer.addActionListener(new ObserverL());

p.add(observer);

p.add(new Label("Access Count"));

p.add(aCount);

add(p);

}

class StartL implements ActionListener {

public void actionPerformed(ActionEvent e) {

for(int i = 0; i

s[i].start();

}

}

class ObserverL implements ActionListener {

public void actionPerformed(ActionEvent e) {

for(int i = 0; i

new Watcher2(Sharing2.this);

}

}

public static void main(String[] args) {

Sharing2 applet = new Sharing2();

// This isn't an applet, so set the flag and

// produce the parameter values from args:

applet.isApplet = false;

applet.numCounters =

(args.length == 0 ? 5 :

Integer.parseInt(args[0]));

applet.numObservers =

(args.length

Integer.parseInt(args[1]));

Frame aFrame = new Frame("Sharing2");

aFrame.addWindowListener(

new WindowAdapter() {

public void windowClosing(WindowEvent e){

System.exit(0);

}

});

aFrame.add(applet, BorderLayout.CENTER);

aFrame.setSize(350, applet.numCounters *100);

applet.init();

applet.start();

aFrame.setVisible(true);

}

}

我们注意到无论run()还是synchTest()都是“同步的”。如果只同步其中的一个方法,那么另一个就可以自由忽视对象的锁定,并可无碍地调用。所以必须记住一个重要的规则:对于访问某个关键共享资源的所有方法,都必须把它们设为synchronized,否则就不能正常地工作。

现在又遇到了一个新问题。Watcher2永远都不能看到正在进行的事情,因为整个run()方法已设为“同步”。而且由于肯定要为每个对象运行run(),所以锁永远不能打开,而synchTest()永远不会得到调用。之所以能看到这一结果,是因为accessCount根本没有变化。

为解决这个问题,我们能采取的一个办法是只将run()中的一部分代码隔离出来。想用这个办法隔离出来的那部分代码叫作“关键区域”,而且要用不同的方式来使用synchronized关键字,以设置一个关键区域。Java通过“同步块”提供对关键区域的支持;这一次,我们用synchronized关键字指出对象的锁用于对其中封闭的代码进行同步。如下所示:

synchronized(syncObject) {

// This code can be accessed by only

// one thread at a time, assuming all

// threads respect syncObject's lock

}

在能进入同步块之前,必须在synchObject上取得锁。如果已有其他线程取得了这把锁,块便不能进入,必须等候那把锁被释放。

可从整个run()中删除synchronized关键字,换成用一个同步块包围两个关键行,从而完成对Sharing2例子的修改。但什么对象应作为锁来使用呢?那个对象已由synchTest()标记出来了――也就是当前对象(this)!所以修改过的run()方法象下面这个样子:

public void run() {

while (true) {

synchronized(this) {

t1.setText(Integer.toString(count1++));

t2.setText(Integer.toString(count2++));

}

try {

sleep(500);

} catch (InterruptedException e){}

}

}

这是必须对Sharing2.java作出的唯一修改,我们会看到尽管两个计数器永远不会脱离同步(取决于允许Watcher什么时候检查它们),但在run()执行期间,仍然向Watcher提供了足够的访问权限。

当然,所有同步都取决于程序员是否勤奋:要访问共享资源的每一部分代码都必须封装到一个适当的同步块里。

2. 同步的效率

由于要为同样的数据编写两个方法,所以无论如何都不会给人留下效率很高的印象。看来似乎更好的一种做法是将所有方法都设为自动同步,并完全消除synchronized关键字(当然,含有synchronized run()的例子显示出这样做是很不通的)。但它也揭示出获取一把锁并非一种“廉价”方案――为一次方法调用付出的代价(进入和退出方法,

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有