2008年度 生物情報科学実験I Java基礎
第4回 8月8日 応用編:データベースアプリケーション
リレーショナルデータベース
データベースって何?
データが含まれていれば、それはテキストファイルでも、画像ファイルでも、データベースと呼んでいいでしょう。iTunesでも音楽データを管理するのにデータベースを使っていますし、アーティスト-アルバム-楽曲というフォルダ構成も、データベースといってもいいかもしれません。
既にファイルの読み書きの方法を知っていればデータベースの作成は原理的に可能です。実際、世の中のデータベース管理システム(DBMS: Database Management System)は、突き詰めれば、ファイルの読み書きで実装されています。
ただし、ディスク上のファイルは一次元配列でしかありません。その一本の配列にのようにデータを書き込めば、データを壊すことなく保存でき、再び取り出せるようになるのか。また、新しいデータの追加できるようにデータの配置を管理するなど、単にディスクにデータを保存するといっても、さまざまな工夫が必要です。さらに、データの検索を早くするために、HashMap, TreeMapなどの索引構造を、ディスク上に作成する必要があります(索引のためのデータ構造としてはB+-treeを使うのが一般的です)。
ですから、単純なフォルダやファイルというよりも、コンピューターから高速にデータを検索、更新しやすい形で整理・保存されているものを、一般にデータベースということが多いようです。そして、データベースを作成、更新、検索するためのシステム、ソフトウェアをデータベース管理システム(DBMS)と言います。
リレーショナルデータベース
今回の演習で扱うデータベースは、リレーショナルデータベース(Relational Database, 関係データベース)というもので、表(テーブル)形式でデータを持つデータベースです。
staff table
| id | name | |
|---|---|---|
| 1 | Taro L. Saito | xxx@xxx.xxxxx |
| 2 | Yoichiro Nakatani | yyy@xxx.xxxxx |
| 3 | Jun Yoshimura | zzz@xxx.xxxxx |
lecture table
| person_id | name |
|---|---|
| 1 | 情報生命科学演習 |
| 1 | 生物情報科学実習 |
| 2 | 生物情報科学実習 |
| 3 | 生物情報科学実習 |
このテーブルデータは、講義を担当しているstaffのリストと、それぞれが担当している講義lectureを表にしたものです。
このようなテーブルデータを作るために、リレーショナルデータベースシステムに SQL言語を用いて、上記のフォーマットのテーブルを作りなさい、行データを追加しなさいという指示を出すことになります。
SQL
SQL (エスキューエル。海外ではsequel(シークェル)と呼ぶ人が多い)は、問い合わせ言語(query language)と言われ、データベースからデータを取り出す命令を記述できます。
今回の演習ではSQLite http://www.sqlite.org/ を使うので、こちらのSQLの構文マニュアル(http://www.sqlite.org/lang.html )を参考にしてください。
テーブルの作成 (create table)
上記のテーブル形式のデータを作成するには、create table文を使います。
create table staff (
id integer primary key not null,
name text,
email text
);
create table文の書式は以下の通りです
create table テーブル名 (
(カラム名) (データ型名)(型の制約...)? ,
...
);
Javaのクラス定義の仕方と似ていますが、使えるデータ型の種類がデータベースシステム毎に決まっています。通常、いろいろなシステムに共通して使えるのは以下のデータ型です。
integer 整数
real 浮動小数点数
text 文字列
datetime 日時を表す型
id integerの後ろについているprimary key (主キー)という指定は、その列の値(id)は、重複してはいけない、ということを意味します。すなわち、テーブル中に同じidの値を持ったデータが存在してはいけない、という意味です。primary keyは、テーブル中から、一意に行を特定するために使われます。
not nullは、空っぽのデータをその列に入れてはいけないという指示です。
lectureテーブルを作るSQLは、以下のようになります。
create table lecture (
person_id integer not null,
name text
);
lectureテーブルでは、person_idは重複があってもOKです。
データの挿入 (insert)
テーブルを作成後、insert文を使い、データを挿入していきます。
insert into staff values('1', 'Taro L. Saito', 'xxx@xxx.xxxxx');
insert into staff values('2', 'Yoichiro Nakatani', 'yyy@xxx.xxxxx');
insert into staff values('3', 'Jun Yoshimura', 'zzz@xxx.xxxxx');
insert into lecture values('1', '情報生命科学演習');
insert into lecture values('1', '生物情報科学演習');
insert into lecture values('2', '生物情報科学演習');
insert into lecture values('3', '生物情報科学演習');
insert文の書式は以下の通り
insert into テーブル名 values('列データ', ...)
データの検索 (select)
データベースの検索にはselect文を使います。
select カラム名, ... from テーブル名,...
where 検索条件
order by 順序指定
limit 取得する件数 offset スキップする件数
where, order by, limit offsetなどは、オプションです。
staffテーブル中の全部のデータを検索
以下は、sqliteデータベースでSQLを実行した例
sqlite> select * from staff;
1|Taro L. Saito|xxx@xxx.xxxxx
2|Yoichiro Nakatani|yyy@xxx.xxxxx
3|Jun Yoshimura|zzz@xxx.xxxxx
staffテーブル中の、特定の列だけを検索
sqlite> select id,email from staff;
1|xxx@xxx.xxxxx
2|yyy@xxx.xxxxx
3|zzz@xxx.xxxxx
条件検索 (where)
sqlite> select * from staff where id = 3;
3|Jun Yoshimura|zzz@xxx.xxxxx
where文を使うと条件を指定してテーブルデータを検索できます。
条件検索 (and, or)
where節中でand, orを使うと複雑な条件を記述できます。
sqlite> select * from staff where name = 'Taro L. Saito' and email = 'xxx@xxx.xxxxx';
1|Taro L. Saito|xxx@xxx.xxxxx
行数のカウント (count)
sqlite> select count(*) from staff;
3
sqlite> select count(*) from staff where id = 3;
1
並び替え (order by 昇順)
sqlite> select * from staff order by name;
3|Jun Yoshimura|zzz@xxx.xxxxx
1|Taro L. Saito|xxx@xxx.xxxxx
2|Yoichiro Nakatani|yyy@xxx.xxxxx
並び替え (order by 降順)
descをつけると、降順になります。
sqlite> select * from staff order by name desc;
2|Yoichiro Nakatani|yyy@xxx.xxxxx
1|Taro L. Saito|xxx@xxx.xxxxx
3|Jun Yoshimura|zzz@xxx.xxxxx
limit, offset 指定
データ数が大きくなると、検索結果を10個に限定したり、先頭からいくつかデータをスキップしたいという場合があります。
sqlite> select * from staff limit 2;
1|Taro L. Saito|xxx@xxx.xxxxx
2|Yoichiro Nakatani|yyy@xxx.xxxxx
sqlite> select * from staff limit 2 offset 2;
3|Jun Yoshimura|zzz@xxx.xxxxx
テーブルの結合 (join)
from部分に二つ以上テーブルを書きます
sqlite> select staff.id, staff.name, lecture.name from staff, lecture where id = person_id;
1|Taro L. Saito|情報生命科学演習
1|Taro L. Saito|生物情報科学演習
2|Yoichiro Nakatani|生物情報科学演習
3|Jun Yoshimura|生物情報科学演習
left join(左側のテーブルデータの各行に対して、右側で指定されたテーブルデータを条件に基づいて結合していく)を使って書くこともできる。
sqlite> select staff.id, staff.name, lecture.name from staff left join lecture on id = person_id;
1|Taro L. Saito|情報生命科学演習
1|Taro L. Saito|生物情報科学演習
2|Yoichiro Nakatani|生物情報科学演習
3|Jun Yoshimura|生物情報科学演習
集約 (group by)
上のjoinで得られた結果に対して、staff.idが同じ行を、ひとつの行にまとめる. (group by staff.id)
sqlite> select staff.id, staff.name, group_concat(lecture.name, ',') from staff left join lecture on id = person_id group by staff.id;
1|Taro L. Saito|情報生命科学演習,生物情報科学演習
2|Yoichiro Nakatani|生物情報科学演習
3|Jun Yoshimura|生物情報科学演習
group_concat(lecture.name, ',') は、集約演算で、文字列のリストを、','でつないで結合する、という意味。
集約演算 (aggregate function)
group_concat以外にも、avg, count, min, max, sumなどで、複数のデータを集約することができます。group byを使って複数行を1つにまとめるときは、まとめられる行には集約演算を使わなくてはなりません。
select 集約演算(列名) from .. group by グループを決めるための列
exam_score table
| id | gender | class | score |
|---|---|---|---|
| 1 | female | A | 50 |
| 2 | male | A | 30 |
| 3 | male | B | 80 |
| 4 | female | B | 70 |
| 5 | female | C | 90 |
| 6 | male | C | 75 |
クラス毎の試験の平均点を求めるSQL
sqlite> select class, avg(score) from exam_score group by class;
A|40.0
B|75.0
C|82.5
平均、最小、最大、idのリストを同時に計算
sqlite> select class, avg(score), min(score), max(score), group_concat(id) from
exam_score group by class;
A|40.0|30|50|1,2
B|75.0|70|80|3,4
C|82.5|75|90|5,6
他の観点から集約
性別ごと
sqlite> select gender, avg(score) from exam_score group by gender;
female|70.0
male|61.6666666666667
各クラスの性別ごとに集約 (group byのあとに2つ以上つなげる)
sqlite> select class, gender, avg(score) from exam_score group by gender, class;
A|female|50.0
B|female|70.0
C|female|90.0
A|male|30.0
B|male|80.0
C|male|75.0
行の更新 (update)
行データの一部分を更新
sqlite> insert into staff values('4', 'Atsushi Sasaki', 'aaa@bbb.ccc');
sqlite> select * from staff where id = '4';
4|Atsushi Sasaki|aaa@bbb.ccc
sqlite> update staff set email = 'zzz@xxx.xxx' where id = '4';
sqlite> select * from staff where id = '4';
4|Atsushi Sasaki|zzz@xxx.xxx
sqlite>
setのあとには、複数の値の設定をカンマでつなげて記述できる。
テーブルの削除
drop table if exists (テーブル名);
SQLの検索結果を使って、テーブルにデータを挿入
insert select (テーブル名) (select文)
データベースを使ってみよう
準備 SQLiteのインストール
コマンドラインで扱えるSQLiteのバイナリをダウンロード
- http://www.sqlite.org/download.html
- Windows版 http://www.sqlite.org/sqlite-3_6_1.zip
- sqlite3.exeが含まれる。sqlite3.exeを、環境変数PATHから見える位置に置くと、コマンドプロンプトやcygwinから使えるようになります。
- PATH変数はコマンドプロンプトからは、PATHコマンド. cygwinからはecho $PATHで確認できます。
SQLiteを起動する
sqlite3 (作成したいデータベースファイル名、あるいは既存のデータベースファイル名)
で起動できます。ファイル名を入力しないとデータベースはメモリ上に作成されるだけで、sqlite終了後にはデータは消えてしまいます。
> sqlite3 staff.db
SQLite version 3.5.1
Enter ".help" for instructions
sqlite>
sqliteの使い方を調べる
sqlite> .help
.bail ON|OFF Stop after hitting an error. Default OFF
.databases List names and files of attached databases
.dump ?TABLE? ... Dump the database in an SQL text format
.echo ON|OFF Turn command echo on or off
.exit Exit this program
.explain ON|OFF Turn output mode suitable for EXPLAIN on or off.
.header(s) ON|OFF Turn display of headers on or off
.help Show this message
.import FILE TABLE Import data from FILE into TABLE
.indices TABLE Show names of all indices on TABLE
.mode MODE ?TABLE? Set output mode where MODE is one of:
csv Comma-separated values
column Left-aligned columns. (See .width)
html HTML <table> code
insert SQL insert statements for TABLE
line One value per line
list Values delimited by .separator string
tabs Tab-separated values
tcl TCL list elements
.nullvalue STRING Print STRING in place of NULL values
.output FILENAME Send output to FILENAME
.output stdout Send output to the screen
.prompt MAIN CONTINUE Replace the standard prompts
.quit Exit this program
.read FILENAME Execute SQL in FILENAME
.schema ?TABLE? Show the CREATE statements
.separator STRING Change separator used by output mode and .import
.show Show the current values for various settings
.tables ?PATTERN? List names of tables matching a LIKE pattern
.timeout MS Try opening locked tables for MS milliseconds
.width NUM NUM ... Set column widths for "column" mode
SQL文を入力
sqlite> create table staff (id integer primary key not null, name text, email text);
sqltie> insert into staff values('1', 'Taro L. Saito', 'xxx@xxx.xxxxxx');
sqlite>
データベースの内容を確認
sqlite> select * from staff;
1|Taro L. Saito|xxx@xxx.xxxxxx
sqlite>
sqliteを終了する
sqlite> .quit
テーブルを確認
sqlite> .tables
staff
sqlite>
テーブルのスキーマ(データ構造)を確認
sqlite> .schema
CREATE TABLE staff (id integer primary key not null, name text, email text);
sqlite>
ファイルに書かれているSQLを実行
sqlite> .read score.sql
score.sql
drop table if exists exam_score;
create table exam_score (
id integer primary key autoincrement not null,
gender text,
class text,
score integer
);
drop table if exists exam_score;
は、exam_scoreテーブルが存在していたら、テーブルを消す、という意味。
tab区切りデータをテーブルに読み込む
score.tab
1 female A 50
2 male A 30
3 male B 80
4 female B 70
5 female C 90
6 male C 75
読み込む
.separatorでタブを区切り文字に指定。.importでテーブルに、ファイルからデータを読み込む。
.separator \t
.import score.tab exam_score
SQLite JDBC
SQLite JDBC (http://www.xerial.org/trac/Xerial/wiki/SQLiteJDBC )は、JavaプログラムからSQLiteデータベースシステムを使うためのツールです。
SQLite JDBCライブリのダウンロード
ダウンロードサイト から最新版のライブラリ(jarファイル)をダウンロードします。2008年8月現在の最新版は、
http://www.xerial.org/maven/repository/artifact/org/xerial/sqlite-jdbc/3.6.0/sqlite-jdbc-3.6.0.jar
です。これを、プロジェクト内に保存します。
クラスパスの設定
外部ライブラリを使用するためには、jarファイルをclasspathに追加する必要があります。プロジェクトフォルダ内の sqlite-jdbc-3.6.0.jar ファイルの上で右クリック - Build Path - Add to Build Pathを選択し、ライブラリをクラスパスに追加します。
追加後
SQLiteAccessSample.java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class SQLiteAccessSample
{
public static void main(String[] args) throws ClassNotFoundException
{
// SQLiteのJDBCドライバの読み込み
// SQLite特融のコードはこの1行のみ
Class.forName("org.sqlite.JDBC");
Connection connection = null;
try
{
// sample.dbというSQLiteデータベースファイルに接続
connection = DriverManager.getConnection("jdbc:sqlite:sample.db");
// SQL文を準備
Statement statement = connection.createStatement();
// 検索・更新がなかなか終わらないときは30秒で打ち切る設定
statement.setQueryTimeout(30);
// personというテーブルが存在していたら消す
statement.executeUpdate("drop table if exists person");
// 新しいpersonテーブルを作成
statement.executeUpdate("create table person (id integer, name string)");
// personテーブルにデータを追加
statement.executeUpdate("insert into person values(1, 'leo')");
statement.executeUpdate("insert into person values(2, 'yui')");
// personテーブルの全ての行を取得するクエリ。ResultSetは検索結果のiteratorの役割
ResultSet rs = statement.executeQuery("select * from person");
while(rs.next())
{
// 検索結果の各行のデータを取り出す
System.out.println("name = " + rs.getString("name"));
System.out.println("id = " + rs.getInt("id"));
}
}
catch(SQLException e)
{
// if the error message is "out of memory",
// it probably means no database file is found
System.err.println(e.getMessage());
}
finally
{
try
{
// connectionを忘れずに閉じる
if(connection != null)
connection.close();
}
catch(SQLException e)
{
// connection close failed.
System.err.println(e);
}
}
}
}
レポート課題9 データベースでピボット演算 (集計)
1からNまでの数(int)を、Collectionに追加するとき、addの効率、contains(特定の要素が含まれているかどうかを調べる)、Collections.sort の性能を調べたい。調べた結果はExcel、あるいはOpenOffice Calkなどを用いてグラフにし、結果について考察せよ。
提出方法
- ソースコードが含まれたJARファイルを1つ
- グラフ・考察を含む文章ファイル(Word, PDF (OpenOffice Writerで作成)などの形式)を1つ
実験対象のCollection
- add, containsの性能を
- LinkedList, ArrayList, TreeSet, HashSetそれぞれについて調べる
- containsは、ランダムに選んだ10個の数(1からNまでの範囲)a1, a2, ..., a10 を決めておき、それぞれのデータ構造について、contains(a1), contains(a2), ...の10個の検索に要する実行時間を計測する。
- LinkedList, ArrayListについては、Collections.sortの性能も調べる
Nを動かす: scalabilityの調査
- サイズNは、100から、10倍ずつ、10万程度まで増やしていくとする。
- 実験結果の信頼性を良くするために、同じ実験は5回繰り返して速度(計算時間)の平均をとる。平均をとる操作はJavaで書くのではなく、データベースに計算させるため、5回の試行(trial)の計算速度をすべてデータベースに記録することになる。
データベースで集計
- SQLite JDBCを用いて、以下形式のテーブルを持つデータベースを作成する。
create table experiment (
operation text,
size integer,
structure text,
trial integer,
time real
)
- これはMicrosoft Excelでピボットテーブルを用いて集計するのと一緒
テーブルのサンプル
- 注意: ここに書かれている計測時間は適当に埋めたもので、実測値ではありません。
- 以下は、同じ実験を3回繰り返したときの例
- 集計例
| operation | size | structure | trial | time |
|---|---|---|---|---|
| add | 1000 | LinkedList | 1 | 0.10 |
| add | 1000 | LinkedList | 2 | 0.12 |
| add | 1000 | LinkedList | 3 | 0.04 |
| add | 1000 | ArrayList | 1 | 0.03 |
| add | 1000 | ArrayList | 2 | 0.01 |
| add | 1000 | ArrayList | 3 | 0.08 |
| add | 10000 | LinkedList | 1 | 1.0 |
| add | 10000 | LinkedList | 2 | 1.2 |
| add | 10000 | LinkedList | 3 | 1.4 |
| add | 10000 | ArrayList | 1 | 0.3 |
| add | 10000 | ArrayList | 2 | 0.1 |
| add | 10000 | ArrayList | 3 | 0.8 |
| contains | 1000 | LinkedList | 1 | 0.2 |
| contains | 1000 | LinkedList | 2 | 0.2 |
| ... | ||||
| operation | size | structure | avg(time) | |
|---|---|---|---|---|
| add | 1000 | LinkedList | 0.10 | |
| add | 10000 | LinkedList | 1.2 | |
| add | 1000 | HashSet | 1.2 | |
| add | 10000 | HashSet | 3.0 | |
| add | 1000 | TreeSet | 1.2 | |
| add | 10000 | TreeSet | 3.0 | |
| ... | ||||
| contains | 1000 | LinkedList | 0.2 | |
| contains | 10000 | LinkdList | 0.2 | |
| contains | 1000 | ArrayList | 0.2 | |
| contains | 10000 | ArrayList | 0.2 | |
| ... | ||||
| sort | 1000 | LinkedList | 0.2 | |
| sort | 1000 | ArrayList | 0.2 | |
| ... | ||||
時間計測の仕方
StopWatchクラス(後述)を使う。
StopWatch stopWatch = new StopWatch();
double elapsedTime = 0;
stopWatch.reset(); //計測開始
// 何か演算を行う
elapsedTime = stopWatch.getElapsedTime(); // 秒(sec.)単位で実行時間が計測できる
// データベースにelapsedTimeを記録
stopWatch.reset(); //計測開始(StopWatchをリセット)
// 何か演算を行う
elapsedTime = stopWatch.getElapsedTime(); // 計測
// データベースにelapsedTimeを記録
...
StopWatch.java
public class StopWatch
{
private long initialSystemTIme;
private long lastSystemTime;
public StopWatch()
{
reset();
}
/**
* Gets the elapsed time since this instance is created in seconds.
*
* @return the elapsed time in seconds.
*/
public double getElapsedTime()
{
lastSystemTime = System.currentTimeMillis();
long diff = lastSystemTime - initialSystemTIme;
return diff / 1000.0;
}
/**
* Gets the interval time since the last call of
* {@link StopWatch#getEleapsedTime()} or
* {@link StopWatch#getIntervalTime()}
*
* @return the interval time in seconds
*/
public double getIntervalTime()
{
long now = System.currentTimeMillis();
long diff = now - lastSystemTime;
lastSystemTime = now;
return diff / 1000.0;
}
/**
* Reset the stop watch. The subsequent calls to
* {@link StopWatch#getEleapsedTime()} or
* {@link StopWatch#getIntervalTime()} will measure the time intervals
* beginning from this method call.
*/
public void reset()
{
initialSystemTIme = System.currentTimeMillis();
lastSystemTime = initialSystemTIme;
}
}
チャレンジ課題4 Twitter Clientを作ろう
注意
わりと実装に時間がかかると思います。
Twitterって?
Twitter (http://twitter.com) は、Web上で皆が140文字以内のメッセージを残していく、というごく単純なサービスです。著名人も多く参加していて、そういう人たちの会話も見ることができます。
課題
Twitterのpublic timelineのXMLデータを取得し、会話の内容、発言した人、発言日時などの情報を抜き出し、それをデータベースに保存する。そして、そのデータベースの内容を手軽に閲覧するためのGUIアプリケーションを作成しなさい。
- 外部ライブラリを使用した場合は、EclipseでRunnable JARを作成すること
- その場合、ソースコードはまた別途にJARでまとめて送ること
Twitter Web API
Public Timeline (最新の、世界中のみなの一言を取得する)
- http://twitter.com/statuses/public_timeline.xml にアクセスすると、以下のようなXMLデータを取得できます。
- Twitter側にアクセス制限されてしまわないように、2-3分に1アクセス程度に抑えるとよいでしょう。
<statuses type="array">
<status>
<created_at>Fri Aug 08 03:09:20 +0000 2008</created_at>
<id>881092392</id>
<text>Free breakfast sandwich at Panera Bread, if you bring in a donation for the mid ohio foodbank. Story on The Dispatch website.</text>
<source>twhirl</source>
<truncated>false</truncated>
<in_reply_to_status_id />
<in_reply_to_user_id />
<favorited>false</favorited>
<user>
<id>14895949</id>
<name>taz288</name>
<screen_name>taz288</screen_name>
<description>first year court reporting student</description>
<location>ohio</location>
<profile_image_url>http://s3.amazonaws.com/twitter_production/profile_images/55306912/scan0007_normal.jpg</profile_image_url>
<url></url>
<protected>false</protected>
<followers_count>28</followers_count>
</user>
</status><status>
<created_at>Fri Aug 08 03:09:20 +0000 2008</created_at>
<id>881092390</id>
<text>I just updated my Squidoo page: Designer Shoes From Copper Cockeral / http://tinyurl.com/6zh4px</text>
<source>squidoo</source>
<truncated>false</truncated>
<in_reply_to_status_id />
<in_reply_to_user_id />
<favorited>false</favorited>
<user>
<id>15423903</id>
<name>EelKat</name>
<screen_name>EelKat</screen_name>
<description>author, artist, CosPlayer, costumer, owned by 14 cats, the Bride of Lord Sesshomaru :)</description>
<location>Maine, USA</location>
<profile_image_url>http://s3.amazonaws.com/twitter_production/profile_images/56621126/2336767218_6259159819_m_normal.jpg</profile_image_url>
<url>http://www.squidoo.com/LordSesshomaruHistory</url>
<protected>false</protected>
<followers_count>180</followers_count>
</user>
</status>
....
</statuses>
XMLデータの解析
public_timelineのXMLデータは1行に1タグという形なので、Readerで読み取って、正規表現で解釈するのでも十分ですが、正確にデータを取得したい場合は、
- XML Pull Parser http://www.xmlpull.org/ を使うとよいでしょう。
定期的にTweetsを更新
public timelineから定期的にデータを取得するには、スレッドによる並列処理が必要になるかもしれません。
- Threadによる並列処理の解説 http://java.sun.com/docs/books/tutorial/essential/concurrency/index.html
定期的にデータを取得する場合、例えば以下のように書きます。
class PeriodicalUpdator implements Runnable
{
StopWatch stopWatch = new StopWatch();
public void run() {
try {
for(;;) {
double elapsedTime = stopWatch.getElapsedTime();
if(elapsedTime >= 3 * 60) {
// 3分経過
loadPublicTimeline();
stopWatch.reset();
}
else
{
Thread.sleep((3 * 60 - elapsedTime) * 1000); // 3分経過するまで休む
}
}
catch (InterruptedException e) {
// 外部から終了を促された
}
}
public void loadPublicTimeline()
{
// timeline を取得 ...
}
}
PeriodicalUpdator p = new PeriodicalUpdator();
// スレッドを使って並列実行
Threat t = new Thread(p).start();
// GUI終了時
t.interrupt();
t.join();

