日期:2011-03-22 16:16:00 来源:本站整理
<b>Java若何同享资源</b>[Java编程]
本文“<b>Java若何同享资源</b>[Java编程]”是由七道奇为您精心收集,来源于网络转载,文章版权归文章作者所有,本站不对其观点以及内容做任何评价,请读者自行判断,以下是其具体内容:
对一种特别的资源——对象中的内存——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.length; 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.length; 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.length; i++) s[i].start(); } } class ObserverL implements ActionListener { public void actionPerformed(ActionEvent e) { for(int i = 0; i < numObservers; 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 < 2 ? 5 : 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()办法象下面这个模样:
这是必须对Sharing2.java作出的唯一改正,我们会看到固然两个计数器永久不会脱离同步(取决于答应Watcher什么时刻查抄它们),但在run()履行期间,仍旧向Watcher供应了充足的拜候权限.public void run() { while (true) { synchronized(this) { t1.setText(Integer.toString(count1++)); t2.setText(Integer.toString(count2++)); } try { sleep(500); } catch (InterruptedException e){} } }
当然,全部同步都取决于程序员能否勤奋:要拜候同享资源的每一部份代码都必须封装到一个得当的同步块里.
2. 同步的效率
由于要为一样的数据编写两个办法,所以无论若何都不会给人留下效率很高的印象.看来仿佛更好的一种做法是将全部办法都设为自动同步,并完好消除synchronized关键字(当然,含有synchronized run()的例子显示出这样做是很不通的).但它也揭暴露获得一把锁并非一种“便宜”筹划——为一次办法调用付出的代价(进入和退出办法,不履行办法主体)至少要累加到四倍,并且按照我们的具体现筹划,这一代价还有大概变得更高.所以假定已知一个办法不会造成冲突,最明智的做法就是撤消此中的synchronized关键字.
以上是“<b>Java若何同享资源</b>[Java编程]”的内容,如果你对以上该文章内容感兴趣,你可以看看七道奇为您推荐以下文章:
本文地址: | 与您的QQ/BBS好友分享! |
评论内容只代表网友观点,与本站立场无关!
评论摘要(共 0 条,得分 0 分,平均 0 分)
查看完整评论