首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java Applet 動畫設計精要:MediaTracker、線程協作與 volatile 關鍵字

     編輯:Java 相關知識

學習目標

通過一個完整的 Applet 動畫範例,掌握:

  1. Applet/JApplet 核心生命週期方法
  2. 圖片載入與 MediaTracker 同步機制
  3. 動畫線程設計與 Thread.join() 優雅停止
  4. volatile 關鍵字在多線程可見性中的關鍵作用

動畫主類:Animate.java


import javax.swing.JApplet;
import java.awt.*;
import java.awt.image.ImageObserver;
import java.awt.MediaTracker;

public class Animate extends JApplet {
    private static final int NUM_OF_PIC = 4;
    private int count = 1;
    private Image[] pics = new Image[NUM_OF_PIC];
    private TimerThread timer;

    public void init() {
        MediaTracker tracker = new MediaTracker(this);
        for (int i = 0; i < NUM_OF_PIC; i++) {
            // 圖片命名 0.jpg, 1.jpg... 放在 codebase 目錄
            pics[i] = getImage(getCodeBase(), i + ".jpg");
            tracker.addImage(pics[i], 0);
        }
        try { tracker.checkAll(true); } // 等待所有圖片載入完成
        catch (InterruptedException e) { e.printStackTrace(); }
    }

    public void start() {
        timer = new TimerThread(this, 1000); // 1 秒切換一次
        timer.start();
    }

    public void stop() {
        timer.shouldRun = false; // 通知線程退出
        try { timer.join(); } // 等待線程真正結束
        catch (InterruptedException e) {}
    }

    public void paint(Graphics g) {
        g.drawImage(pics[count++], 0, 0, this);
        if (count == NUM_OF_PIC) count = 0;
    }
}

動畫控制線程:TimerThread.java


import java.awt.Component;

public class TimerThread extends Thread {
    private Component comp;
    private int timediff;
    public volatile boolean shouldRun = true; // 關鍵:volatile 保證可見性

    public TimerThread(Component comp, int timediff) {
        super("TimerThread-" + timediff);
        this.comp = comp;
        this.timediff = timediff;
    }

    public void run() {
        while (shouldRun) {
            try {
                comp.repaint(); // 觸發 paint()
                sleep(timediff);
            } catch (Exception e) { break; }
        }
    }
}

核心技術深度解析

1. MediaTracker:確保圖片完全載入

getImage() 非阻塞立即返回,圖片實際載入在背景進行。直接使用會導致繪製空白或不完整。

三步驟標準流程:

  1. MediaTracker tracker = new MediaTracker(component);
  2. tracker.addImage(image, id); (id 可分組管理)
  3. tracker.checkAll(true); 阻塞等待全部載入完成(true 表示遇錯誤拋出異常)。

2. Thread.join() vs 輪詢

join():當前線程阻塞,直到目標線程結束。CPU 佔用極低,響應即時。

輪詢替代方案:


while (timer.isAlive()) {
    try { Thread.sleep(50); } catch (InterruptedException e) {}
}

缺點:浪費 CPU、引入額外延遲(最多 50ms)、代碼冗長。強烈推薦 join()。

3. volatile 關鍵字:多線程記憶體可見性保證

背景:JVM 1.2+ 引入線程本地緩存(寄存器、L1/L2 Cache),線程讀寫變量可能不直接操作主記憶體。

問題:主線程修改 shouldRun = false,動畫線程可能仍讀取舊的緩存值 true,導致無法停止。

解決:volatile boolean shouldRun 強制每次讀寫直接操作主記憶體,確保所有線程立即看到最新值。

規則:多線程共享的標誌位、狀態變量,必須加 volatile(或使用 AtomicBoolean/同步塊)。

完整運行流程

  1. 瀏覽器載入 Applet → init() 載入 4 張圖片並阻塞等待完成。
  2. start() 啟動 TimerThread,每秒呼叫 repaint()。
  3. paint() 循環繪製圖片,實現動畫。
  4. 用戶離開頁面 → stop() 設 shouldRun=false → join() 等待線程安全退出 → 資源釋放。

遷移建議

Applet 技術已淘汰(瀏覽器不再支援 Java 插件)。現代替代方案:

  • HTML5 Canvas + JavaScript:原生、無插件、跨平台。
  • JavaFX:豐富 UI 控件、CSS 樣式、FXML 分離。
  • Swing/Java2D:桌面應用仍可用,配合 SwingWorker 處理背景任務。

但本例展示的多線程協作、資源同步、記憶體可見性核心概念,在所有 Java 併發編程中通用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved