Java 在开始编码之前如何处理并发性

Java 在开始编码之前如何处理并发性,java,concurrency,multithreading,project-planning,Java,Concurrency,Multithreading,Project Planning,我正在编写一个Java程序的中途,我正在调试的并发性问题比我想处理的要多得多 我必须问:在精神上制定计划时,您如何处理并发性问题?在我的例子中,这是一个相对简单的游戏,但线程的问题不断出现-任何快速修复几乎肯定会导致一个新的问题 一般来说,在决定应用程序如何“流动”而不让所有线程陷入困境时,应该使用哪些技术 这取决于线程的功能。通常,程序有一个主线程进行思考,工作线程执行并行任务(计时器、在GUI上处理长时间的计算等),但您的应用程序可能不同——这取决于您的设计。你用线程做什么?您必须使用什么锁

我正在编写一个Java程序的中途,我正在调试的并发性问题比我想处理的要多得多

我必须问:在精神上制定计划时,您如何处理并发性问题?在我的例子中,这是一个相对简单的游戏,但线程的问题不断出现-任何快速修复几乎肯定会导致一个新的问题


一般来说,在决定应用程序如何“流动”而不让所有线程陷入困境时,应该使用哪些技术

这取决于线程的功能。通常,程序有一个主线程进行思考,工作线程执行并行任务(计时器、在GUI上处理长时间的计算等),但您的应用程序可能不同——这取决于您的设计。你用线程做什么?您必须使用什么锁来保护共享数据结构?如果使用多个锁,是否有一个单独的锁顺序来防止死锁?

线程越少,它们共享的状态越小,并且它们在此共享状态下的交互模式越简单,您的生活就越简单

你说列表抛出了ConcurrentModificationException。我认为您的列表是通过不同的线程访问的。所以你首先应该问自己这是否必要。第二个线程不可能对列表的副本进行操作吗

如果线程确实需要并发访问列表,那么在整个遍历过程中锁定列表可能是一个选项(如果列表通过除迭代器之外的任何其他方式修改,迭代器将无效)。当然,如果在遍历列表时执行其他操作,则此遍历可能需要很长时间,锁定其他线程可能会威胁到系统的活力


还要记住,如果列表是共享状态,那么它的内容也是共享状态,因此如果您打算通过复制列表来绕过锁定,请确保执行深度复制,或者证明列表中包含的对象本身是线程安全的。

我尽可能使用不可变的数据结构。大约我唯一一次使用可变结构是在我必须这样做的时候,比如使用一个可以节省大量工作的库。即使如此,我还是尝试将该库封装在一个不可变的结构中。如果事情不能改变,那么就不用担心了

我要补充的是,在你未来的努力中,要记住的一些事情是STM和演员模型。这两种并发方法都显示出非常好的进展。虽然每个都有一些开销,但这取决于您的程序的性质,这可能不是问题

编辑:


下面是一些链接,指向一些可以在下一个项目中使用的库。顾名思义,还有一个用于Java的STM实现。还有一个,顾名思义,它是Java的参与者模型。但是,我忍不住要用内置的参与者模型为其制作插件。

从设计角度来看,我发现绘制序列图非常有用,其中每个线程的动作都有颜色编码(即每个线程都有自己的颜色)。以这种方式使用颜色可能是序列图的一种非标准用法,但它有助于概述线程的相互作用方式和位置

正如其他人所提到的,将设计中的线程数量减少到正常工作所需的绝对最小值也会有很大帮助

  • 尝试使用java.util.concurrent包中的集合,或者更好地使用Google集合中的不可变集合
  • 阅读有关使用同步块的信息

  • 就您提到的ConcurrentModificationException而言,应用程序的多线程特性可能是一种误导:您可以通过其他方式获得不一定涉及多线程的ConcurrentModificationException。 考虑以下事项:

    List<Item> items = new ArrayList<Item>();
    
    //... some code adding items to the list
    
    for (Item item : items) {
        if(item.isTheOneIWantToRemove()) {
            items.remove(item); //This will result in a ConcurrentModificationException
        }
    }
    
    List items=new ArrayList();
    //... 将项目添加到列表中的一些代码
    用于(项目:项目){
    if(item.isTheOneIWantToRemove()){
    items.remove(item);//这将导致ConcurrentModificationException
    }
    }
    
    将for循环更改为具有迭代器的循环,或增加索引值可以解决此问题:

    for (Iterator<String> it = items.iterator(); it.hasNext();) {
        if(item.isTheOneIWantToRemove()) {
            it.remove(); //No exception thrown
        }
    }
    
    for(Iterator it=items.Iterator();it.hasNext();){
    if(item.isTheOneIWantToRemove()){
    it.remove();//未引发异常
    }
    }
    

    for(int i=0;i
    并发性归结为管理共享状态

    “所有并发问题归结为 协调对可变状态的访问。 状态的易变性越小,越容易改变 是为了确保螺纹安全。” --Java并发在实践中的应用

    因此,你必须问自己的问题是:

    • my应用程序需要哪些固有的共享数据
    • 线程何时可以处理数据的快照,也就是说,它可以瞬间处理共享数据的克隆
    • 我是否可以识别已知的模式并使用更高级别的抽象,而不是低级别的锁和线程协调,例如队列、执行器等
    • 可以将全局锁定方案看作是为了避免死锁并获得一致的锁
    管理共享状态的最简单方法是序列化每个操作。但是,这种粗粒度方法会导致高锁争用和低性能。管理并发可以看作是一个优化练习,您可以在其中尝试减少争用。因此,接下来的问题是:

    • 最简单的方法是什么
    • 在不使解决方案过于复杂的情况下,我可以做出哪些简单的选择来减少争用(可能使用细粒度锁定)并提高性能
    • 我什么时候去
      for (int i = 0; i < items.size(); i++) {
          if(item.isTheOneIWantToRemove()) {
              items.remove(items.get(i)); //No exception thrown
          }
      }