??xml version="1.0" encoding="utf-8" standalone="yes"?>
U程的同步是Z防止多个U程讉K一个数据对象时Q对数据造成的破坏?br />
例如Q两个线EThreadA、ThreadB都操作同一个对象Foo对象Qƈ修改Foo对象上的数据?br />
public class Foo {
private int x = 100;
public int getX() {
return x;
}
public int fix(int y) {
x = x - y;
return x;
}
}
public class MyRunnable implements Runnable {
private Foo foo = new Foo();
public static void main(String[] args) {
MyRunnable r = new MyRunnable();
Thread ta = new Thread(r, "Thread-A");
Thread tb = new Thread(r, "Thread-B");
ta.start();
tb.start();
}
public void run() {
for (int i = 0; i < 3; i++) {
this.fix(30);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " : 当前foo对象的x? " + foo.getX());
}
}
public int fix(int y) {
return foo.fix(y);
}
}
q行l果Q?br />
Thread-A : 当前foo对象的x? 40
Thread-B : 当前foo对象的x? 40
Thread-B : 当前foo对象的x? -20
Thread-A : 当前foo对象的x? -50
Thread-A : 当前foo对象的x? -80
Thread-B : 当前foo对象的x? -80
Process finished with exit code 0
从结果发玎ͼq样的输出值明显是不合理的。原因是两个U程不加控制的访问Foo对象q修改其数据所致?br />
如果要保持结果的合理性,只需要达C个目的,是对Foo的访问加以限Ӟ每次只能有一个线E在讉K。这样就能保证Foo对象中数据的合理性了?br />
在具体的Java代码中需要完成一下两个操作:
把竞争访问的资源cFoo变量x标识为privateQ?br />
同步哪些修改变量的代码,使用synchronized关键字同步方法或代码?br />
二、同步和锁定
1、锁的原?br />
Java中每个对象都有一个内|锁
当程序运行到非静态的synchronized同步Ҏ上时Q自动获得与正在执行代码cȝ当前实例Qthis实例Q有关的锁。获得一个对象的锁也UCؓ获取锁、锁定对象、在对象上锁定或在对象上同步?br />
当程序运行到synchronized同步Ҏ或代码块时才该对象锁才v作用?br />
一个对象只有一个锁。所以,如果一个线E获得该锁,没有其他线E可以获得锁Q直到第一个线E释放(或返回)锁。这也意味着M其他U程都不能进入该对象上的synchronizedҎ或代码块Q直到该锁被释放?br />
释放锁是指持锁线E退Zsynchronized同步Ҏ或代码块?br />
关于锁和同步Q有一下几个要点:
1Q、只能同步方法,而不能同步变量和c;
2Q、每个对象只有一个锁Q当提到同步Ӟ应该清楚在什么上同步Q也是_在哪个对象上同步Q?br />
3Q、不必同步类中所有的ҎQ类可以同时拥有同步和非同步Ҏ?br />
4Q、如果两个线E要执行一个类中的synchronizedҎQƈ且两个线E用相同的实例来调用方法,那么一ơ只能有一个线E能够执行方法,另一个需要等待,直到锁被释放。也是_如果一个线E在对象上获得一个锁Q就没有M其他U程可以q入Q该对象的)cM的Q何一个同步方法?br />
5Q、如果线E拥有同步和非同步方法,则非同步Ҏ可以被多个线E自p问而不受锁的限制?br />
6Q、线E睡眠时Q它所持的M锁都不会释放?
7Q、线E可以获得多个锁。比如,在一个对象的同步Ҏ里面调用另外一个对象的同步ҎQ则获取了两个对象的同步锁?br />
8Q、同步损宛_ƈ发性,应该可能羃同步范围。同步不但可以同步整个方法,q可以同步方法中一部分代码块?br />
9Q、在使用同步代码块时候,应该指定在哪个对象上同步Q也是说要获取哪个对象的锁。例如:
public int fix(int y) {
synchronized (this) {
x = x - y;
}
return x;
}
当然Q同步方法也可以改写为非同步ҎQ但功能完全一LQ例如:
public synchronized int getX() {
return x++;
}
?br />
public int getX() {
synchronized (this) {
return x;
}
}
效果是完全一L?br />
三、静态方法同?br />
要同步静态方法,需要一个用于整个类对象的锁Q这个对象是是q个c(XXX.class)?br />
例如Q?br />
public static synchronized int setName(String name){
Xxx.name = name;
}
{h?br />
public static int setName(String name){
synchronized(Xxx.class){
Xxx.name = name;
}
}
四、如果线E不能不能获得锁会怎么?br />
如果U程试图q入同步ҎQ而其锁已l被占用Q则U程在该对象上被d。实质上Q线E进入该对象的的一U池中,必须在哪里等待,直到光被释放,该线E再ơ变为可q行或运行ؓ止?br />
当考虑dӞ一定要注意哪个对象正被用于锁定Q?br />
1、调用同一个对象中非静态同步方法的U程彼此阻塞。如果是不同对象Q则每个U程有自q对象的锁Q线E间彼此互不q预?br />
2、调用同一个类中的静态同步方法的U程彼此阻塞,它们都是锁定在相同的Class对象上?br />
3、静态同步方法和非静态同步方法将永远不会彼此dQ因为静态方法锁定在Class对象上,非静态方法锁定在该类的对象上?br />
4、对于同步代码块Q要看清楚什么对象已l用于锁定(synchronized后面括号的内容)。在同一个对象上q行同步的线E将彼此dQ在不同对象上锁定的U程永q不会彼此阻塞?br />
五、何旉要同?br />
在多个线E同时访问互斥(可交换)数据Ӟ应该同步以保护数据,保两个U程不会同时修改更改它?br />
对于非静态字D中可更改的数据Q通常使用非静态方法访问?br />
对于静态字D中可更改的数据Q通常使用静态方法访问?br />
如果需要在非静态方法中使用静态字D,或者在静态字D中调用非静态方法,问题变得非常复杂。已l超出SJCP考试范围了?br />
六、线E安全类
当一个类已经很好的同步以保护它的数据Ӟq个cdUCؓ“U程安全?#8221;?br />
即是线E安全类Q也应该特别心Q因为操作的U程是间仍然不一定安全?br />
举个形象的例子,比如一个集合是U程安全的,有两个线E在操作同一个集合对象,当第一个线E查询集合非I后Q删除集合中所有元素的时候。第二个U程也来执行与第一个线E相同的操作Q也许在W一个线E查询后Q第二个U程也查询出集合非空Q但是当W一个执行清除后Q第二个再执行删除显然是不对的,因ؓ此时集合已经为空了?br />
看个代码Q?br />
public class NameList {
private List nameList = Collections.synchronizedList(new LinkedList());
public void add(String name) {
nameList.add(name);
}
public String removeFirst() {
if (nameList.size() > 0) {
return (String) nameList.remove(0);
} else {
return null;
}
}
}
public class Test {
public static void main(String[] args) {
final NameList nl = new NameList();
nl.add("aaa");
class NameDropper extends Thread{
public void run(){
String name = nl.removeFirst();
System.out.println(name);
}
}
Thread t1 = new NameDropper();
Thread t2 = new NameDropper();
t1.start();
t2.start();
}
}
虽然集合对象
private List nameList = Collections.synchronizedList(new LinkedList());
是同步的Q但是程序还不是U程安全的?br />
出现q种事g的原因是Q上例中一个线E操作列表过E中无法L另外一个线E对列表的其他操作?br />
解决上面问题的办法是Q在操作集合对象的NameList上面做一个同步。改写后的代码如下:
public class NameList {
private List nameList = Collections.synchronizedList(new LinkedList());
public synchronized void add(String name) {
nameList.add(name);
}
public synchronized String removeFirst() {
if (nameList.size() > 0) {
return (String) nameList.remove(0);
} else {
return null;
}
}
}
q样Q当一个线E访问其中一个同步方法时Q其他线E只有等待?br />
七、线E死?br />
死锁对JavaE序来说Q是很复杂的Q也很难发现问题。当两个U程被阻塞,每个U程在等待另一个线E时发生死锁?br />
q是看一个比较直观的死锁例子Q?br />
public class DeadlockRisk {
private static class Resource {
public int value;
}
private Resource resourceA = new Resource();
private Resource resourceB = new Resource();
public int read() {
synchronized (resourceA) {
synchronized (resourceB) {
return resourceB.value + resourceA.value;
}
}
}
public void write(int a, int b) {
synchronized (resourceB) {
synchronized (resourceA) {
resourceA.value = a;
resourceB.value = b;
}
}
}
}
假设read()Ҏ׃个线E启动,write()Ҏ由另外一个线E启动。读U程拥有resourceA锁,写线E将拥有resourceB锁,两者都坚持{待的话出现死锁?br />
实际上,上面q个例子发生死锁的概率很。因为在代码内的某个点,CPU必须从读U程切换到写U程Q所以,死锁基本上不能发生?br />
但是Q无Z码中发生死锁的概率有多小Q一旦发生死锁,E序死掉。有一些设计方法能帮助避免死锁Q包括始l按照预定义的顺序获取锁q一{略。已l超出SCJP的考试范围?br />
八、线E同步小l?br />
1、线E同步的目的是ؓ了保护多个线E反问一个资源时对资源的破坏?br />
2、线E同步方法是通过锁来实现Q每个对象都有切仅有一个锁Q这个锁与一个特定的对象兌Q线E一旦获取了对象锁,其他讉K该对象的U程无法再讉K该对象的其他非同步方法?br />
3、对于静态同步方法,锁是针对q个cȝQ锁对象是该cȝClass对象。静态和非静态方法的锁互不干预。一个线E获得锁Q当在一个同步方法中讉K另外对象上的同步ҎӞ会获取这两个对象锁?br />
4、对于同步,要时L醒在哪个对象上同步,q是关键?br />
5、编写线E安全的c,需要时L意对多个U程竞争讉K资源的逻辑和安全做出正的判断Q对“原子”操作做出分析Qƈ保证原子操作期间别的U程无法讉K竞争资源?br />
6、当多个U程{待一个对象锁Ӟ没有获取到锁的线E将发生d?br />
7、死锁是U程间相互等待锁锁造成的,在实际中发生的概率非常的。真让你写个死锁E序Q不一定好使,呵呵。但是,一旦程序发生死锁,E序死掉?/p>
//循环加蝲jar?br />
for /f %%i in ('dir /b %LIBPATH%\*.jar^|sort') do (
set CP=!CP!%LIBPATH%\%%i;
)
echo ===============================================================================
echo.
echo Engine Startup Environment
echo.
echo JAVA: %JAVA%
echo.
echo CONFIG: %CONFIG%
echo.
echo JAVA_OPTS: %OPTS%
echo.
echo CLASSPATH: %CP%
echo.
echo ===============================================================================
echo.
%JAVA% %OPTS% -cp %CP% %MAIN% //q行
# 以下为服务器、数据库信息
dbPort = localhost
databaseName = mydb
dbUserName = root
dbPassword = root
# 以下为数据库表信?/span>
dbTable = mytable
# 以下为服务器信息
ip = 192.168.0.9
在上面的文g中我们假设该文g名ؓQ?/span> test.properties 文g。其?/span> # 开始的一行ؓ注释信息Q在{号“ = ”左边的我们称之ؓ key Q等?#8220; = ”双的我们称之ؓ value 。(其实是我们常说的键 - 值对Q?/span> key 应该是我们程序中的变量。?/span> value 是我们根据实际情况配|的?/span>
二. JDK 中的 Properties c?/span> Properties cd在于?/span> Java.util 中,该类l承?/span> Hashtable Q它提供了几个主要的ҎQ?/span>
1Q?/span> getProperty ( String key) Q?/span> 用指定的键在此属性列表中搜烦属性。也是通过参数 key Q得?/span> key 所对应?/span> value?/span>
2Q?/span> load ( InputStream inStream) Q从输入中d属性列表(键和元素对)。通过Ҏ定的文gQ比如说上面?/span> test.properties 文gQ进行装载来获取该文件中的所有键 - 值对。以?/span> getProperty ( String key) 来搜索?/span>
3Q?/span> setProperty ( String key, String value) Q调?/span> Hashtable 的方?/span> put 。他通过调用基类?/span>putҎ来设|?/span> ?/span> - 值对?/span>
4Q?/span> store ( OutputStream out, String comments) Q?/span> 以适合使用 load Ҏ加蝲?/span> Properties 表中的格式,此 Properties 表中的属性列表(键和元素对)写入输出。与 load Ҏ相反Q该Ҏ键 - 值对写入到指定的文g中去?/span>
5Q?/span> clear () Q清除所有装载的 ?/span> - 值对。该Ҏ在基cM提供?/span>
有了以上几个Ҏ我们可以对 .properties 文gq行操作了!
三.代码实例
package configuration;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
/** *//**
* dproperties文g
* @author Qutr
*
*/
public class Configuration
...{
private Properties propertie;
private FileInputStream inputFile;
private FileOutputStream outputFile;
/** *//**
* 初始?/span>Configurationc?/span>
*/
public Configuration()
...{
propertie = new Properties();
}
/** *//**
* 初始?/span>Configurationc?/span>
* @param filePath 要读取的配置文g的\?/span>+名称
*/
public Configuration(String filePath)
...{
propertie = new Properties();
try ...{
inputFile = new FileInputStream(filePath);
propertie.load(inputFile);
inputFile.close();
} catch (FileNotFoundException ex) ...{
System.out.println("d属性文?/span>--->p|Q?/span>- 原因Q文件\径错误或者文件不存在");
ex.printStackTrace();
} catch (IOException ex) ...{
System.out.println("装蝲文g--->p|!");
ex.printStackTrace();
}
}//end ReadConfigInfo(...)
/** *//**
* 重蝲函数Q得?/span>key的?/span>
* @param key 取得其值的?/span>
* @return key的?/span>
*/
public String getValue(String key)
...{
if(propertie.containsKey(key))...{
String value = propertie.getProperty(key);//得到某一属性的?/span>
return value;
}
else
return "";
}//end getValue(...)
/** *//**
* 重蝲函数Q得?/span>key的?/span>
* @param fileName properties文g的\?/span>+文g?/span>
* @param key 取得其值的?/span>
* @return key的?/span>
*/
public String getValue(String fileName, String key)
...{
try ...{
String value = "";
inputFile = new FileInputStream(fileName);
propertie.load(inputFile);
inputFile.close();
if(propertie.containsKey(key))...{
value = propertie.getProperty(key);
return value;
}else
return value;
} catch (FileNotFoundException e) ...{
e.printStackTrace();
return "";
} catch (IOException e) ...{
e.printStackTrace();
return "";
} catch (Exception ex) ...{
ex.printStackTrace();
return "";
}
}//end getValue(...)
/** *//**
* 清除properties文g中所有的key和其?/span>
*/
public void clear()
...{
propertie.clear();
}//end clear();
/** *//**
* 改变或添加一?/span>key的|?/span>key存在?/span>properties文g中时?/span>key的Dvalue所代替Q?/span>
* ?/span>key不存在时Q该key的值是value
* @param key 要存入的?/span>
* @param value 要存入的?/span>
*/
public void setValue(String key, String value)
...{
propertie.setProperty(key, value);
}//end setValue(...)
/** *//**
* 更改后的文件数据存入指定的文g中,该文件可以事先不存在?/span>
* @param fileName 文g路径+文g名称
* @param description 对该文g的描q?/span>
*/
public void saveFile(String fileName, String description)
...{
try ...{
outputFile = new FileOutputStream(fileName);
propertie.store(outputFile, description);
outputFile.close();
} catch (FileNotFoundException e) ...{
e.printStackTrace();
} catch (IOException ioe)...{
ioe.printStackTrace();
}
}//end saveFile(...)
public static void main(String[] args)
...{
Configuration rc = new Configuration("."config"test.properties");//相对路径
String ip = rc.getValue("ipp");//以下dproperties文g的?/span>
String host = rc.getValue("host");
String tab = rc.getValue("tab");
System.out.println("ip = " + ip + "ip-test leng = " + "ip-test".length());//以下输出propertiesd的?/span>
System.out.println("ip's length = " + ip.length());
System.out.println("host = " + host);
System.out.println("tab = " + tab);
Configuration cf = new Configuration();
String ipp = cf.getValue("."config"test.properties", "ip");
System.out.println("ipp = " + ipp);
// cf.clear();
cf.setValue("min", "999");
cf.setValue("max", "1000");
cf.saveFile("."config"save.perperties", "test");
// Configuration saveCf = new Configuration();
// saveCf.setValue("min", "10");
// saveCf.setValue("max", "1000");
// saveCf.saveFile("."config"save.perperties");
}//end main()
}//end class ReadConfigInfo
四.结 通过上面的例子不隄出,?/span>Java中操作配|文件是非常单的。在一个需要用到大量配|信息的模块或系l里Q我们有必要装一个专门的cL׃用。通过最后的main函数调用Q相信大家可以看cȝ用法。不x出希望大家多多指炏V?/span>
Java properties文g的操?/span>
----------------------------------------------------
java中的properties文g是一U配|文Ӟ主要用于表达配置信息Q文件类型ؓ*.propertiesQ格式ؓ文本文gQ文件的内容是格式是"?/span>=?/span>"的格式,?/span>properties文g中,可以?/span>"#"来作注释Q?/span>properties文g?/span>Java~程中用到的地方很多Q操作很方便。下面是一个操?/span>java properties文g的例子,l出了操作方法和properties文g。从中可以看到如何读?/span>properties文gQƈ应用d出来的|是学习操?/span>properties文g的好例子?/span>
一?/span>properties文g
IcisReport.properties
------------------------------------------------------
#################################
# 工商报表应用IcisReport的配|文?/span> #
# 作者:h?/span> #
# 日期Q?/span>2006q?/span>11?/span>21?/span> #
#################################
#
# 说明:业务pȝTopIcis和报表系l?/span>IcisReport是分ȝ
# 可分开部vC同的服务器上Q也可以部v到同一个服?/span>
# 器上;IcisReprot作ؓ独立?/span>web应用E序可以使用M
# ?/span>Servlet容器或?/span>J2EE服务器部|ƈ单独q行Q也可以
# 通过业务pȝ的接口调用作Z务系l的一个库来应?/span>.
#
# IcisReport?/span>ip
IcisReport.server.ip=192.168.3.143
# IcisReport的端?/span>
IcisReport.server.port=8080
# IcisReport的上下文路径
IcisReport.contextPath=/IcisReport
------------------------------------------------------
二、操?/span>properties文g?/span>javaҎ
下面是一个操?/span>properties文g的方?/span>
/** *//**
* @return 获取IcisReport报表应用?/span>URL
*/
private String getIcisReportURL() ...{
String icisReportURL = ""; // IcisReport报表应用?/span>URL
String icisReportServerIP = ""; // IcisReport服务器的IP
String icisReportServerPort = ""; // IcisReport服务器的服务端口
String icisReportContextPath = ""; // IcisReport应用?/span>ContextPath
Properties prop = new Properties();
InputStream in;
try ...{
in = getClass().getResourceAsStream("/IcisReport.properties");
prop.load(in);
Set keyValue = prop.keySet();
for (Iterator it = keyValue.iterator(); it.hasNext();) ...{
String key = (String) it.next();
if (key.equals("IcisReport.server.ip")) ...{
icisReportServerIP = (String) prop.get(key);
} else if (key.equals("IcisReport.server.port")) ...{
icisReportServerPort = (String) prop.get(key);
} else if (key.equals("IcisReport.contextPath")) ...{
icisReportContextPath = (String) prop.get(key);
}
}
} catch (Exception e) ...{
log.error("IOd出错Q找不到IcisReport.properties!");
}
if (icisReportServerIP.trim().equals("")) ...{
log
.error("h查配|文?/span>IcisReport.properties中的IcisReport.server.ip的值是否正?/span>!");
}
if (icisReportServerPort.trim().equals("")) ...{
log
.error("h查配|文?/span>IcisReport.properties中的IcisReport.server.port的值是否正?/span>!");
}
if (icisReportServerPort.trim().equals("")) ...{
log
.error("h查配|文?/span>IcisReport.properties中的IcisReport.server.port的值是否正?/span>!");
}
icisReportURL = "http://" + icisReportServerIP.trim() + ":"
+ icisReportServerPort.trim() + icisReportContextPath.trim();
log.info("获取?/span>icisReportURL=" + icisReportURL);
return icisReportURL;
}
ȝQ?/span>java?/span>properties文g需要放?/span>classpath下面Q这L序才能读取到Q有?/span>classpath实际上就?/span>javacL者库的存放\径,?/span>java工程中,properties攑ֈclass文g一块。在web应用中,最单的Ҏ是放?/span>web应用?/span>WEB-INF"classes目录下即可,也可以放在其他文件夹下面Q这时候需要在讄classpath环境变量的时候,这个文件夹路径加到classpath变量中,q样也也可以d到。在此,你需要对classpath有个深刻理解Q?/span>classpathl非pȝ中刻意设定的那个pȝ环境变量Q?/span>WEB-INF"classes其实也是Q?/span>java工程?/span>class文g目录也是?/span>
该文章{载自|络大本营:http://www.xrss.cn/Dev/JAVA/200761514149.Html