我只有一个同步方法。其目的是防止两个线程更新同一个表,这肯定会导致异常。我试图更新一个表,其数据来自一个巨大的外部数据库。在更新我的表之前,我必须进行一些过滤和清理,这需要花费很多时间。我创建了多个线程,以便我可以快速划分任务并更新表。计划/设计是在一个线程完成后清理数据,它应该更新我的表,而其他线程仍在进行清理。但是,在我的示例代码中,由于之前发生关系,线程会覆盖这些值。我不希望线程共享数据,我只是希望它们在某些线程更新时不会更新。如果您可以在下面的代码中看到,我创建了一个列表,我用它来循环创建一个线程并传递列表中的每个数据。但是,在结果中,线程1,4和5在不应该使用Synchronized方法时使用相同的programId。任何人都可以帮助防止这种情况,以便每个线程在同步代码中都有唯一的ID吗?
这是我的代码:
public Class HomeController{
List<String> programList = new ArrayList<String>();
programList.add("xxx");
programList.add("yyy");
for(String programId: programList){
System.out.println("programId Controller is " + programId);
//for each program Id create a thread
runnable.setProgramId(programId);
taskExecutor.execute(runnable);
}
}
public class TestRunnable implements Runnable{
public void run(){
String threadName = Thread.currentThread().getName();
System.out.println("threadName and programId Runnable are " + threadName +"--"+ programId);
TestSynchronized testSynchronized = new TestSynchronized();
testSynchronized.updateTable(programId);
}
}
}
public class TestSynchronized{
private volatile boolean isLocked = false;
public synchronized void updateTable(String programId){
while(isLocked) {
try {
wait();
} catch (InterruptedException e) {}
}
isLocked=true;
String threadName = Thread.currentThread().getName();
System.out.println("threadName and programId Synchronized are " + threadName +"--"+ programId);
isLocked=false;
notify();
}
}
结果如下:
programId Controller is 23022490857
programId Controller is 23022491963
threadName and programId Runnable are taskExecutor-1--23022490857
programId Controller is 23022492068
threadName and programId Runnable are taskExecutor-2--23022491963
programId Controller is 23022492300
threadName and programId Synchronized are taskExecutor-1--23022491963
programId Controller is 23022495561
threadName and programId Runnable are taskExecutor-3--23022492068
threadName and programId Synchronized are taskExecutor-2--23022492068
programId Controller is 38416827134
threadName and programId Synchronized are taskExecutor-3--23022492300
threadName and programId Runnable are taskExecutor-4--23022492300
threadName and programId Runnable are taskExecutor-1--38416827134
threadName and programId Synchronized are taskExecutor-1--38416827134
threadName and programId Runnable are taskExecutor-5--23022495561
threadName and programId Synchronized are taskExecutor-4--38416827134
threadName and programId Synchronized are taskExecutor-5--38416827134
答案 0 :(得分:2)
您在显示的代码中遇到两个问题:
TestRunnable
,这意味着在您使用programId
之前(或可以)覆盖它。TestSynchronized
,因此您根本没有没有线程安全。您需要做的是:
TestSynchronized
TestRunnable
。在构造函数中传递programId和共享TestSynchronized
。答案 1 :(得分:1)
你有多少份TestSynchronized
?如果它不止一个,您将无法获得任何同步。
将同步与实际代码执行分开。
答案 2 :(得分:1)
您需要了解对象监视器的工作方式。 synchronized
将阻止对资源(对象引用)的访问。在这种情况下,您为每个线程创建一个新的TestSynchronized
。由于同步适用于对象引用,因此您基本上不会阻塞任何地方。
如果您创建一个TestSynchronized
并将其传递给每个TestRunnable
,您将获得所需的输出。
答案 3 :(得分:1)
...由于之前发生的关系,线程正在覆盖这些值。
并没有真正回答你的问题但是,我认为你误解了&#34;之前发生的事情&#34;。
Java语言规范(JLS)使用短语&#34;在&#34;之前发生。描述某些保证,您可以用它来推断您的程序将如何表现。例如,JLS表示在一个线程中留下synchronized(o)
块在另一个线程在同一对象o
上同步之前发生。这就是你怎么知道如果线程A进入块,更新一些变量,然后离开块,然后线程B进入块;线程B保证看到变量中存储了什么线程A.
但是这里没有告诉你的事情:
它不会告诉您哪个线程首先将 中的传递给synchronized块。如果程序中的两个线程可能同时尝试同一个对象同步,那么您就有数据竞争。一个线程将赢得胜利,另一个线程将会失败,并且无法知道哪一个会赢。
如果您的程序中有数据竞争,并且哪个线程获胜很重要,那么您就会遇到缺陷。
由您决定更改您的设计以使哪个线程获胜无关紧要,或者更改您的设计以使用某种更高级别的同步(队列,信号量,障碍等) 。)确保正确的线程获胜。