[英]Multi-threading — a faster way?
我在某个字段上有一个带有getter getInt()
和setter setInt()
,比如说field
Integer Int;
SomeClass
表示类的对象。
此处的setInt()
已同步getInt()
不同步。
我正在从多个线程中更新Int
的值。 每个线程都获取值Int
,并进行适当设置。 线程不会以任何方式共享任何其他资源。
在每个线程中执行的代码如下。
public void update(SomeClass c) {
while (<condition-1>) // the conditions here and the calculation of
// k below dont have anything to do
// with the members of c
if (<condition-2>) {
// calculate k here
synchronized (c) {
c.setInt(c.getInt()+k);
// System.out.println("in "+this.toString());
}
}
}
run()
方法只是对通过传递给它的参数从构造函数内部更新的成员调用上述方法:
public void run() { update(c); }
当我按较大的顺序运行时,线程之间的交错并不多-我看到一个线程执行了很长时间,而其他线程之间没有运行。
必须有更好的方法来做到这一点。
我无法更改SomeClass
或调用线程的类的内部。
如何更好地做到这一点?
TIA。
// ====================================
编辑:
我不是在操纵线程的执行顺序之后。 它们都具有相同的优先级。 它`只是我在结果看到的是这表明线程不共享的执行时间他们evenly--之一,一旦接管,执行上。 但是,我看不到为什么这段代码应该这样做。
JDK为多线程int访问AtomicInteger提供了一个很好的解决方案:
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicInteger.html
我在结果中看到的只是暗示线程没有平均共享执行时间
好吧,如果您追求效率,这就是您所不想要的。 取消执行某个线程并调度另一个线程通常非常昂贵。 因此,执行one of them, once takes over, executing on
实际上是有利的。 当然,如果这样做过分,您会发现吞吐量更高,但响应时间更长。 理论上。 实际上,JVM线程调度已针对几乎所有目的进行了很好的调整,并且您不想在几乎所有情况下都尝试对其进行更改。 根据经验,如果您对以毫秒为单位的响应时间感兴趣,那么您可能想远离它。
tl; dr:这并不是效率低下,您可能希望保持原样。
编辑:
话虽如此,使用AtomicInteger
可能有助于提高性能,并且在我看来,与使用锁( synchronized
关键字)相比,它更不容易出错。 为了获得可衡量的收益,您需要非常努力地达到该变量。
正如Enno Shioji指出的那样 ,在某些情况下,让一个线程继续执行可能是执行代码的最有效方法。
这取决于线程同步相对于代码的其他工作所造成的成本(我们不知道)。 如果您有如下循环:
while (<condition-1>)
if (<condition-2>) {
// calculate k here
synchronized (c) {
c.setInt(c.getInt()+k);
}
}
并且条件1和条件2的测试以及k
的计算与同步成本相比相当便宜,热点优化器可能会决定通过将代码转换为以下代码来减少开销:
synchronized (c) {
while (<condition-1>)
if (<condition-2>) {
// calculate k here
c.setInt(c.getInt()+k);
}
}
(或者通过执行循环展开并将synchronized
块扩展到多个迭代中来构建一个更为复杂的结构)。 最重要的是,优化的代码可能会阻塞其他线程更长的时间,但是让拥有锁的线程更快地完成,从而总体上提高了执行速度。
这并不意味着单线程执行是处理问题的最快方法。 这也不意味着在这里使用AtomicInteger
是解决问题的最佳选择。 这会产生较高的CPU负载,并可能产生较小的加速度,但并不能解决您的真正错误:
完全没有必要在循环中以较高的频率更新c
。 毕竟,您的线程不依赖于及时查看c
更新。 甚至看起来他们根本没有使用它。 因此正确的解决方法是将更新移出循环:
int kTotal=0;
while (<condition-1>)
if (<condition-2>) {
// calculate k here
kTotal += k;
}
synchronized (c) {
c.setInt(c.getInt()+kTotal);
}
现在,所有线程都可以并行运行(假设您未在此处发布的代码不包含线程间依赖性),并且synchronization
成本已降至最低。 您仍然可以将其更改为AtomicInteger
,但这不再那么重要了。
回答这个
我看到一个线程执行了很长时间,而其他线程之间没有运行。 必须有更好的方法来做到这一点。
您无法控制如何执行线程。 JVM为您执行此操作,并且不希望您干预其工作。
仍然可以将yield
视为您的选择,但这也不能确保不会再次选择同一线程。
java.lang.Thread.yield()方法使当前正在执行的线程对象暂时暂停并允许其他线程执行。
我发现使用wait()和notify()比yield更好。 查看此示例(从书中看到)-
class Q {
int n;
boolean valueSet = false;
synchronized int get() {
if(!valueSet)
wait(); //handle InterruptedException
//
valueSet = false;
notify();//if thread waiting in put, now notified
}
synchronized void put(int n) {
if(valueSet)
wait(); //handle InterruptedException
//
valueSet = true;
//if thread in get waiting then that is resumed now
notify();
}
}
或者您可以尝试使用sleep()并最终在main()中加入线程,但这不是万无一失的方法
您的代码中包含public void update(SomeClass c)
方法,并且此方法是instance method
,您在其中将对象作为参数传递。
您代码中的synceded synchronized(c)
什么也不做。 让我举一些例子
因此,如果您要制作此类的不同对象,然后尝试使它们成为不同的线程,例如,
class A extends Thread{
public void update(SomeClass c){}
public void run(){
update(c)
}
public static void main(String args[]){
A t1 = new A();
A t2 = new A();
t1.start();
t2.start();
}
}
然后,这两个t1和t2都将拥有自己的更新方法副本,并且使两个线程同步的参考变量c
也将不同。 t1调用自己的update()方法,t2调用自己的update()方法。 因此,同步将无法进行。
当两个线程都有共同点时,同步将起作用。
就像是,
class A extends Thread{
static SomeClass c;
public void update(){
synchronized(c){
}
}
public void run(){
update(c)
}
public static void main(String args[]){
A t1 = new A();
A t2 = new A();
t1.start();
t2.start();
}
}
这样,将应用同步的实际概念。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.