我在某个字段上有一个带有getter getInt()
和setter setInt()
的课程,比如字段
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。
// =====================================
编辑:
我不是在操纵线程的执行顺序之后。他们都有同样的优先权。它只是我在结果中看到的是暗示线程没有均匀地共享执行时间 - 其中一个,一旦接管,执行。但是,我不明白为什么这段代码应该这样做。
答案 0 :(得分:1)
JDK为多线程int访问提供了一个很好的解决方案,AtomicInteger:
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicInteger.html
答案 1 :(得分:1)
只是我在结果中看到的是暗示线程没有均匀地共享执行时间
嗯,如果您追求效率,这正是您不想要的。从一个线程执行并调度另一个线程通常非常昂贵。因此,one of them, once takes over, executing on
实际上是有利的。当然,当这种情况过度时,您可以看到更高的吞吐量但响应时间更长。理论上。实际上,JVM的线程调度几乎可以用于所有目的,并且您不希望在几乎所有情况下都尝试更改它。根据经验,如果您对毫秒级的响应时间感兴趣,您可能希望远离它。
tl; dr:这不是效率低下,你可能想要保持原样。
修改强>
话虽如此,使用AtomicInteger
可能有助于提高性能,在我看来,与使用锁定(synchronized
关键字)相比,它更不容易出错。你需要非常努力地击中那个变量才能获得可衡量的好处。
答案 2 :(得分:1)
作为Enno Shioji has pointed out,让一个线程继续运行可能是在某些情况下执行代码的最有效方式。
这取决于线程同步与代码的其他工作(我们不知道)相关的成本。如果你有一个循环:
while (<condition-1>)
if (<condition-2>) {
// calculate k here
synchronized (c) {
c.setInt(c.getInt()+k);
}
}
并且条件-1和条件-2的测试以及k
的计算与同步成本相比相当便宜,Hotspot优化器可能决定通过将代码转换为如下代码来减少开销:
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
,但这不再重要了。
答案 3 :(得分:0)
回答这个
我看到一个线程执行很长时间没有任何其他线程在运行。 必须有更好的方法来做到这一点。
您无法控制线程的执行方式。 JVM会为您执行此操作,并且不希望您干涉其工作。
您仍然可以选择yield
作为选项,但这也不能确保不会再次选择相同的帖子。
java.lang.Thread.yield()方法导致当前正在执行的线程对象暂时暂停并允许其他线程执行。
答案 4 :(得分:0)
我发现使用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()中加入最后的线程,但这不是一个万无一失的方式
答案 5 :(得分:-1)
您的代码中有public void update(SomeClass c)
方法,此方法是instance method
,您将对象作为参数传递。
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&amp; 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();
}
}
这样就可以应用实际的同步概念。