🌊 IO 流#
入力出力
四大抽象类#
- データタイプに基づく
- バイトストリーム
- キャラクターストリーム
- データの流れに基づく
- 入力ストリーム メモリに入力
- 出力ストリーム メモリから出力
- 機能の違いに基づく
- ノードストリーム
データストリームを直接操作 - 処理ストリーム
他のストリームを処理
- ノードストリーム
📥 InputStream#
FileInputStream#
public FileInputStream(String)
入力ストリームのリンクを開く。ファイルパスを渡す必要があり、例外をスローする。public int reader()
次のバイトを読み取り、対応する ASCII 値を返す。ファイルの終わりに達した場合は-1
を返す。
📤 OutputStream#
FileOutputStream#
public FileOutputStream(String)
ストリームのリンクを開く。ファイルパスを渡す必要があり、例外をスローする。上書き書き込み。public FileOutputStream(String,Boolean)
ストリームのリンクを開く。ファイルパスを渡す必要があり、例外をスローする。true
の場合は追加書き込み。public void write()
public void write(byte[])
public void write(byte[],int,int)
public void flush()
public void append()
📖 Reader#
FileReader#
キャラクタ入力ストリーム。1 回の読み取りで 1 文字を読み取ることができ、漢字の文字化け問題を回避できる。主にプレーンテキストファイルに適している。
✍️ Writer#
FileWriter#
public void write()
public void write(char[])
public void write(char[],int,int)
public void write(String)
public void write(String,int,int)
public void flush()
public void append()
🔌 ノード処理ストリーム#
ノードストリーム(FileInputStream、FileOutputStream、FileReader、FileWriter)を渡して処理する必要がある。
🧺 バッファストリーム#
BufferedReader#
BufferedWriter#
BufferedInputStream#
public String readLine()
読み取ったデータを返す。ファイルの終わりに達した場合はnull
を返し、改行文字は読み取らない。public int available()
このファイルから取得可能なバイト数。
BufferedOutputStream#
💫 変換ストリーム#
バイトストリームをキャラクターストリームに変換する。
🖨️ PrintStream#
印刷ストリーム。操作を簡単にするために多くの印刷メソッドを提供し、ユーザーはデータを渡して直接印刷できる。
System
クラスの 標準入力出力
in
:標準入力、デフォルトはコンソール入力。
out
:標準出力、デフォルトはコンソールに印刷。
err
System
クラスでは、標準入力 / 出力をリダイレクトするための 3 つのメソッドが提供されている。
static void setErr (PrintStream err) 標準エラー出力ストリームをリダイレクト。
static void setIn (PrintStream in) 標準入力ストリームをリダイレクト。
static void setOut (PrintStream out) 標準出力ストリームをリダイレクト。
FileOutputStream fos = new FileOutputStream("./src/test.txt");
// 出力ストリームをラップすると、出力操作がより便利になる。
// バイト印刷ストリーム
PrintStream ps = new PrintStream(fos);
// System のデフォルトの out はコンソールに印刷されるが、印刷パスを変更できる。
System.setOut(ps);
System.out.println("===============");
特徴#
- 印刷ストリームは出力が最も便利なクラス。
- バイト印刷ストリーム
PrintStream
、キャラクタ印刷ストリームPrintWriter
を含む。 PrintStream
は OutputStream のサブクラスで、出力ストリームのインスタンスを印刷ストリームに渡すことで、内容をより便利に出力できる。出力ストリームを再包装することに相当する。PrintStream
クラスのprint()
メソッドは多くの回数オーバーロードされている。print(int i)
、print(boolean b)
、print(char c)
。
PrintWriter#
キャラクタ印刷ストリーム。
public void println()
✍️ DataInputStream#
Linux、Windows などのオペレーティングシステムはデータの保存方法が異なる。
異なるプラットフォーム間でのデータ読み取りの統一性を解決するために、データストリームは データの一貫性を保証する。
📁 File#
public boolean isFile()
ファイルかどうかを判断する。public boolean isDirectory()
ディレクトリかどうかを判断する。public boolean exists()
存在するかどうかを判断する。public String getAbsolutePath()
フルパス(ファイル名と拡張子を含む)を取得する。public String getName()
ファイル名 + 拡張子 a.txt を取得する。public String getParent()
親ディレクトリを取得する。public File getParentFile()
親ディレクトリのファイルオブジェクトを取得する。public File[] listFiles()
子ファイルオブジェクトを取得する。
📬 シリアル化、逆シリアル化#
シリアル化:オブジェクトをハードディスクに保存し、永続的に保存すること。
逆シリアル化:永続的に保存されたオブジェクトをメモリにロードすること。
目的:特定のオブジェクトを長期間保存し、ネットワークでの伝送を容易にすること。
このクラスは Serializable
インターフェースを実装する必要があり、シリアル化される。
指定されたバージョン番号は、serialVersionUID
属性を使用して Serializable
のバージョンを指定し、ロードされたクラスとシリアル化されたオブジェクトが互換性があるかどうかを検証する。
transient#
キーワード
現在の属性がシリアル化されないように制限し、属性値は保存されない。
🧵 マルチスレッド#
プログラム:特定の機能を完了するための命令の集合。プログラムは静的な概念であり、通常はハードディスクに保存される。
プロセス:実行中のプログラムであり、動的な概念であり、メモリに保存される。オペレーティングシステムは対応する PID を割り当てる。特定のプロセスを直接閉じると、そのプロセスは終了する。
スレッド:プログラム内の異なる実行分岐。同じ時間ノードで複数のスレッドが同時に実行されることを許可されている場合、これをマルチスレッドサポートと呼ぶ。
Java では、main()
メソッドが実行を開始すると、主スレッドと呼ばれるスレッドが生成される。
並行性 一つの CPU が同時に複数のタスクを実行すること。
並列性 複数の CPU が同時に複数のタスクを実行すること。
継承方式#
Thread
クラスを継承し、run()
メソッドをオーバーライドすることは、新しいスレッドの main()
メソッドと同じである。
実装方式#
Runnable
インターフェースを実装し、run()
メソッドをオーバーライドする。
継承と実装の違い#
- 違い
- Thread を継承:スレッドコードは Thread サブクラスの run メソッドに格納される。
- Runnable を実装:スレッドコードはインターフェースのサブクラスの run メソッドに存在する。
- 実装方式の利点
- 単一継承の制限を回避できる。
- 複数のスレッドが同じインターフェース実装クラスのオブジェクトを共有できるため、同じリソースを処理する複数のスレッドに非常に適している。
注意
run()
メソッドを直接呼び出すことはできない。そうでなければ、単なるメソッド呼び出しであり、新しいスレッドは開始されない。
優先度#
優先度はデフォルトで 5。
public final int getPriority()
スレッドの優先度数値を取得する。public final void setPriority(int newPriority)
スレッドの優先度数値を設定する。
メソッド#
public final synchronized void setName(String name)
スレッド名を設定する。public final String getName()
スレッド名を取得する。名前は Thread-0、Thread-1 など。public final native boolean isAlive()
スレッドがまだ「生きている」かどうかを判断する。つまり、スレッドがまだ終了していないかどうか。public static native void sleep(long millis) throws InterruptedException
現在のスレッドを指定されたミリ秒数だけスリープさせる。public final void stop()
このスレッドを停止する(推奨されない)。デッドロック状態を引き起こす可能性があるため、識別子を使用して終了する。public final void join() throws InterruptedException
2 つのスレッドを統合する。public static native void yield();
現在の CPU タイムスライスを放棄し、他のスレッドを実行させる。
CPU タイムスライスを取得し、同じ優先度のスレッドが存在する場合、譲渡を選択できる。public static native Thread currentThread()
現在実行中のスレッドオブジェクトの参照を返す。
⌛ ライフサイクル#
JDK では Thread.state
クラスでスレッドのいくつかの状態を定義している。
5 つの状態
- 新規
Thread クラスまたはそのサブクラスのオブジェクトが宣言され、作成されたとき、新生のスレッドオブジェクトは新規状態にある。 - 準備
新規状態のスレッドが start () されると、スレッドキューに入り、CPU タイムスライスを待つ。この時点で、実行の条件を満たしているが、CPU リソースが割り当てられていない。 - 実行
準備状態のスレッドが スケジュールされ、CPU リソースを取得すると、実行状態に入る。run () メソッドはスレッドの操作と機能を定義する。 - ブロック
- 特殊な状況下で、人為的に一時停止されたり、入出力操作を実行する際に、CPU を放棄し、一時的に実行を中止し、ブロック状態に入る。
- 死亡
スレッドがすべての作業を完了した場合、またはスレッドが強制的に中止されたり、例外が発生して終了した場合。
🤝 スレッド同期#
複数のスレッドが実行する不確実性が実行結果の不安定性を引き起こす。
スレッド同期:複数のスレッドが同じデータを同時に操作する可能性がある場合、データの一貫性を保証するために、同期実行を行う必要がある。
本質的にはデータを同期させることであり、安全メカニズムの一種である。
非同期プログラミング:スレッド間は完全に独立しており、互いに影響を与えない。
同期プログラミング:スレッド間は完全に独立しておらず、互いに影響を与える可能性がある。
🔒 スレッドロック#
メソッドロック#
synchronized:メソッドにロックをかける。
あるスレッドが特定のオブジェクト内の synchronized 修飾されたメンバー メソッドにアクセスすると、そのオブジェクト内のすべての synchronized 修飾されたメンバー メソッドがロックされる。
この時点で、どのスレッドも synchronized 修飾されたメンバー メソッドにアクセスできず、前のスレッドがメソッドを実行し終わった後にロックを解放するまで、他のスレッドはアクセスできない。
欠点:効率が低い。実行を待つために列に並ぶ必要があり、オブジェクト内のすべてのロックされたメンバー メソッドがすべてロックされる。
利点:データの安全性と一貫性が保証され、データエラーの発生を回避できる。
🍰 ステートメントブロックロック#
クラスロック#
静的にもロックをかけることができ、これをクラスロックと呼ぶ。
すべてのロックされた静的メソッドと静的ステートメントブロックがすべてロックされる。 Synchronized(クラス名.class){}
。
オブジェクトロック#
メンバーもロックをかけることができ、これを オブジェクトロック と呼ぶ。
そのオブジェクト内のすべてのロックされたメンバー メソッドとメンバー ステートメントブロックがすべてロックされる。 Synchronized(オブジェクト){}
(異なるオブジェクトは直接影響しない)。
🔐 Lock ロック#
- JDK 5.0 以降、Java はより強力なスレッド同期メカニズムを提供している。明示的に同期ロックオブジェクトを定義することで同期を実現する。同期ロックは Lock オブジェクトを使用する。
java.util.concurrent.locks.Lock
インターフェースは、複数のスレッドが共有リソースにアクセスするのを制御するためのツールである。ロックは共有リソースへの独占アクセスを提供し、毎回 1 つのスレッドだけが Lock オブジェクトをロックできる。スレッドが共有リソースにアクセスする前に、Lock オブジェクトを取得する必要がある。- ReentrantLock クラスは Lock を実装しており、synchronized と同じ並行性とメモリセマンティクスを持つ。スレッドセーフな制御を実現するために、ReentrantLock がよく使用され、明示的にロックをかけたり、ロックを解放したりできる。
lock は明示的なロックであり、手動で開閉する必要がある。
synchronized は暗黙的なロックであり、自動的に開き、実行が完了すると自動的に閉じる。
lock はコードブロックロックのみであり、synchronized はメソッドとコードブロックロックをサポートする。
lock ロックは、JVM がリソース調整にかかる時間が少なく、性能が相対的に良好で、拡張性も高い。
使用順序 : Lock ロック --> 同期コードブロックロック --> メソッドロック。
⏱️ タイマタスク#
タイマー:計画されたタスク。
計画されたタスクがある限り、スレッドが開始され、カウントダウンが行われ、指定された時間に達した後、そのスレッドがこのタスクを完了する。
メソッド#
public void schedule(TimerTask task, long delay, long period)
public void schedule(TimerTask task, long delay)
public class MyTimerTest {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new MyTimer(), 1000, 500);
}
}
class MyTimer extends TimerTask {
@Override
public void run() {
System.out.println("run() メソッドが実行されました");
}
}
🦸 デーモンスレッド#
各プログラムが実行されると、デーモンスレッドが同期して起動し、正常なプログラムを監視する。
主スレッドが実行を完了すると、デーモンスレッドは存在する価値がなくなる。作業がないため、JVM はシャットダウンし、デーモンスレッドは終了する。
特定のスレッドをデーモンスレッドに設定するには、スレッドオブジェクト.setDaemon(true)
を使用する(開始前に設定する必要がある)。
🧟♀️ デッドロック#
プログラムの実行中に、相手がロックを取得するメソッドに入ることで、メソッドにアクセスできなくなる。
原理 :
- あるスレッドが実行を完了するには、順番にロックを取得する必要がある 2 つのオブジェクトをロックし、最初のオブジェクトをロックしてから 2 番目のオブジェクトをロックする。
- 別のスレッドが実行を完了するには、順番にロックを取得する必要がある 2 つのオブジェクトをロックし、最初に 2 番目のオブジェクトをロックしてから最初のオブジェクトをロックする。
- 最初のスレッドが最初のオブジェクトをロックした後、2 番目のオブジェクトをロックしようとすると、2 番目のオブジェクトがすでにロックされていることに気づき、待機するしかない。
- 2 番目のスレッドが 2 番目のオブジェクトをロックした後、最初のオブジェクトをロックしようとすると、最初のオブジェクトがすでにロックされていることに気づき、待機するしかない。
✉️ スレッド通信#
Object のメソッド
wait()
:このスレッドを待機状態(サスペンド状態)にし、呼び出されると準備状態に入り、以前にサスペンドされた場所から実行を再開する。
引数なしまたは引数に 0
を渡すと、自動的には目覚めず、呼び出し (notify(),notifyAll()
) によってのみ目覚める。
notify()
: このオブジェクトで待機しているスレッドの 1 つをランダムに目覚めさせ、他のスレッドを実行させる。notifyAll()
: このオブジェクトで待機しているすべてのスレッドを目覚めさせる。
long 型のミリ秒数を渡すこともでき、指定されたミリ秒数後に自動的に目覚める。
wait と sleep の違い:sleep はロックを放棄せず、ロックを占有し続け、他のスレッドは入れない。wait はロックを放棄し、他のスレッドが入ることができる。
上記のメソッドはメンバー メソッド内で使用する必要があり、そのメソッドにはロック(synchronized)が必要である。