VM:java1.4.2
OS:WindowsXp
マルチスレッドのプログラムで、一つのファイルにテキストの出力を行うところで、うまくいかないところがあります。
<ソースファイル>
import java.io.*;
import java.util.*;
import java.text.*;
public class ThreadIppai {
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
Thread thread = new ThreadHontai();
thread.start();
}
}
}
class ThreadHontai extends Thread {
public void run() {
try {
for (int i = 0; i < 500; i++) {
BufferedWriter bw = new BufferedWriter(new FileWriter(
"D:\\out.log", true));
String msg = (String) Values.ht.get(String.valueOf((int) (Math.random() * 10)).substring(0, 1)) + "\n";
bw.write(msg, 0, msg.length());
bw.flush();
bw.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Values {
public static Hashtable ht = new Hashtable();
static {
//ht.put("0", "0000000000");
//ht.put("1", "1111111111");
//ht.put("2", "2222222222");
//ht.put("3", "3333333333");
//ht.put("4", "4444444444");
//ht.put("5", "5555555555");
//ht.put("6", "6666666666");
//ht.put("7", "7777777777");
//ht.put("8", "8888888888");
//ht.put("9", "9999999999");
ht.put("0", "0");
ht.put("1", "11");
ht.put("2", "222");
ht.put("3", "3333");
ht.put("4", "44444");
ht.put("5", "555555");
ht.put("6", "6666666");
ht.put("7", "77777777");
ht.put("8", "888888888");
ht.put("9", "9999999999");
}
}
<問題点>
ファイルに出力された結果をみると、テキストの一部が欠けていたり、改行がされない行があったりします。
おそらく、同期処理を加えてないからだとは思うのですが・・・。(質問に続く)
<質問1>
テキストの一部が欠けたり、改行されない行が発生する原因はなぜでしょうか?
たとえば、「0」と「11」を出力するとき、同時に複数のスレッドが書き込んだ場合、「101」となるのは、なんとなく分かります。
しかし、これが「01」のように、出力されるべき文字が出力されないという現象が発生してます。
<質問2>
htにputする値の文字列長が、すべて異なっていますが、これをコメントアウトされている行のように、すべて同じ文字列長に
した場合、上記の問題は発生しなくなります。
この原因はなんでしょうか?
<質問3>
この問題を、ThreadHontaiクラスのfor文の中だけの変更で解決することは可能でしょうか?(極力手を加えずに)
synchronizedブロックの追加でいけるのかと思いましたが、試行錯誤の結果うまくいきませんでした。
以上、よろしくお願いします。
A 回答 (4件)
- 最新から表示
- 回答順に表示
No.4
- 回答日時:
素人です。
勘ですが・・・。
■質問(1)(2)について
>これが「01」のように、
>出力されるべき文字が出力されないという現象
「競合」が起こっている以上、
とりあえずは、(実装に応じた)どんな不具合も起こりうる気がします。
が、たとえば仮に、
「ファイル追記処理」の実装が、
(i)(現在の"ファイル書き込み位置"から)文字列を書き込む
(ii)"ファイル書き込み位置"(ファイル末尾)を更新する
という"2段構え"なっていたとします。
そして今、2つのスレッドA,Bが同時に、
同じ"ファイル書き込み位置"pから、
スレッドAは"11\n"を、スレッドBは"44444\n"を書き込もうとしているとします。
ここで、
ア:(B-i)(A-i)(A-ii)(B-ii)の順で処理が発生すれば、
最終的にファイルに追記される文字列は"11\n44\n"。
イ:(B-i)(A-i)(B-ii)(A-ii)の順で処理が発生すれば、
最終的にファイルに追記される文字列は"11\n"。(Bによる書き込みが完全につぶれる)
ウ:(A-i)(B-i)(B-ii)(A-ii)の順で処理が発生すれば、
最終的にファイルに追記される文字列は"44\n"。
エ:(A-i)(B-i)(A-ii)(B-ii)の順で処理が発生すれば、
最終的にファイルに追記される文字列は"44444\n"。(Aによる書き込みが完全につぶれる)
イとエは、一見、スレッド競合が発生してないように見えます。
■質問(3)について
synchronized(ThreadHontai.class){
・・・
}
などとすればよいのでは?
No.3
- 回答日時:
よくわからないですが、1つのスレッドの中で500個もBufferedWriterクラスとFileWriterクラスをforループの中で生成しては破棄し、を繰り返しているので、スレッド処理とあいまってメモリ管理がおかしくなってるのかな。
。って気がしますね。※だから文字列長が同じならBufferedで出力される長さが同じなので大丈夫なってる?
普通は、for文の中だけってのは、難しいですが、外に1個クラスを作れば、限りなくfor文だけの修正にみえなくはないようにはできます(笑)
import java.io.*;
import java.util.*;
import java.text.*;
public class ThreadIppai {
public static void main(String[] args) {
OutLog.init();
for (int i = 0; i < 100; i++) {
Thread thread = new ThreadHontai();
thread.start();
}
OutLog.dispose();
}
}
class ThreadHontai extends Thread {
public void run() {
try {
for (int i = 0; i < 500; i++) {
OutLog.prn();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Values {
public static Hashtable ht = new Hashtable();
static {
// ht.put("0", "0000000000");
// ht.put("1", "1111111111");
// ht.put("2", "2222222222");
// ht.put("3", "3333333333");
// ht.put("4", "4444444444");
// ht.put("5", "5555555555");
// ht.put("6", "6666666666");
// ht.put("7", "7777777777");
// ht.put("8", "8888888888");
// ht.put("9", "9999999999");
ht.put("0", "0");
ht.put("1", "11");
ht.put("2", "222");
ht.put("3", "3333");
ht.put("4", "44444");
ht.put("5", "555555");
ht.put("6", "6666666");
ht.put("7", "77777777");
ht.put("8", "888888888");
ht.put("9", "9999999999");
}
}
public class OutLog{
private static PrintWiter prn = null;
private static boolean init_end = false;
private static String lock = new String("lock");
public static void init(){
synchronized(lock){
if(init_end){
return;
}
prn = new PrintWriter(new BufferedWriter(new FileWriter("D:\\out.log", true)));
init_end = true;
}
return;
}
public static void dispose(){
synchronized(lock){
if(!init_end){
return;
}
prn.close();
init_end = false;
}
}
public static void prn(){
String msg = (String) Values.ht.get(String.valueOf((int) (Math.random() * 10)).substring(0, 1)) + "\n";
prn.write(msg);
prn.flush();
}
}
No.2
- 回答日時:
No.1
- 回答日時:
Javaは初心者なので、まるで分かりません。
試しに <ソースファイル> を自分の環境で実行してみたところ、正常に実行されました。実行環境の問題でしょうかね。(ちなみに自分の環境)
OS: Windows XP Home SP2
環境: cygwin 1.5.24 (1.5.24(0.156/4/2) 2007-01-23 18:50 i686 Cygwin)
コンパイラ: gcj (GCC) 3.4.4 (cygming special, gdc 0.12, using dmd 0.125)
Copyright (C) 2004 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
なお、インスタンスを 1 つのスレッドで実行するすべてのクラスでは、Runnable インタフェースを実装する必要があるらしいので、
synchronizedブロックを追加すると、以下の様なプログラムになるのではないでしょうか。
import java.io.*;
import java.util.*;
import java.text.*;
class Share {
public synchronized void run_module() {
try {
for (int i = 0; i < 500; i++) {
BufferedWriter bw = new BufferedWriter(new FileWriter(
"D:\\out.log", true));
String msg = (String) Values.ht.get(String.valueOf((int) (Math.random() * 10)).substring(0, 1)) + "\n";
bw.write(msg, 0, msg.length());
bw.flush();
bw.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Sync implements Runnable {
Share var;
Sync(Share obj) {
var = obj;
}
public void run() {
var.run_module();
}
}
public class ThreadIppai {
public static void main(String[] args) {
Share obj = new Share();
Thread[] thres = new Thread[100];
for (int i = 0; i < 100; i++) {
thres[i] = new Thread(new Sync(obj));
thres[i].start();
}
}
}
class Values {
public static Hashtable ht = new Hashtable();
static {
// ht.put("0", "0000000000");
// ht.put("1", "1111111111");
// ht.put("2", "2222222222");
// ht.put("3", "3333333333");
// ht.put("4", "4444444444");
// ht.put("5", "5555555555");
// ht.put("6", "6666666666");
// ht.put("7", "7777777777");
// ht.put("8", "8888888888");
// ht.put("9", "9999999999");
ht.put("0", "0");
ht.put("1", "11");
ht.put("2", "222");
ht.put("3", "3333");
ht.put("4", "44444");
ht.put("5", "555555");
ht.put("6", "6666666");
ht.put("7", "77777777");
ht.put("8", "888888888");
ht.put("9", "9999999999");
}
}
回答ありがとうございます。
>自分の環境で実行してみたところ、正常に実行されました。実行環境の問題でしょうかね。
動作テストありがとうございます。
正常に実行されるとは、環境の問題なのかもしれませんね。
ただ、もともとこのプログラムは同期化されないハズだと思って作ったものなので、正常に実行されるとは意外でした。
私は、OSはXPとビスタ、その他の環境は全て同じで3台のPCで試験しましたが、どれも結果は同じでした。(ただ、マシン性能によってなのか、高速なマシンほど出力が乱れました。)
また、せっかく修正ソースを出していただいたのに、申し訳ないのですが、質問3については「ThreadHontaiクラスのfor文の中だけの変更」という箇所が実は重要です。
もし仮に、「ThreadHontaiクラスのfor文の中だけの変更」だけでは技術的に不可能ということであれば、その事実が分かるだけでも構わないと考えています。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- Java java 入力 3 4 3 出力 ABC DEFG HIJ このようなプログラムの書き方を教えてくだ 2 2022/07/15 14:18
- 数学 熱核 Ht とガウス関数 Gσ との関係性について 1 2022/07/28 16:42
- ホームページ作成・プログラミング Adobe DreamweaverでのサイトのFTP 2 2023/03/05 11:55
- Ruby 【JAVA】数字をひし形に出力するプログラムについて 2 2022/07/11 23:32
- Java Java プログラム public class Main { public static void 3 2023/08/10 23:46
- 英語 私が作った英語の文の添削をしてください 3 2022/11/18 13:34
- Java javaでのプログラム(配列)について質問です. 2 2022/10/14 22:27
- 英語 一般のyou, we,theyなどの答え方がわかりません。 2 2022/10/29 15:22
- Java Java 配列<選挙> 4 2023/07/31 15:07
- 英語 but の後の形 2 2023/03/04 09:22
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
C# 矢印キーの取得
-
正規表現
-
Visual C♯ フォーム間での値の...
-
アプレットでマウスイベントが...
-
コンボボックスを使う時の警告
-
エンターキーを押すとOKボタン...
-
C# 半角カナの文字化けについて
-
ボタンの複数割り当てについて
-
配列の受け渡し
-
大量のデータを読み込んで表示...
-
JavaプログラムのUML化
-
パスがとおらない・・・
-
C#でのWNetAddConnection3の使...
-
Thread.sleepのInterruptedExce...
-
Processingでマウスクリックで...
-
キーリピート?(Javaプログラミ...
-
フェードアウト時にぶちっと画...
-
C#で、あるクラスのメンバーす...
-
長方形をドラッグするJavaアッ...
-
Javaの課題について質問です。...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
大量のデータを読み込んで表示...
-
ボタンの複数割り当てについて
-
C#で、あるクラスのメンバーす...
-
Junitテストでvoid戻り値メッソ...
-
C#でのWNetAddConnection3の使...
-
C# 矢印キーの取得
-
Thread.sleepのInterruptedExce...
-
unityでのC++エラーの原因がわ...
-
C#で別のFormへ複数の値を返そ...
-
C# visibleプロパティをfalseに...
-
エンターキーを押すとOKボタン...
-
Processingでマウスクリックで...
-
C#でキーイベントが発生しない...
-
[C#.net]スレッド化された別フ...
-
C# DataGridView列カスタマイズ
-
【C#】ソースコードをファイル...
-
LVM_SETITEMSTATEでListViewの...
-
GetDIBits関数の使い方について
-
未割り当てのローカル変数
-
C# MouseHoverを何度も呼ぶには
おすすめ情報