<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html lang="ja"> <head> <style type="text/css">body { background-color: #FFEEBB;}</style> </head> <script type="text/javascript"> var n = 0; function sub1(){ try { if(! document.ap01.isActive()){//onloadで実行すると失敗する可能性があり、タイマーで可能になるまでトライさせる。 setTimeout("sub1()", 100);// 0.1秒後に実行 document.getElementById("X").innerHTML = ++n + "回目の待ち状態"; return;//アプレットがまだ使えない } } catch(e){ setTimeout("sub1()", 100);// 0.1秒後に実行 document.getElementById("X").innerHTML = ++n + "回目の待ち状態"; return;//アプレットがまだ使えない } //alert( document.ap01.isActive() );//アプレットが使えるようになった。 var count = document.ap01.getCount(); document.getElementById("X").innerHTML = "移動回数:" + count; } function sub2(page){ var path = location.href; var i=path.lastIndexOf("/"); page = path.substring(0, i+1) + page; alert( page + "へ移動します。"); document.ap01.setPage(page);//アプレットに作った移動メソッドの実行(ブラウザなどの環境によって実行できないかも?) } function start(){ setTimeout("sub1()", 100);// 0.1秒後に実行 document.getElementById("X").innerHTML = ++n + "回目の待ち";//onloadで実行してかまわない。 } </script> <body onload="start()"> <h1>1のページ</h1> <applet CODEBASE="./" code="TestGotoApplet.class" name="ap01" Width="120" Height="90" > </applet><br> <br> <span id="X">XXXX</span>です<br> <br> <span style="border-style: outset; border-width: 8px; border-color: yellow; cursor: pointer" onclick="sub2('testgotoapplet2.html')"> ページ移動</span> </body> </html>
上記の2つのページで使っているアプレットクラスのソースを以下に示します。
import java.applet.*; import java.util.*; import java.awt.*; import java.awt.event.*; import java.net.*; public class TestGotoApplet extends Applet implements Runnable{ static int count = 1; String page;//移動先 Thread thread; byte thread_state;//0:停止状態,1:起動中, 2:停止途中 public void run(){//スレッド try { thread_state = 1; while(thread_state == 1){ if(page != null){ AppletContext context = this.getAppletContext(); try { URL url = new URL(page); context.showDocument(url);//ページ移動 } catch(Exception e){e.printStackTrace();} } Thread.sleep(2000);//2秒 System.out.println((new Date()).toString()); } } catch(Exception e){e.printStackTrace();} thread_state = 0; } public void init(){//アプレットメソッドのオーバーライド count++;//カウント this.thread = new Thread(this); this.thread.start(); } public void stop(){//アプレットメソッドのオーバーライド System.out.println("stop1"); if(thread == null) return; thread_state = 2; while(thread_state != 0){//スレッドが停止するまで待つ try { Thread.sleep(1); } catch(Exception e){e.printStackTrace();} } thread = null; System.gc(); System.out.println("stop2"); } public void setPage(String page){//移動ページ設定 this.page = page; } public int getCount(){//スタティック変数を返す(セッション管理に使える技法) return count; } }
ページの移動の部分をクリックすることでアプレット内のインスタンス変数に、移動ページが記憶されて
それが、スレッド内のアプレット移動処理で実行されますが、それによってアプレットが破棄されます。
Javaコンソールを表示さいた例を以下に示します。
破棄の時に、InterruptedException: sleep interruptedが起きて、スレッドが停止しているのが分かります。
しかし、これはなぜ起きるのでしょうか?
上記では、アプレットのstopメソッドがスレッド内ループを止める働きがあるので、この例外は起きなくても良いはずです。
実験を行うと分かるですが、この例外が起きないで移動できる場合もあります。
この頻度は、ソースのThread.sleep(2000);//2秒を減らすと少なくなります。
Mon Mar 05 17:09:30 JST 2012 Mon Mar 05 17:09:32 JST 2012 Mon Mar 05 17:09:34 JST 2012 Mon Mar 05 17:09:36 JST 2012 stop1 java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at TestGotoApplet.stop(TestGotoApplet.java:45) at sun.plugin2.applet.Plugin2Manager$AppletExecutionRunnable.run(Unknown Source) at java.lang.Thread.run(Unknown Source) java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at TestGotoApplet.run(TestGotoApplet.java:25) at java.lang.Thread.run(Unknown Source) stop2 Mon Mar 05 17:09:39 JST 2012 Mon Mar 05 17:09:41 JST 2012 Mon Mar 05 17:09:43 JST 2012
以上の実験より、アプレットのスレッドは、プログラムで止めなくても、アプレッドが破棄される処理で、
自動的に、、InterruptedException: sleep interruptedの例外が送られて停止されるということが分かります。
(stopメソッドが正しく実行されれば、この例外が起きないからです)
つまり、スレッド内の処理で時間がかかるプログラムを書いた場合に、ページ移動で実行できないケースがあり得るということが分かります。
また、そのスレッドを制御するプログラムをのstopメソッドに書いても、働かない危険があるということになります。
この技法を使う場合、以上の危険を考慮してプログラミングしなければならないということです。
この例であれば、移動メソッド実行前で、スレッドを停止し、その確認後に行えばこの不具合は起きないでしょう。