Javaデモ/Swing「Sinグラフを横スクロールさせる、Thread、排他制御(synchronized)、最適化の抑制(volatile)」(007)

 Sin グラフを横スクロールさせる、ComboBox で「波長の数、カラー」を指定して、[Make]ボタンで新しい Sin グラフを追加できます。

2つ以上の Sin グラフを表示すると、白色で合成波を表示します。

なお、SinGraph の Draw メソッドは使用していません。


このデモは「Concurrency Utilities」のスレッドを使用しています。

排他制御(synchronized)、最適化の抑制(volatile)も使用しており、特に排他制御(synchronized)は必要最小限にするように熟慮しています。


(知らぬ間に)古いタイプのThreadが非推奨に?、(正式な非推奨では無いかもしれないが)そんなクズみたいなモノを何時までも使わないでくれよ、って話。

スレッドは「Concurrency Utilities」のスレッドが強く推奨されます。

なお 新しいJavaでも古いタイプのThreadを使ってたら意味ないので注意して下さい、ただし素のJavaとAndroid Javaは内部的には別モノなので、Android Javaなら普通の(古いタイプの)Threadでおkです。


下記 動画を参照して下さい、青色がCUPの未使用率、緑色がCUPの使用率だと思われます。

その動画のコンソール入力に注目すると、「ConcurrencyTest」を実行すると、CUPの使用率が ほぼ100%になり、圧倒的に効率が上がります(逆に言うと古いタイプのThreadが如何にクズかと言うことでも有りますが)。


Concurrency Utilities for EE 7

https://yoshio3.com/2013/05/15/concurrency-utilities-for-ee-7/



【SinGraph007.java】


import java.awt.EventQueue;

import java.awt.event.ActionListener;

import java.awt.event.ActionEvent;


import java.awt.BorderLayout;

import javax.swing.DefaultComboBoxModel;

import javax.swing.JButton;

import javax.swing.JComboBox;

import javax.swing.JFrame;

import javax.swing.JPanel;


import java.awt.Graphics;

import java.awt.Graphics2D;

import java.awt.Color;


import java.util.ArrayList;


import java.util.concurrent.Callable;

import java.util.concurrent.CountDownLatch;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.Future;


public class SinGraph007 extends JFrame {

  static String sAppTitle = "Application SinGraph 7.";

  static boolean lApplication = false; // true;

  static SinGraph007 oAppFrame;

  JPanel content;


  // Thread oAppThread;

  int iLayoutX, iLayoutY;

  CanvasObj oCanvas;

  int iCanvasWidth, iCanvasHeight;

  JComboBox ccbSinGraph_WaveQnt;

  int iCbSinGraph_WaveQnt_Width, iCbSinGraph_WaveQnt_Height;

  JComboBox ccbSinGraph_Color;

  int iCbSinGraph_Color_Width, iCbSinGraph_Color_Height;

  Color[] d1oCbSinGraph_ColorSets;

  JButton cbtSinGraph_Make;

  int iBtSinGraph_Make_Width, iBtSinGraph_Make_Height;


  ExecutorService voTPool;

  CountDownLatch voCDLatch_Canvas;

  Future<Boolean> voFuture_Canvas;


  /**

   * Launch the application.

   */

  public static void main(String[] args) {

    // 以前はイベントもメイン・スレッドでしたが、

    // イベントはイベント・ディスパッチ・スレッドに分離されたために、

    // GUIコンポーネントの設定もイベント・ディスパッチ・スレッドで設定しなければならなくなったので、

    // メイン・スレッドでのGUIコンポーネントの設定はNGになりました、

    // 下記のように「EventQueue.invokeLater()」でイベント・ディスパッチ・スレッドに登録しなければなりません。

    EventQueue.invokeLater(new Runnable() {

      public void run() {

        try {

          oAppFrame = new SinGraph007();

          oAppFrame.setVisible(true);

        } catch (Exception ex) {

          ex.printStackTrace();

        }

      }

    });

  }


  /**

   * Create the frame.

   */

  public SinGraph007() {

    // 閉じるボタンをクリックされた場合の動作を設定

    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);


    setTitle(sAppTitle);

    setLocation(50, 50);


    // ContentPaneの実態はJFrameにビルトインされているデフォルト・パネル。

    content = (JPanel) getContentPane();

    // content.setSize(500, 500);


    // content.setLayout(null);

    // getContentPane().add(oAppFrame, BorderLayout.CENTER);


    getContentPane().setLayout(null); // Layout 座標指定モード


    ccbSinGraph_WaveQnt = new JComboBox();

    ccbSinGraph_WaveQnt.setModel(new DefaultComboBoxModel(new Object[] { " 1 ", " 2 ", " 3 " }));

    ccbSinGraph_WaveQnt.setDoubleBuffered(false);

    ccbSinGraph_WaveQnt.setBorder(null);

    iCbSinGraph_WaveQnt_Width = ccbSinGraph_WaveQnt.getMinimumSize().width;

    iCbSinGraph_WaveQnt_Height = ccbSinGraph_WaveQnt.getMinimumSize().height;


    d1oCbSinGraph_ColorSets = new Color[] {

        Color.GREEN, Color.PINK,

        Color.RED, Color.YELLOW,

    };

    ccbSinGraph_Color = new JComboBox();

    ccbSinGraph_Color.setModel(new DefaultComboBoxModel(new Object[] { "グリーン", "ピンク", "赤", "黄色", }));

    ccbSinGraph_Color.setDoubleBuffered(false);

    ccbSinGraph_Color.setBorder(null);

    iCbSinGraph_Color_Width = ccbSinGraph_Color.getMinimumSize().width;

    iCbSinGraph_Color_Height = ccbSinGraph_Color.getMinimumSize().height;


    cbtSinGraph_Make = new JButton();

    cbtSinGraph_Make.setText("Make");

    iBtSinGraph_Make_Width = cbtSinGraph_Make.getMinimumSize().width;

    iBtSinGraph_Make_Height = cbtSinGraph_Make.getMinimumSize().height;

    cbtSinGraph_Make.addActionListener(new ActionListener() {

      @Override

      public void actionPerformed(ActionEvent ev) {

        if (null != oCanvas) {

          oCanvas.SinGraph_Make();

        }

      }

    });

    

    iCanvasWidth = 500;

    iCanvasHeight = 500;


    iLayoutX = 0;

    iLayoutY = 0;

    getContentPane().add(ccbSinGraph_WaveQnt);

    ccbSinGraph_WaveQnt.setBounds(iLayoutX, iLayoutY, iCbSinGraph_WaveQnt_Width, iCbSinGraph_WaveQnt_Height);


    iLayoutX = iLayoutX + iCbSinGraph_WaveQnt_Width; // iLayoutY = 0;

    getContentPane().add(ccbSinGraph_Color);

    ccbSinGraph_Color.setBounds(iLayoutX, iLayoutY, iCbSinGraph_Color_Width, iCbSinGraph_Color_Height);


    iLayoutX = iLayoutX + iCbSinGraph_Color_Width; // iLayoutY = 0;

    getContentPane().add(cbtSinGraph_Make);

    cbtSinGraph_Make.setBounds(iLayoutX, iLayoutY, iBtSinGraph_Make_Width, iBtSinGraph_Make_Height);


    iLayoutX = 0;

    iLayoutY = iLayoutY + iBtSinGraph_Make_Height;

    oCanvas = new CanvasObj(iLayoutX, iLayoutY, iCanvasWidth, iCanvasHeight);

    getContentPane().add(oCanvas);

    // oCanvas.setVisible(true);


    iLayoutX = iCanvasWidth;

    iLayoutY = iLayoutY + iCanvasHeight;

    content.setSize(iLayoutX, iLayoutY);


    // 「JFrame#setSize(~)」でフレーム・サイズを設定すると、

    // タイトルも含むウインドウ全体のサイズになります。

    // つまり、ウインドウ内の表示領域の縦がタイトルのサイズ分 少なくなります。

    // 「JFrame#getContentPane().setPreferredSize(~)」で設定し、

    // 「pack()」すれば、ContentPaneで設定された全表示領域が表示されます。

    content.setPreferredSize(content.getSize());

    pack();


    voTPool = Executors.newCachedThreadPool();

    voCDLatch_Canvas = new CountDownLatch(1);

    voFuture_Canvas = voTPool.submit(oCanvas);

    voTPool.shutdown();

    // ExecutorService#shutdownは既に投入済みの全タスクの

    // 処理が終わるまで待機してからスレッドを終了させます。

    // また、shutdownを実行しないと、mainスレッドが終了してもJVMは終了しません。

  }


  class SinGraph {

    CanvasObj oCanvas;

    // int iWidth, iHeight;

    int iMoveStride = 3;

    // volatile:最適化の抑制.

    volatile int iMoveDistance = 0;

    int iWaveQnt = 1;

    double vdWaveHeightRate = iWaveQnt;

    int iGraphXStep = 0;

    int iWaveWidth;

    int iWaveHeight;

    Color oColor = Color.BLUE;

    double vdRadian;

    double vdWaveWidth, vdWaveHeight;

    double vdGraphX1, vdGraphX2;

    double vdGraphY1, vdGraphY2;

    double vdWaveY1, vdWaveY2;

    double vdRate, vdRateInt;


    SinGraph(CanvasObj ccv, int iWQ, Color oC) {

      // コンストラクター自体には synchronized 修飾子を付与できないので、

      // synchronized ブロックで囲う。

      synchronized (this) {

        // iMoveDistance に対する競合(描画中の変更)を回避する。

        // 対象は SinGraph("自分")内の変数です。

        oCanvas = ccv;

        iWaveQnt = iWQ;

        vdWaveHeightRate = iWaveQnt; // sin グラフの高さの調整。

        oColor = oC;

      }

    }


    synchronized void Draw(Graphics oGraphic) {

      // iMoveDistance に対する競合(描画中の変更)を回避する。

      // 対象は SinGraph("自分")内の変数です。

      Graphics2D oGraphic2D = (Graphics2D) oGraphic;


      vdGraphX1 = -1;

      vdGraphX2 = -1;


      oGraphic2D.setColor(oColor);

      vdWaveWidth = (double) oCanvas.iWidth / iWaveQnt;

      vdWaveHeight = vdWaveWidth / (2 * Math.PI) * vdWaveHeightRate;

      iGraphXStep = 0;

      while (iGraphXStep < oCanvas.iWidth) { // true / false

        DrawBullet(oGraphic, iGraphXStep);

        iGraphXStep++;

      }

    }


    void DrawBullet(Graphics oGraphic, int iBulletGraphXStep) {

      Graphics2D oGraphic2D = (Graphics2D) oGraphic;


      oGraphic2D.setColor(oColor);


      if (0 == iBulletGraphXStep) {

        vdGraphX1 = -1;

        vdGraphX2 = -1;

      }

      vdWaveWidth = (double) oCanvas.iWidth / iWaveQnt;

      vdWaveHeight = vdWaveWidth / (2 * Math.PI) * vdWaveHeightRate;


      vdGraphX1 = vdGraphX2;

      vdGraphY1 = vdGraphY2;

      vdWaveY1 = vdWaveY2;

      vdRate = (double) (iBulletGraphXStep + oCanvas.iWidth - iMoveDistance) / oCanvas.iWidth * iWaveQnt;

      vdRateInt = Math.floor(vdRate);

      vdRate = vdRate - vdRateInt;

      vdGraphX2 = iBulletGraphXStep;

      vdRadian = 2 * Math.PI * vdRate;

      vdWaveY2 = Math.sin(vdRadian) * vdWaveHeight;

      vdGraphY2 = oCanvas.iHeight / 2 - vdWaveY2;

      if (0 <= vdGraphX1) {

        oGraphic2D.drawLine(

            (int) Math.round(vdGraphX1), (int) Math.round(vdGraphY1),

            (int) Math.round(vdGraphX2), (int) Math.round(vdGraphY2));

        // oCanvas.vdGraphX1 = oCanvas.vdGraphX1+vdGraphX1;

        // oCanvas.vdGraphX2 = oCanvas.vdGraphX2+vdGraphX2;

        oCanvas.vdWaveY1 = oCanvas.vdWaveY1 + vdWaveY1;

        oCanvas.vdWaveY2 = oCanvas.vdWaveY2 + vdWaveY2;

      }

    }

  }


  class CanvasObj extends JPanel implements Callable<Boolean> {

    // Swing には JCanvas は存在しないので、

    // グラフィックの描画には JPanel を継承して Canvas の代わりに使う。

    Thread oThread;

    Future<Boolean> voFuture_Canvas;

    // CountDownLatch voCDLatch_Alpha;

    // SinGraph oSinGraph;

    int iWidth;

    int iHeight;

    ArrayList<SinGraph> dl1oSinGraph = new ArrayList<SinGraph>();

    int iGraphXStep;

    double vdGraphX1, vdGraphX2;

    double vdGraphY1, vdGraphY2;

    double vdWaveY1, vdWaveY2;

    double vdWaveYRate = 0.666; // 合成波のY軸の倍率。


    CanvasObj(int x, int y, int w, int h) {

      super();

      setBounds(x, y, w, h);

      iWidth = getWidth();

      iHeight = getHeight();

      SinGraph_Make();


    }


    void SinGraph_Make() {

      dl1oSinGraph.add(new SinGraph(this,

          ccbSinGraph_WaveQnt.getSelectedIndex() + 1,

          d1oCbSinGraph_ColorSets[ccbSinGraph_Color.getSelectedIndex()]));

    }


    @Override

    public Boolean call() {

      while (true) { // true / false

        repaint();

        try {

          Thread.sleep(50); // ←50ミリ秒の sleep。

        } catch (InterruptedException ex) {

          // ex.printStackTrace();

          System.out.println("CanvasObj#(Boolean)call(): Interrupted Exception.");

          break;

        }

        synchronized (CanvasObj.this) {

          // SinGraph内の iMoveDistance変数に対する競合(描画中の変更)を回避する。

          // 対象は SinGraph内の変数なのだが、

          // (CanvasObj の) paint内のループ全体の処理を1つと見なす必要があるので

          // 実際の対象は CanvasObj とする必要がある。

          for (SinGraph oSinGraph : dl1oSinGraph.toArray(new SinGraph[0])) {

            // ↑この「new SinGraph[0]」の部分は要素がゼロ個の配列を指定しなければならないらしい。

            // それにより指定された型と同一の型が返されるらしい。

            oSinGraph.iMoveDistance = oSinGraph.iMoveDistance + oSinGraph.iMoveStride;

            if (iWidth <= oSinGraph.iMoveDistance) {

              oSinGraph.iMoveDistance = oSinGraph.iMoveDistance - iWidth;

            }

          }

        }

      }

      return null;

    }


    @Override

    public synchronized void paint(Graphics g) {

      Graphics2D g2 = (Graphics2D) g;

      // ↑(JPanel を継承しているので)ここの Graphics の実態は Graphics2D となるから、

      // キャストしてやれば Graphics2D が使える.


      g2.setBackground(Color.BLACK);

      g2.clearRect(0, 0, iWidth, iHeight);


      g2.setColor(Color.WHITE);

      g2.drawLine(0, iHeight / 2, iWidth, iHeight / 2);


      iGraphXStep = 0;

      vdGraphX1 = 0;

      vdGraphX2 = 0;

      while (iGraphXStep < iWidth) { // true / false

        vdWaveY1 = 0;

        vdWaveY2 = 0;

        vdGraphX1 = vdGraphX2;

        for (SinGraph oSinGraph : dl1oSinGraph.toArray(new SinGraph[0])) {

          // ↑この「new SinGraph[0]」の部分は要素がゼロ個の配列を指定しなければならないらしい。

          // それにより指定された型と同一の型が返されるらしい。

          oSinGraph.DrawBullet(g, iGraphXStep);

        }

        vdGraphX2 = iGraphXStep;

        if (2 <= dl1oSinGraph.size() && 0 < iGraphXStep) {

          vdGraphY1 = iHeight / 2 - vdWaveY1 * vdWaveYRate;

          vdGraphY2 = iHeight / 2 - vdWaveY2 * vdWaveYRate;

          g2.setColor(Color.WHITE);

          g2.drawLine(

              (int) Math.round(vdGraphX1), (int) Math.round(vdGraphY1),

              (int) Math.round(vdGraphX2), (int) Math.round(vdGraphY2));

        }

        iGraphXStep++;

      }

    }

  }

}





コメント

このブログの人気の投稿