关于线程数量与线程池数量的关系

denverbenjamin2000 2008-07-09 08:34:00
大家好,小弟写一个多线程程序,使用了线程池,结果遇到问题,就是
我这个程序中线程池中指定的线程数目如果少于我创建的线程数的话,程序就会死锁,大家说这个正常么?
不正常的话,是不是说我的程序中有线程不安全的因素啊,如何改进?

package thread1.ParaNull;

import java.util.Stack;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class GlobalControl {
public static int NumOfLp = 100;//at least three node r needed ,or there must be wrong .
public static double limit = 10000.0;
public static double simulationEndTime = 500.0;
public static long seed = 1233;
/*
* make it wellknown
*/

//public static LogicalProcess LP[] = new LogicalProcess[NumOfLp];
private static int GVT = 0;
private static final int POOL_SIZE = NumOfLp;//seems at least no less than the number of the NumOfLp

public void init() {
ExecutorService exe = null;//
// for performance ,we use the pool for execution
exe = Executors.newFixedThreadPool(POOL_SIZE);// �����̳߳�

int i = 0;
Event temp1;

// 1 creation
/* LogicalProcess tempLP=null;
for (int j = 0; j < GlobalControl.NumOfLp; j++) {
tempLP= LP.getInstance(j);
tempLP= new LogicalProcess(j, this, seed);

}*/
// 2 initialization of the logicalprocess
while (i < NumOfLp) {

// structure the influence set ,both side should know eachother!! the couples

LP.getInstance(i).setInfluenced((i +1 + NumOfLp)% NumOfLp);
LP.getInstance(i).setInfluenced((i - 1 + NumOfLp)% NumOfLp);
// set the rest to null ,by construction default
//
i++;
}
// 3 first push
for (int j = 0; j < GlobalControl.NumOfLp; j++) {

if (LP.getInstance(j).isFirstpush()) {

temp1 = LP.getInstance(j).remoteEventgeneration();
LP.getInstance(j).toScheduleEvent(temp1);
/*System.out.println("issue a event from the " + j
+ "LP to the " + temp1.to + "with timestamp"
+ temp1.timeStamp);*/

}
}

// start the thread execution
for (int j = 0; j < NumOfLp; j++) {
exe.execute(LP.getInstance(j));
System.out.println("we r creating the " + j + "LP");
}
}

public static void main(String[] args) {

GlobalControl global = new GlobalControl();
global.init();


}
...全文
1283 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
xyaoyuan 2008-12-08
  • 打赏
  • 举报
回复
线程池可以有效利用系统资源,提高系统效率,但是一旦出现死锁等问题,就很麻烦。所以线程间彼此独立对提高程序的稳定性有重要的意思。
sagezk 2008-07-13
  • 打赏
  • 举报
回复
放到池中去执行的线程彼此之间最好要相对独立,要是纠缠不清系统能稳定才怪。
  • 打赏
  • 举报
回复
up



............
iwillrockyou 2008-07-09
  • 打赏
  • 举报
回复
帮顶~
denverbenjamin2000 2008-07-09
  • 打赏
  • 举报
回复
看到一篇如下文章,大家说他的解释对不对呢?
Java 理论与实践: 线程池与工作队列
线程池有助于实现最佳资源利用率


文档选项
将此页作为电子邮件发送


Brian Goetz (brian@quiotix.com), 首席顾问, Quiotix Corp



使用线程池的风险

虽然线程池是构建多线程应用程序的强大机制,但使用它并不是没有风险的。用线程池构建的应用程序容易遭受任何其它多线程应用程序容易遭受的所有并发风险,诸如同步错误和死锁,它还容易遭受特定于线程池的少数其它风险,诸如与池有关的死锁、资源不足和线程泄漏。

死锁

任何多线程应用程序都有死锁风险。当一组进程或线程中的每一个都在等待一个只有该组中另一个进程才能引起的事件时,我们就说这组进程或线程 死锁了。死锁的最简单情形是:线程 A 持有对象 X 的独占锁,并且在等待对象 Y 的锁,而线程 B 持有对象 Y 的独占锁,却在等待对象 X 的锁。除非有某种方法来打破对锁的等待(Java 锁定不支持这种方法),否则死锁的线程将永远等下去。

虽然任何多线程程序中都有死锁的风险,但线程池却引入了另一种死锁可能,在那种情况下,所有池线程都在执行已阻塞的等待队列中另一任务的执行结果的任务,但这一任务却因为没有未被占用的线程而不能运行。当线程池被用来实现涉及许多交互对象的模拟,被模拟的对象可以相互发送查询,这些查询接下来作为排队的任务执行,查询对象又同步等待着响应时,会发生这种情况。

资源不足

线程池的一个优点在于:相对于其它替代调度机制(有些我们已经讨论过)而言,它们通常执行得很好。但只有恰当地调整了线程池大小时才是这样的。线程消耗包括内存和其它系统资源在内的大量资源。除了 Thread 对象所需的内存之外,每个线程都需要两个可能很大的执行调用堆栈。除此以外,JVM 可能会为每个 Java 线程创建一个本机线程,这些本机线程将消耗额外的系统资源。最后,虽然线程之间切换的调度开销很小,但如果有很多线程,环境切换也可能严重地影响程序的性能。

如果线程池太大,那么被那些线程消耗的资源可能严重地影响系统性能。在线程之间进行切换将会浪费时间,而且使用超出比您实际需要的线程可能会引起资源匮乏问题,因为池线程正在消耗一些资源,而这些资源可能会被其它任务更有效地利用。除了线程自身所使用的资源以外,服务请求时所做的工作可能需要其它资源,例如 JDBC 连接、套接字或文件。这些也都是有限资源,有太多的并发请求也可能引起失效,例如不能分配 JDBC 连接。

并发错误

线程池和其它排队机制依靠使用 wait() 和 notify() 方法,这两个方法都难于使用。如果编码不正确,那么可能丢失通知,导致线程保持空闲状态,尽管队列中有工作要处理。使用这些方法时,必须格外小心;即便是专家也可能在它们上面出错。而最好使用现有的、已经知道能工作的实现,例如在下面的 无须编写您自己的池中讨论的 util.concurrent 包。

线程泄漏

各种类型的线程池中一个严重的风险是线程泄漏,当从池中除去一个线程以执行一项任务,而在任务完成后该线程却没有返回池时,会发生这种情况。发生线程泄漏的一种情形出现在任务抛出一个 RuntimeException 或一个 Error 时。如果池类没有捕捉到它们,那么线程只会退出而线程池的大小将会永久减少一个。当这种情况发生的次数足够多时,线程池最终就为空,而且系统将停止,因为没有可用的线程来处理任务。

有些任务可能会永远等待某些资源或来自用户的输入,而这些资源又不能保证变得可用,用户可能也已经回家了,诸如此类的任务会永久停止,而这些停止的任务也会引起和线程泄漏同样的问题。如果某个线程被这样一个任务永久地消耗着,那么它实际上就被从池除去了。对于这样的任务,应该要么只给予它们自己的线程,要么只让它们等待有限的时间。

请求过载

仅仅是请求就压垮了服务器,这种情况是可能的。在这种情形下,我们可能不想将每个到来的请求都排队到我们的工作队列,因为排在队列中等待执行的任务可能会消耗太多的系统资源并引起资源缺乏。在这种情形下决定如何做取决于您自己;在某些情况下,您可以简单地抛弃请求,依靠更高级别的协议稍后重试请求,您也可以用一个指出服务器暂时很忙的响应来拒绝请求。






回页首




有效使用线程池的准则

只要您遵循几条简单的准则,线程池可以成为构建服务器应用程序的极其有效的方法:

不要对那些同步等待其它任务结果的任务排队。这可能会导致上面所描述的那种形式的死锁,在那种死锁中,所有线程都被一些任务所占用,这些任务依次等待排队任务的结果,而这些任务又无法执行,因为所有的线程都很忙。
在为时间可能很长的操作使用合用的线程时要小心。如果程序必须等待诸如 I/O 完成这样的某个资源,那么请指定最长的等待时间,以及随后是失效还是将任务重新排队以便稍后执行。这样做保证了:通过将某个线程释放给某个可能成功完成的任务,从而将最终取得 某些进展。

理解任务。要有效地调整线程池大小,您需要理解正在排队的任务以及它们正在做什么。它们是 CPU 限制的(CPU-bound)吗?它们是 I/O 限制的(I/O-bound)吗?您的答案将影响您如何调整应用程序。如果您有不同的任务类,这些类有着截然不同的特征,那么为不同任务类设置多个工作队列可能会有意义,这样可以相应地调整每个池。
denverbenjamin2000 2008-07-09
  • 打赏
  • 举报
回复


package thread1.ParaNull;

import java.util.Stack;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class GlobalControl {
public static int NumOfLp = 100;
public static double limit = 10000.0;
public static double simulationEndTime = 500.0;
public static long seed = 1233;
private static int GVT = 0;
private static final int POOL_SIZE = 99;
//if POOL_SIZE is smaller than NumOfLp which is the number of thread ,it will deadloack ,why?

public void init() {
ExecutorService exe = null;
exe = Executors.newFixedThreadPool(POOL_SIZE);

int i = 0;
Event temp1;


while (i < NumOfLp) {

LP.getInstance(i).setInfluenced((i +1 + NumOfLp)% NumOfLp);
LP.getInstance(i).setInfluenced((i - 1 + NumOfLp)% NumOfLp);

i++;
}

for (int j = 0; j < GlobalControl.NumOfLp; j++) {

if (LP.getInstance(j).isFirstpush()) {

temp1 = LP.getInstance(j).remoteEventgeneration();
LP.getInstance(j).toScheduleEvent(temp1);
}
}


for (int j = 0; j < NumOfLp; j++) {
exe.execute(LP.getInstance(j));
System.out.println("we r creating the " + j + "LP");
}
}

public static void main(String[] args) {

GlobalControl global = new GlobalControl();
global.init();


}
denverbenjamin2000 2008-07-09
  • 打赏
  • 举报
回复
Q在程序设计中我做了一个公共的java开发平台,在此平台上可以运行多个应用,开发平台触发一个事件送到指定的应用, 应用在向java平台发送请求后,应用使用wait 同步等待开发平台的异步消息,在异步消息中使用notify来通知前面的wait等待,这种模式下,java开发平台线程池如果配置了100个,那末在并发数很少的时候,不会有问题,但是当并发数超过100的时候,都被应用中的wait给吊住了,java开发平台给应用回送异步响应消息的时候,就找不到空闲的线程了,这时整个应用被吊死了。
我考虑目前的修改办法是:应用中不使用线程同步的方法,采用异步的方法。
大家做这样的设计的时候的解决办法由那些呢?
欢迎大家讨论。

A
首先我觉得应该确定你的是你的公共平台有没有必要对线程进行同步?
也就是说如果并发情况下,不同步会不会对共享信息产生影响

如果必须同步,那么你可以再考虑同步的对象,不要把整个平台对象都同步了,那样效率非常低,就出现了你的问题

A
不太明白你的问题,瞎说两句:
1。能不能把线程池线程数做成不设上限的?
2。可以考虑用先入先出的队列存放应用的请求
3。100个线程都在wait是你预料之中的事吗?如果不是,如楼上那位老兄所说,可能同步方面的设计有问题。如果是,那么你的平台处理请求都是很耗时的,应该考虑以上两点办法。

A
to:jFresH_MaN(TM)
不是在公共的平台上进行同步的,我在详细说明一下java公共开发平台和应用的关系。
在java公共开发平台上可以承载多个应用,当java公共平台上受到一个事件后,找到一个空闲
的线程 发送到指定的应用, 然后应用 在发起一个请求 比如计费 ,应用调用wait方法,等待java
开发平台的计费异步消息答复。由此造成了线程的吊死。
是在同时大会话量的条件下出现的。

现在已经认识到了,这种做法的危险性,现在想知道大家一般采用哪种处理方法。
我考虑的方法如下:
1。应用中不采用wait notify方法,而采用状态机的方法。
2。当java平台上报事件给应用的时候,应用在开一个线程,保证不吊死java平台上报的线程。

欢迎大家讨论。
A
一个前提:线程池中找不到空闲的线程可以使用,都是在同时进行很多会话的条件下发生的, 比如每秒1000个事件上报。

1。设置了上限。
2。这个很有意思,我要考虑一下。
3。都需要wait 比如都要计费。

q. 对于线程间的消息通知,如果不是用wait notify方式,使用异步方式应用的改动会很大。
相当于重新写。

欢迎大家讨论。

A
感觉像线程池之类的,应用事件报告(线程是平台分配的)/ 请求 /响应(线程是平台触发的)的模式,java中的wait notify
没有任何生命力。
A
最近一直在看.net的异步回调函数,其实也就是多线程的处理
我觉得你这个问题不能使用线程池,而且上面说的类似数据库连接池其实也是和你现在的做法几乎一样。你的问题就是并发大了,而同步的对象来不及被其他线程使用。
我还是坚持上面的观点,你同步的对象是有问题的。

上面你具体解释了你的系统,那么我想问,比如说你的计费模块,由于同步了他而产生了问题。那么它是不是被请求的次数很多,而且必须被同步?
我觉得从这里着手就可以解决问题。
首先还是确定这个计费模块是不是必须同步?并发下对计算结果是不是有影响。如果必须同步,那么到底是具体到对象里面的哪一个小对象必须同步?这个问题搞清楚了么?

其实我的意思可能表达的不是很清楚,我举个小例子,你就知道我在说什么了!
HashMap hm=new HashMap();
..
synchornized(hm) {//第一种方法
wait();

}

String key=xxx;//第二种方法
String value=(String)hm.get(key);
synchornized(value) {
wait();
...
}
这样我的意思你明白了么?

A
首先同步的不是平台的对象,但是做同步的时候是在平台发起的线程 中 做的,所以调用wait()方法的时候 会把平台发起的线程 吊死。 同步的对象是是应用自己创建的,而不是平台的。
比如计费的时候,同步了用户的对象。

同步产生的问题是:在应用中用于调用wait 吊着了平台发给应用的线程,所以并发的情况下,
平台会在线程池中找不到空闲的线程,所以对于在平台计费的结果也返还不给应用了。

我是同意jFresH_MaN(TM) ( ) 信誉:164 的说法,这个根本不能使用同步(线程池环境下)。
laorer 2008-07-09
  • 打赏
  • 举报
回复
呵呵,
先改下代码吧...
老紫竹 2008-07-09
  • 打赏
  • 举报
回复
代码一团糟,你去掉那些没用的注释吧!

62,614

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧