javathread:Java Thread应该注意的问题来源: 发布时间:星期三, 2008年12月17日 浏览:123次 评论:0
Java线程编程非常简单但有时会看到些有关线程使用方法下面列出些应该注意问题
1.同步对象恒定性
All java objects are references.
对于局部变量和参数来说java里面, float, double, boolean等基本数据类型都在栈上这些基本类型是无法同步;java里面对象(根对象是Object)全都在堆里指向对象reference在栈上
java中同步对象实际上是对于reference所指“对象地址”进行同步
需要注意问题是千万不要对同步对象重新赋值举个例子
A implements Runnable{
Object lock = Object;
void run{
for(...){
synchronized(lock){
// do something
...
lock = Object;
}
}
}
run里面这段同步代码实际上是毫无意义每次lock都给重新分配了新对象reference每个线程都在新reference同步
大家可能觉得奇怪如何会举这么个例子个人所见过这样代码同步对象在其它里被重新赋了新值
这种问题很难查出来
所以般应该把同步对象声明为final.
final Object lock = Object;
使用Singleton Pattern 设计模式来获取同步对象也是种很好选择
2.如何放置共享数据
实现线程有两种思路方法种是继承Thread类种是实现Runnable接口
上面举例子采用实现Runnable接口思路方法本文推荐这种思路方法
首先把需要共享数据放在个实现Runnable接口类里面然后把这个类例子传给多个Thread构造思路方法这样新创建多个Thread都共同拥有个Runnable例子共享同份数据
如果采用继承Thread类思路方法就只好使用静态成员了如果共享数据比较多就需要大量静态成员令数据结构混乱难以扩展这种情况应该尽量避免
编写段多线程代码处理个稍微复杂点问题两种思路方法优劣试便知
3.同步粒度
线程同步粒度越小越好即线程同步代码块越小越好尽量避免用synchronized修饰符来声明思路方法尽量使用synchronized(anObject)方式如果不想引入新同步对象使用synchronized(this)方式而且synchronized代码块越小越好
4.线程的间通知
这里使用“通知”这个词而不用“通信”这个词是为了避免词义扩大化
线程的间通知通过Object对象wait和noty 或notyAll 思路方法实现
下面用个例子来介绍说明其工作原理:
假设有两个线程A和B共同拥有个同步对象lock
1.首先线程A通过synchronized(lock) 获得lock同步对象然后lock.wait放弃lock同步对象线程A停止运行进入等待队列
2.线程B通过synchronized(lock) 获得线程A放弃lock同步对象做完定处理然后 lock.noty 或者lock.notyAll 通知等待队列里面线程A
3.线程A从等待队列里面出来进入ready队列等待调度
4.线程B继续处理出了synchronized(lock)块的后放弃lock同步对象
5.线程A获得lock同步对象继续运行
例子代码如下:
public SharedResource implements Runnable{
Object lock = Object;
public void run{
// 获取当前线程名称
String threadName = Thread.currentThread.getName;
( “A”.equals(threadName)){
synchronized(lock){ //线程A通过synchronized(lock) 获得lock同步对象
try{
.out.prln(“ A gives up lock.”);
lock.wait; // lock.wait放弃lock同步对象
// 线程A停止运行进入等待队列
}catch(InterruptedException e){
}
// 线程A重新获得lock同步对象的后继续运行
.out.prln(“ A got lock again and continue to run.”);
} // end of synchronized(lock)
}
( “B”.equals(threadName)){
synchronized(lock){//线程B通过synchronized(lock) 获得线程A放弃lock同步对象
.out.prln(“B got lock.”);
lock.noty; //通知等待队列里面线程A进入ready队列等待调度
//线程B继续处理出了synchronized(lock)块的后放弃lock同步对象
.out.prln(“B gives up lock.”);
} // end of synchronized(lock)
boolean hasLock = Thread.holdsLock(lock); // 检查B是否拥有lock同步对象
.out.prln(“B has lock ? -- ” +hasLock); // false.
}
}
}
public TestMain{
public void {
Runnable resource = SharedResource;
Thread A = Thread(resource”A”);
A.start;
// 强迫主线程停止运行以便线程A开始运行
try {
Thread.sleep(500);
}catch(InterruptedException e){
}
Thread B = Thread(resource”B”);
B.start;
}
}
5.跨类同步对象
对于简单问题可以把访问共享资源同步代码都放在个类里面
但是对于复杂问题我们需要把问题分为几个部分来处理需要几个区别类来处理问题这时就需要在区别类中共享同步对象比如在生产者和消费者的间共享同步对象在读者和写者的间共享同步对象
如何在区别类中共享同步对象有几种思路方法实现
(1)前面讲过思路方法使用静态成员(或者使用Singleton Pattern.)
(2)用参数传递思路方法把同步对象传递给区别类
(3)利用串常量“原子性”
对于第 3种思路方法这里做下解释般来说代码中串常量经过编译的后都具有唯性即内存中不会存在两份相同串常量
(通常情况下CC语言编译的后也具有同样特性)
比如我们有如下代码
String A = “atom”;
String B = “atom”;
我们有理由认为A和B指向同个串常量即AB
注意声明串变量代码不符合上面规则
String C= String(“atom”);
String D = String(“atom”);
这里C和D声明是串变量声明所以C != D
有了上述认识我们就可以使用串常量作为同步对象
比如我们在区别类中使用synchronized(“myLock”), “myLock”.wait,“myLock”.noty, 这样代码就能够实现区别类的间线程同步
本文并不强烈推荐这种使用方法只是介绍说明有这样种思路方法存在
本文推荐第 2种思路方法(2)用参数传递思路方法把同步对象传递给区别类
0
相关文章读者评论发表评论 |