• 締切済み

マルチスレッドプログラムからのファイル出力

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ブロックの追加でいけるのかと思いましたが、試行錯誤の結果うまくいきませんでした。 以上、よろしくお願いします。

  • Java
  • 回答数4
  • ありがとう数2

みんなの回答

  • kacchann
  • ベストアンサー率58% (347/594)
回答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){ ・・・ } などとすればよいのでは?

  • PED02744
  • ベストアンサー率40% (157/390)
回答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(); } }

  • maku_x
  • ベストアンサー率44% (164/371)
回答No.2
  • maku_x
  • ベストアンサー率44% (164/371)
回答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"); } }

btom0106
質問者

お礼

回答ありがとうございます。 >自分の環境で実行してみたところ、正常に実行されました。実行環境の問題でしょうかね。 動作テストありがとうございます。 正常に実行されるとは、環境の問題なのかもしれませんね。 ただ、もともとこのプログラムは同期化されないハズだと思って作ったものなので、正常に実行されるとは意外でした。 私は、OSはXPとビスタ、その他の環境は全て同じで3台のPCで試験しましたが、どれも結果は同じでした。(ただ、マシン性能によってなのか、高速なマシンほど出力が乱れました。) また、せっかく修正ソースを出していただいたのに、申し訳ないのですが、質問3については「ThreadHontaiクラスのfor文の中だけの変更」という箇所が実は重要です。 もし仮に、「ThreadHontaiクラスのfor文の中だけの変更」だけでは技術的に不可能ということであれば、その事実が分かるだけでも構わないと考えています。

関連するQ&A

  • Javaのプログラムについて教えてください!

    BufferedWriterを使ってコマンドプロンプトから文字を入力して実行時に与えたパスのテキストに書き込んでいくプログラムをコーディングしてみたのですがなかなか上手くいきません。添削をお願いします。 import java.io.*; class BW{ public static void main(String[] args){ try{ File fl = new File("args[0]"); FileWriter fw = new FileWriter(fl); BufferedWriter bw = new BufferedWriter(fw); for(int i = 0;args[i]==null;i++){ bw.write(args[i]); bw.newLine(); } bw.flush(); bw.close(); }catch(IOException e){ e.printStackTrace(); } } }

    • ベストアンサー
    • Java
  • 文字化けをなおしたいです。

    下記のようなプログラムで、文字をテキストに出力しているのですが、文字化けしてしまう文字があります。 どのように対処すればよろしいのか教えていただけないでしょうか。(ソース中の(文字化けのする文字)には、例えば、はしごたかの高などです。) OSはhp-uxです。 ======== class test { public static void main(String args[]) { try { BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("out.txt",true), "Shift_JIS")); String msg=""; msg="(文字化けのする文字)"; bw.write(msg); bw.flush(); bw.close(); } catch (IOException e) { System.err.println(e); } } }

    • ベストアンサー
    • Java
  • Double,Longをファイルに書き込み

    以下のように、Double,Longをファイルに書き込もうとした場合、どのようにキャストをすればよろしいのでしょうか? (String)でString型への変換や Integer.parseInt等を試したのですが、できませんでした。よろしくお願いします。 import java.io.*; class test { public static void main(String[] args) { double dNumber = 3.45; long lNumber = 12345; BufferedWriter bw = new BufferedWriter(new FileWriter("test.txt")); bw.write(dNumber); bw.write(lNumber); } //end main } //end class

    • ベストアンサー
    • Java
  • file操作時のエラー

    現在、Eclipseを開発環境としたJavaでのファイル操作の勉強をしているのですがエラーが出て困っています。 さっそくですがソースを張らせていただきます。 import java.io.*; import java.text.*; import java.util.*; public class Hallo { public static void main(String[] args) { File file = new File("C:\\Tomcat 6.0\\webapps\\blog","coment.txt"); FileOutputStream fos = new FileOutputStream(file); OutputStreamWriter osw = new OutputStreamWriter(fos , "Windows-31J"); BufferedWriter bw = new BufferedWriter(osw); String msg = "abc"; bw.write(msg); bw.close(); osw.close(); fos.close(); } } この様なソースを書いたところEclipseのエディタ上で 処理されない例外の型 FileNotFoundException 処理されない例外の型 UnsupportedEncodingException 処理されない例外の型 IOException などのエラーがMain分中のほとんどの行で出てしまいます。 しかし、このプログラムをサーブレットとして実装して動作させた場合には特に問題なく動きます。 個人的には開発環境からくるエラーか、それとも別の何か根本的な所で自分が間違っているのではないかと考えています。 みなさんのお返事お待ちしております。

    • ベストアンサー
    • Java
  • CSVファイルから特定の文字列を含む行を出力

    CSVファイルを読み込み、特定の文字列を含む行を出力するプログラムを作成しています。 出力メソッドを作り、呼び出したいのですがうまくいきません。 コンパイルは通りますが出力したファイルには何も書き込まれていない状況です。 ヒントだけでも頂けると幸いです。 よろしくお願いします。 import java.io.*; import java.util.*; public class Test{ static String[] data; static BufferedWriter writer = null; public static void main(String[] args) throws Exception { String line; BufferedReader reader = null; reader = new BufferedReader(new FileReader("Week.csv")); writer = new BufferedWriter(new FileWriter("New.csv")); while (reader.ready()) { line = reader.readLine(); if (line.contains("氏名")) { create(line); } else if (line.contains("月")) { create(line); } else if (line.contains("水")) { create(line); } else if (line.contains("金")) { create(line); } } } //メソッド static void create(String line) throws Exception { data = line.split(","); for (int i = 0; i < data.length; i++) { writer.print(data[i]); writer.println(); } } }

    • ベストアンサー
    • Java
  • インスタンスの作成について

    下記の方法にてHashtableのインスタンスを作成しようと思ったのですが has1.put("key",1); の場所でNullpointerException が発生します。 has1がNullになってしまうのですが 下記のような手法でインスタンスは作れないのでしょうか。 public class InstanceCreate { Hashtable has1; Hashtable has2; Hashtable has3; public static void main(String[] args) { InstanceCreate instance = new InstanceCreate(); instance.exe(); } public void exe() { Hashtable hash[] = {has1,has2,has3}; for(int i=0; i<hash.length;i++){ hash[i] = new Hashtable(); hash[i].put("key", "value"); } has1.put("key",1); } }

    • ベストアンサー
    • Java
  • テキストファイルの日付表示

    JAVA初心者なのですが、テキストファイルに出力する時にファイル名を現在日付にしたいのですが、どうすればよいでしょうか?(yyyymmdd.txtを20070316.txtというふうにしたい) 後、@SuppressWarnings("unchecked")と記述しているのにコンパイルで-Xlint:unchecked.警告が出てきてします!どなたかご教授ください!!ちなみにJAVA1.5 import java.io.File; import java.io.FileWriter; import java.io.BufferedWriter; import java.io.IOException; import java.util.ArrayList; public class Writedown { @SuppressWarnings("unchecked")/*コンパイラの警告を抑制する */ public static void fileWrite(ArrayList<ArrayList> WriteArrayList) { try { File txt = new File("yyyymmdd.txt"); /*出力するファイル*/ BufferedWriter bw = new BufferedWriter(new FileWriter(txt));/* try { for(int i = 0; i < WriteArrayList.size(); i++){ ArrayList list = WriteArrayList.get(i); if(i == 0){ bw.write((list.size() - 3) + "科目成績表\r\n"); } for(int j = 0; j < list.size(); j++){ if(j > 0){ bw.write("\t"); } if(list.get(j).toString().equals("名前")){ list.remove(j); list.add(j,"名前"); } bw.write(list.get(j).toString()); System.out.print(list.get(j) + "\t"); } bw.write("\r\n"); bw.flush(); System.out.println(""); } } catch (IOException e) { System.out.println("ファイル書き込みエラー"); } bw.close(); } catch (IOException e) { System.out.println("ファイルオープンエラー"); } } }

  • 1レコードを2レコードに分けて出力したい

    このソースでテキスト入出力しますと 連続番号を付加して入出力できます。 さらに 下記のようにしたいのです。 例    1レコード目 1,テレビ,カメラ,クーラー,扇風機,パソコン 2レコード目 2,時計,マッサーサージ機,HDD,USB,扇風機 入出力後に(先頭に表・裏の"文字"を付加) 1レコード目   表, 1, テレビ, カメラ, クーラー 2レコード目   裏, 扇風機, パソコン 3レコード目   表, 2, 時計, マッサーサージ機, HDD 4レコード目   裏, USB, 扇風機 としたいのです。 1レコード分のデータを表と裏に分けて2レコードずつ出力するのが 目的になります。(先頭に表・裏の文字を付加) ですので1000レコードでしたらテキスト入出力後2000レコードになります。 下記のような連続番号を付加して入出力する場合 どの箇所を見直す必要があるのでしょうか?? どうぞよろしくお願いします。 package hoge.hoge.com; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; public class HelloWorld { private static String inFileName = "c:\\in.txt"; private static String outFileName = "c:\\out.txt"; /* Shift_JIS, ISP-2022-JP, EUC-JP Windows-31J */ /* 出力時に JISAutoDetect の指定は不可 */ private static String outFileCoding = "Shift_JIS"; public static void main(String[] args) { try{ String line; int i = 0; BufferedReader buf = new BufferedReader( new InputStreamReader( new FileInputStream( inFileName ), "JISAutoDetect" ) ); /* ファイルが存在しない場合には新たに作成.存在する場合には上書き */ BufferedWriter outFile = new BufferedWriter( new OutputStreamWriter( new FileOutputStream( outFileName ), outFileCoding ) ); while( (line=buf.readLine()) != null ) { StringBuffer out = new StringBuffer(); i++; out.append(Integer.toString(i)); out.append(":"); out.append(line); outFile.write(out.toString()); outFile.newLine(); System.out.println(out); } buf.close(); outFile.flush(); outFile.close(); } catch(IOException e) { e.printStackTrace(); System.exit(1); } } }

    • ベストアンサー
    • Java
  • テキストファイルを読み込んで別のファイルに出力するプログラム

    テキストファイルを読み込んで分類し、別のファイルに出力するプログラムを作ろうと思っています。 大まかに作ってみたのですが、そもそもこれであっているのか確認してください。 import java.io.*; public class Test { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader ( new FileReader("kazu.txt")); int note, b1 = 0, b2 = 0, b3 = 0, b4 = 0; String input; while((input = br.readLine()) != null) { note = input.toint(); { //読み込んだString型のデータinputをint型に変換してnoteに代入 /*条件分岐 :noteの値が80以上ならb1に1を加える  条件分岐 :上記以外でnoteの値が70以上ならb2に1を加える  条件分岐 :上記以外でnoteの値が60以上ならb3に1を加える  条件分岐 :上記以外ならb4に1を加える*/ } //bwに文字リテラル"80以上"を書き込む //bwにTAB記号を1文字書き込む //bwにint型の変数b1の値の文字列表現を書き込む //bwに改行記号を書き込む //同様に"70以上80未満" "60以上70未満" "60未満"についてbwに必要な書き込みを行う bw.flush(); br.close(); bw.close(); } } 最近プログラミングを学び始め、急にいろんなことをやり出してしまったので何がなんだかわからなくなっています。 途中にある、String型のデータinputをint型に変換してnoteに代入というのは、どのようにすればいいのでしょうか? またその後にある条件分岐では、if note >=80 b1=b1+1; else if note>=70 ...とやっていけばいいのでしょうか? また、bwへの書き込みというのが(別のプログラムでも)うまくできません。 長々と書いてしまいましたが、よろしくお願いします。

  • JavaのTCPソケット通信プログラムについて

    初めてJavaでTCPソケット通信を書いてみたのですが、質問です。ソケット通信をサーバーとクライアント側で確立した後、メッセージの送受信をやるとても簡素なプログラムを作成しました。一回目の送受信をするだけだと上手くいくのですが、同じコネクション内で二回目の送受信をするよう追記したところ動作がおかしくなりました。(一つ目のメッセージも受信しないまま画面が停止した状態になる)何が問題なのでしょうか。 (Receiver.java) public class Receiver { public static final int PORT = 30000; public static void main(String[] args) { try { ServerSocket serverSoc = new ServerSocket(PORT); Socket socket = null; System.out.println("Waiting for Connection.."); socket = serverSoc.accept(); System.out.println("Connection from "+socket.getInetAddress()); // receive message BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); System.out.println("Message from sender ="+new String(br.readLine())); //send message←ここを追記するとおかしくなりました。 String message = "Hey This is receiver"; BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); System.out.println("I will send: "+message); bw.write(message); br.close(); bw.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } (Sender.java) public class Sender { public static final int PORT = 30000; public static void main(String[] args) { try { InetAddress LocalHost = InetAddress.getLocalHost(); InetSocketAddress socketAddress = new InetSocketAddress(LocalHost, PORT); Socket socket = new Socket(); socket.connect(socketAddress, 10000); //send message String message = "Hey This is sender"; BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); System.out.println("I will send: "+message); bw.write(message); // receive message←同じくここを追記するとおかしくなりました。 BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); System.out.println("Message from receiver ="+new String(br.readLine())); br.close(); bw.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } }