質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

1回答

2899閲覧

Javaのスレッドの対象インスタンスについて

退会済みユーザー

退会済みユーザー

総合スコア0

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

0グッド

0クリップ

投稿2016/08/19 04:52

Javaのsynchronized修飾子の対象インスタンスについての質問です。
http://www.tohoho-web.com/java/thread.htm
こちらの記事を参考にしました。

// テストプログラム class SyncTest { static Counter counter = new Counter(); public static void main(String[] args) { // スレッドを1000個作成する MyThread[] threads = new MyThread[1000]; for (int i = 0; i < 1000; i++) { threads[i] = new MyThread(); threads[i].start(); } // スレッドがすべて終了するのを待つ for (int i = 0; i < 1000; i++) { try { threads[i].join(); } catch (InterruptedException e) { System.out.println(e); } } // カウンターを表示する System.out.println(SyncTest.counter.count); } } // スレッド class MyThread extends Thread { public void run() { SyncTest.counter.countUp(); } } // カウンター class Counter { int count ; void countUp() { System.out.print("["); int n = count; // カウンターを読み出して System.out.print("."); count = n + 1; // 加算して書き戻す System.out.print("]"); } }

「この問題(スレッドの競合のことです)を防ぐには、同期処理 や 排他制御 と呼ばれる制御を行います。下記のように synchronized を用いることで、(...) で指定したオブジェクト(下記の例では this、つまり Counter オブジェクト)に対してロック権を取得した単一のスレッドのみが { ... } の処理を実行できるようになります。
(中略)
synchronized メソッドは、常にひとつのスレッドのみがそのメソッドを実行すると誤解されがちですが、スレッドを実行するインスタンスが複数あれば、インスタンスの個数だけ多重に実行される可能性がありますので注意してください。例えば、上記の例で add() ではなく run() メソッドを synchronized にしても、run() メソッドのインスタンスはスレッドの個数分 1000個ありますので、排他制御はうまく機能しません。」

つまり、例えばsynchronized(this)とすると、thisに対して、実行する権利を得たスレッドのみが{}で囲まれた部分を実行するわけですよね?
このコードで言えば、countUpメソッドにsynchronized修飾子をつけると、Counterインスタンスに対して、実行する権利を得たスレッドのみがブロックで囲まれた部分を実行することになるから、競合は起こらないという意味だと思います。

一方、私の使っている参考書(スッキリわかるJava入門編 第二版)に次のような記述がありました。

System.out.println("こんにちわ"); synchronized(this) { a +=2; } System.out.println("さようなら"); synchronized(this) { a+=3; } System.out.println("おやすみ"); synchronized(System.out) { b+=3; }

スレッドAが最後のブロック(対象インスタンスがSystem.outのブロックです)を実行している時に、真ん中のブロックを実行中のスレッドBは真ん中のブロックを実行後、この最後のブロックに入れるというような記述があります。
この本の引用ですと、「スレッドAとは対象インスタンスが違うため、侵入可。」と書いています。
System.outインスタンスが一つではないから、スレッドAもスレッドBも許可を得られるので、実行できるということなのでしょうか。
回答お願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

swordone

2016/08/19 15:08

実行の形態がよくわからないので、この最後のコードの周辺含めて全体を書いていただけませんか?
guest

回答1

0

ベストアンサー

どういう状況かわからなかったので、以下の様なコードで実験してみました(本の意図と違うようならご指摘下さい)。

java

1public class Q44822 { 2 3 public static void main(String[] args) { 4 new Test("A").start(); 5 new Test("B").start(); 6 } 7 8 static class Test extends Thread { 9 String id; 10 int a = 0, b = 0; 11 12 public Test(String id) { 13 this.id = id; 14 } 15 16 @Override 17 public void run() { 18 System.out.println(id + ":こんにちわ"); 19 synchronized (this) { 20 a += 2; 21 } 22 System.out.println(id + ":さようなら"); 23 synchronized (this) { 24 a += 3; 25 } 26 System.out.println(id + ":おやすみ"); 27 synchronized (System.out) { 28 System.out.println(id + "がout-synchronizedに進入"); 29 b += 3; 30 31 try { 32 Thread.sleep(1000); 33 } catch (InterruptedException e) { 34 // TODO 自動生成された catch ブロック 35 e.printStackTrace(); 36 } 37 38 System.out.println(id + "がout-synchronizedから脱出"); 39 } 40 } 41 } 42 43}

結果、何度やっても、片方が最後のブロックを実行中(Thread.sleep(1000)で止まっている間)、もう片方が最後のブロックに入ってくるということはありませんでした。
根本的に考えて、System.outはstaticフィールドなのでインスタンスはただ一つです。片方がこれのロックを取得しているなら、もう片方がこのロックを取得できないので、synchronizedブロックに入れません。

投稿2016/08/20 02:15

swordone

総合スコア20649

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2016/08/20 03:40

質問の補足への対応が遅れまして、申し訳ありません。 swordoneさんのコードで問題ありません。 コードの結果拝見いたしました。 そうすると、この本の記述が少しおかしいのでしょうね。 ありがとうございます。 わからないところが1点あります。 答えていただければ、幸いです。 まず、あまり意識していなかったのですが、System.outってインスタンスなのでしょうか? よくよく考えると、static変数であって、インスタンスではないような気がしてきました。
swordone

2016/08/20 03:51

System.outは、「java.lang.Systemクラス」のstaticフィールドで、PrintStreamオブジェクトです。
退会済みユーザー

退会済みユーザー

2016/08/20 13:52

APIリファレンスを読みました。 以下、お答え頂ければ嬉しいです。 特にインスタンス化もしていないのに、 System.out.println(); のように呼び出せますので、インスタンス化のタイミングはクラスがロードされた時でしょうか。 また、staticフィールドだったら、インスタンスはただ一つなのはなぜでしょうか? 調べてみましたが、わかりませんでした。
swordone

2016/08/20 14:36

インスタンス化のタイミングはすみませんがわかりません。 > staticフィールドだったら、インスタンスはただ一つなのはなぜでしょうか? クラス内で唯一の存在だからこそ、staticなのです。(Systemクラスはインスタンス化できませんが)非staticフィールドの場合、そのクラスのインスタンスにそのフィールドが結びついているため、インスタンスの数だけそのフィールドが存在することになります。staticフィールドはクラスに結びついており、インスタンスに依存しないクラスで唯一のものになります。
退会済みユーザー

退会済みユーザー

2016/08/21 13:39

回答ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問