2014-11-04 138 views
1

只需花費兩個小時來調試這個問題,我想我知道問題的原因,但我不明白它怎麼能產生它所做的那種結果。同樣的計算結果如何產生不同的結果

我有一個基於Swing應用程序,我重寫JPanel.paint(圖形G)這樣

public void paint(Graphics g) { 
    <snipped code> 

    Insets is = getInsets(); 
    Dimension sz = getSize(); 
    g2.setClip(is.left + 1, is.top + 1, sz.width - is.right - is.left - 2, sz.height - is.top - is.bottom - 2); 

    int w = getWidth(); 
    int h = getHeight(); 
    double s = Math.min(sz.getWidth(), sz.getHeight()); 

    AffineTransform mc = g2.getTransform(); 

    AffineTransform m1 = new AffineTransform(mc); 
    m1.preConcatenate(AffineTransform.getTranslateInstance(-m_CenterX, -m_CenterY)); 
    m1.preConcatenate(AffineTransform.getScaleInstance(m_ScaleX * s, -m_ScaleY * s)); 
    m1.preConcatenate(AffineTransform.getTranslateInstance(w/2, h/2)); 

    AffineTransform m2 = new AffineTransform(mc); 
    m2.preConcatenate(AffineTransform.getTranslateInstance(-m_CenterX, -m_CenterY)); 
    m2.preConcatenate(AffineTransform.getScaleInstance(m_ScaleX * s, -m_ScaleY * s)); 
    m2.preConcatenate(AffineTransform.getTranslateInstance(w/2, h/2)); 

    try { 
     m_InverseViewTransform = m1.createInverse(); 
    } catch (NoninvertibleTransformException e) { 

     AffineTransform m3 = new AffineTransform(mc); 
     m3.preConcatenate(AffineTransform.getTranslateInstance(-m_CenterX, -m_CenterY)); 
     m3.preConcatenate(AffineTransform.getScaleInstance(m_ScaleX * s, -m_ScaleY * s)); 
     m3.preConcatenate(AffineTransform.getTranslateInstance(w/2, h/2)); 

     System.out.println("m1 = " + m1); 
     System.out.println("m2 = " + m2); 
     System.out.println("m3 = " + m3); 

     AffineTransform m4 = new AffineTransform(mc); 
     System.out.println("m4 = " + m4); 
     m4.preConcatenate(AffineTransform.getTranslateInstance(-m_CenterX, -m_CenterY)); 
     System.out.println("m4 = " + m4); 
     m4.preConcatenate(AffineTransform.getScaleInstance(m_ScaleX * s, -m_ScaleY * s)); 
     System.out.println("m4 = " + m4); 
     m4.preConcatenate(AffineTransform.getTranslateInstance(w/2, h/2)); 
     System.out.println("m4 = " + m4); 

     System.out.println(w); 
     System.out.println(h); 
     System.out.println(s); 
     System.out.println(m_CenterX); 
     System.out.println(m_CenterY); 
     System.out.println(m_ScaleX); 
     System.out.println(m_ScaleY); 

     e.printStackTrace(); 

現在的奧祕,有時當我啓動應用程序,往往不是,說三次在四個中,引發NoninvertibleTransformException。

正如你所看到的調試,我輸出違規矩陣和三個其他相同計算的矩陣和用於形成這些矩陣的變量。

這就是它變得有趣的地方,請看下面的輸出,所有的矩陣都沒有相同的值!

m1 = AffineTransform[[0.0, 0.0, 400.0], [0.0, -0.0, 289.0]] 
m2 = AffineTransform[[0.0, 0.0, 400.0], [0.0, -0.0, 289.0]] 
m3 = AffineTransform[[0.0, 0.0, 400.0], [0.0, -0.0, 289.0]] 
m4 = AffineTransform[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]] 
m4 = AffineTransform[[1.0, 0.0, -49.5], [0.0, 1.0, -0.362762953421903]] 
m4 = AffineTransform[[5.254545454545455, 0.0, -260.1], [0.0, -587.9922126928986, 213.3017916655554]] 
m4 = AffineTransform[[5.254545454545455, 0.0, 139.89999999999998], [0.0, -587.9922126928986, 502.3017916655554]] 
800 
578 
578.0 
49.5 
0.36276295342190257 
0.009090909090909092 
1.0172875652126274 
java.awt.geom.NoninvertibleTransformException: Determinant is 0 
at java.awt.geom.AffineTransform.createInverse(AffineTransform.java:2706) 

更有趣的東西,如果我適當地移動調試輸出/計算周圍深入挖掘這個問題消失。

這種情況只發生在jdk1.8.0_25.jdk(Mac OS X 10.8.5)的JRE中,而不是Java 1.6或1.6。

好吧,我認爲真正的罪魁禍首是JPanel不是在Event Dispatched線程中創建的,一旦我這樣做了,那麼異常永遠不會拋出,沒有不一致。

所以很明顯是一個線程相關的時間依賴問題,但我很好奇在Java JVM的「規則」中如何發生這種情況?所涉及的所有變量都是本地方法,並且AffineTransform的矩陣方法不應該有副作用很難理解螺紋問題如何導致這樣的事情...

對於我的想法,我想了解這裏發生了什麼。

作爲一個副作用,這個問題可能會幫助一些可憐的靈魂在類似的問題上掙扎。

另外我不介意如果有人設法在代碼/調試/扣除中指出如此明顯的缺陷,因爲我現在已經盯着這些線路幾個小時......。

+0

那麼,你的值(我看到像m_CenterX這樣的東西)在除了讀取它們的EDT之外的任何其他線程上更改(或初始化)?如果是這樣,那麼你知道你可能有一個多線程問題。 – NESPowerGlove 2014-11-04 16:29:42

+0

難道是另一個線程正在與您從中複製初始轉換的'g2'同時'混淆'? - 嗯。可能不會,看到你正在爲你的調試輸出使用相同的'mc'。 – JimmyB 2014-11-04 16:36:02

+0

感謝和公平點,m_CenterX等在MouseListener中更新。但是,這是運行在EDT ...也許當我在主線程(而不是EDT,因爲它應該是)創建JPanel時,paint()被調用錯誤的線程(至少一次)。嗯,我可以在出現問題時輸出線程名稱...。 – nyholku 2014-11-04 16:39:37

回答

1

如果該方法不依賴任何共享數據,那麼它不能表現出您描述的行爲。但是,從您提供的內容中可以看出,您的方法避免了共享數據。我特別懷疑變量m_CenterX,m_CenterY,m_ScaleXm_ScaleY。這些聞起來像你的類的實例變量,如果是這樣的話,它們當然是在實例化類和EDT的線程之間共享的。如果沒有正確的同步,EDT可能會在構造函數(或其他線程中調用的其他方法)分配值之前查看這些變量的默認值。更一般地說,通過EDT中運行的方法直接或間接訪問的類的任何實例變量都在EDT和創建對象的線程(如果這些不同)之間共享。

+0

對,它們是成員變量,但由於paint和事件處理程序訪問這些m_ *變量應該在EDT中調用(在Swing/AWT線程模型中),這應該不是問題,但是如果(由於缺陷實例化JPanel在EDT)也許他們不是......我需要驗證這一點。 – nyholku 2014-11-04 19:29:05

+0

如果EDT通過在不同線程中運行的構造函數或方法進行初始化(沒有適當的同步),那麼EDT就會讀取這些實例變量的值。甚至有這樣的同步問題的術語:「不正確的發佈」。在這個特定的例子中,EDT可以在構造器完成初始化之前看到對象的狀態。 – 2014-11-04 19:40:03

+0

John,好的指針,搜索「不正確的發佈」提供了許多關於對象如何可見但未被完全/正確初始化的答案。如果你在答案中提到這個問題(不介意你說了一下但不是必要的),我會接受這個答案。 – nyholku 2014-11-05 09:41:07