如何在java中的同步代码中防止发生?

时间:2015-10-14 14:31:52

标签: java multithreading synchronization

我只有一个同步方法。其目的是防止两个线程更新同一个表,这肯定会导致异常。我试图更新一个表,其数据来自一个巨大的外部数据库。在更新我的表之前,我必须进行一些过滤和清理,这需要花费很多时间。我创建了多个线程,以便我可以快速划分任务并更新表。计划/设计是在一个线程完成后清理数据,它应该更新我的表,而其他线程仍在进行清理。但是,在我的示例代码中,由于之前发生关系,线程会覆盖这些值。我不希望线程共享数据,我只是希望它们在某些线程更新时不会更新。如果您可以在下面的代码中看到,我创建了一个列表,我用它来循环创建一个线程并传递列表中的每个数据。但是,在结果中,线程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

4 个答案:

答案 0 :(得分:2)

您在显示的代码中遇到两个问题:

  1. 您正在为每个线程重复使用相同的TestRunnable,这意味着在您使用programId之前(或可以)覆盖它。
  2. 您正在为每个帖子创建一个新的TestSynchronized,因此您根本没有没有线程安全。
  3. 您需要做的是:

    1. 创建一个,只有一个TestSynchronized
    2. 为每个programId创建 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块。如果程序中的两个线程可能同时尝试同一个对象同步,那么您就有数据竞争。一个线程将赢得胜利,另一个线程将会失败,并且无法知道哪一个会赢。

如果您的程序中有数据竞争,并且哪个线程获胜很重要,那么您就会遇到缺陷

由您决定更改您的设计以使哪个线程获胜无关紧要,或者更改您的设计以使用某种更高级别的同步(队列,信号量,障碍等) 。)确保正确的线程获胜。