まず、マルチスレッド
並列並行
パラレル:2つのイベントが同時に、彼らが行われている
、同時に2つのイベントを、場所を取っていると、時間の期間内に(別の実行)
プロセスとスレッド
プロセス:プログラムがメモリで実行され、我々は、プロセスを呼び出す
(プロセス実行部関数を使用して)プロセスを完了するために小さい機能モジュール:スレッド
スレッドは、プロセスの一部であり、
各プロセスは、独自のメモリ空間(ヒープの別個のスタック独立、等)を有し、少なくとも1つのスレッド
の各スレッドが別々のスタックを使用してアプリケーションを処理する、ヒープは、プロセスを共有しました
CPUスレッドコールすばやく別の異なるプロセス・スレッドを切り替える手段
分類スレッドスケジューリング:
時分割スケジュール:各スレッドは、CPUの平均実行有する
プリエンプティブコールを:ランダムCPUの各スレッドの実行に割り当てられた(特定のスレッドの優先度に関連する数と分布)
プリエンプティブスケジューリングを使用してすべてのスレッドで私たちのJavaプログラム(Javaプロセス)
スレッドの状態
NEW(新)、RUNNABLEがブロックされ、(ランニング)(ブロッキング)、WAITING(待機)、TIMED_WAITING(待ち時間)、TERMINATED(終了);次の状態遷移:Threadオブジェクト6つの状態があります。
1.Threadクラス
a.Thread类是什么?
Thread是Java定义好的,代表线程的类,只要创建该类的一个对象,其实就是创建了一个线程
b.Thread类的构造方法
public Thread(); // 无参构造,线程会有默认的名字,Thread-0,Thread-1等...
public Thread(String name); //带有线程名字的构造
public Thread(Runnable r);//带有线程任务的构造
public Thread(Runnable r,String name); //即带有线程名字,又带有线程任务的构造
c.Thread类的成员方法
public String getName(); //获取线程的名字
public void setName(String name);//修改线程的名字
public void run();//代表线程要执行的任务,任务有关的代码需要写在次方法中
public void start();//线程只创建并不会执行,必须调用start开启后才会执行任务
public static void sleep(long millis); //让当前线程"休眠/暂停"多少毫秒
这里的当前线程只指 Thread.sleep(1000)这句代码写哪个线程中,哪个线程就是当前线程
public static Thread currentThread();//获取当前线程对象
这里的当前线程是指 Thread.currentThread() 这句代码写哪个线程中,哪个线程就是当前线程
线程执行有优先级,优先级越高先执行机会越大(并不是一定先执行!!)。优先级用int的priority参数表示。
线程优先级最高为10,最低为1。默认为5
継承 - 新しいスレッドの道を作成します。2.
a.描述:
将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。接下来可以分配并启动该子类的实例
b.分析创建的步骤:
i.创建子类 继承 Thread
ii.子类中重写run方法(在run中编写线程要执行的任务代码)
iii.创建子类对象(实际上就是创建一个线程对象)
iv. 调用线程对象的start方法(启动该线程)
c.案例:
//i.创建子类 继承 Thread
public class MyThread extends Thread {
//ii.子类中重写run方法(在run中编写线程要执行的任务代码)
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println("子线程..."+i);
}
}
}
public class ThreadDemo02 {
public static void main(String[] args) {
// iii.创建子类对象(实际上就是创建一个线程对象)
MyThread mt = new MyThread();
//iv. 调用线程对象的start方法(启动该线程)
mt.start();
//主线程 不会等待子线程任务结束
for (int i = 0; i < 50; i++) {
System.out.println("主线程..."+i);
}
}
}
注意:
a.我们可以给线程起名字,也可以使用默认的名字
b.我们获取线程的名字时:
建议使用通用方式: Thread.currentThread().getName();
如果是子线程内部也可以直接调用getName()获取子线程的名字
新しいスレッドの実装を作成する2番目の方法_
a.描述
声明实现 Runnable 接口的类。该类然后实现 run 方法。
然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递 并启动.
b.分析步骤:
i.创建实现类 实现 Runnable接口(实际上接口中一个任务方法,run方法)
ii.实现类重写run方法(run中编写具体的任务代码)
iii.创建实现类对象(该实现类对象并不是线程对象,我们称为任务对象)
iv. 创建Thread对象,同时传入实现类对象
public Thread(Runnable r);//带有线程任务的构造
v. 启动该线程(调用线程对象的start方法)
c.代码实现
//i.创建实现类 实现 Runnable接口(实际上接口中一个任务方法,run方法)
public class MyRunnable implements Runnable {
//ii.实现类重写run方法(run中编写具体的任务代码)
@Override
public void run() {
//run中写任务代码
for (int i = 0; i < 50; i++) {
System.out.println("子线程..."+i);
}
}
}
public class TestThread {
public static void main(String[] args) {
//iii.创建实现类对象(该实现类对象并不是线程对象,我们称为任务对象)
MyRunnable mr = new MyRunnable();
//iv. 创建Thread对象,同时传入实现类对象
Thread tt = new Thread(mr);
//v. 启动该线程(调用线程对象的start方法)
tt.start();
//主线程不会等待子线程执行完毕
for (int i = 0; i < 50; i++) {
System.out.println("主线程..."+i);
}
}
}
2つの方法の長所と短所を比較
スレッドの道を作成する2つの方法、達成するためのより良い方法
スレッドおよびタスクの実装は、独自の組み合わせプログラマによって、分離されているので。インプリメンテーションは、優れている
B。Javaの単一継承の実装の不足を回避するために
Cを。スレッドおよびタスクの実装デカップリング、相続スレッドとタスクが結合されている
スレッドプールのdは、我々は、スレッドのサブクラスを必要とせずに、Runnableを実装クラスを必要としています。
合計する:開発中で、私たちは実装を使用することをお勧めします(間違った道を受け継いと言うことではありません)
匿名内部クラスは、スレッドを作成する方法を簡素化
匿名内部类作用:
可以快速创建一个类的子类对象或者一个接口的实现类对象
public class TestDemo {
public static void main(String[] args) {
//1.继承方式创建线程
new Thread(){
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName()+"..."+i);
}
}
}.start();
//2.实现方式创建线程
new Thread(new Runnable(){
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName()+"..."+i);
}
}
}).start();
//主线程不会等待子线程任务结束
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName()+"..."+i);
}
}
}
II。高並行性、およびスレッドセーフ
高並行性とスレッドセーフ紹介
高い同時実行は何か:時点を指し、ユーザー(スレッド)の多数が同時に同じリソースにアクセス
スレッドセーフに:時点を指し、「規格外実際の文字の高いデータアクセスが発生した後データは、「スレッド安全性の問題と呼ばれています
マルチスレッド操作機構
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("i = " + i);
}
}
}
public class Demo {
public static void main(String[] args) {
//1.创建两个线程对象
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
//2.启动两个线程
t1.start();
t2.start();
}
}
マルチスレッドのセキュリティ問題 - 可視性
什么有可见性:
当一个共性变量,被多个线程使用时,其中某个线程对共性变量进行了修改,对于其他线程来说并不是立刻可见的
其他线程获取的值还是以前的副本(旧的值)
案例:
public class MyThread extends Thread {
//无论创建多个MyThread对象,他们共性一个静态变量a
public static int a = 0;
@Override
public void run() {
System.out.println("线程启动,休息2秒...");
try { Thread.sleep(1000 * 2); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("将a的值改为1");
a = 1;
System.out.println("线程结束...");
}
}
public class TestSafaDemo01 {
public static void main(String[] args) {
//1.启动线程
MyThread t = new MyThread();
t.start();
//2.主线程继续
while (true) {
if (MyThread.a == 1) {
System.out.println("主线程读到了a = 1");
}
}
}
}
マルチスレッドのセキュリティ問題 - 秩序
秩序は何か:
コード「再配置」のエクステントは、コードの結果に影響を与えない
マルチスレッド、「再配置」の場合には、同じコード、結論異なる結果の操作になる可能性がある場合
、我々がしたいですマルチスレッドではなく、コード「再配置」の場合にはコードが順序付けされることを保証することを確実にする(再配列を使用しないでください!)
マルチスレッドのセキュリティ問題 - アトミック
どのような原子性:
++は++ 1によって2つの段階、最初の値に分け、その後、一般的な変数の割り当てに取り出したときに、共通の変数をスレッド
値プラス1が取り出された場合、時間の割り当てを持っていなかった、他のスレッドが奪われてCPUは、我々は何のアトミック操作を持たないこのケースを呼び出す++
III。volatileキーワード
揮発性がメンバ変数(静的変数)を変更するために使用するキーワードである、彼は変数が可視性と秩序を持っている修正しました
揮発性の解決の可視性
public class MyThread extends Thread {
//无论创建多个MyThread对象,他们共性一个静态变量a
public volatile static int a = 0;
@Override
public void run() {
System.out.println("线程启动...");
try { Thread.sleep(1000 * 2); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("将a的值改为1");
a = 1;
System.out.println("线程结束...");
}
}
public class TestSafaDemo01 {
public static void main(String[] args) {
//1.启动线程
MyThread t = new MyThread();
t.start();
//2.主线程继续
while (true) {
if (MyThread.a == 1) {
System.out.println("主线程读到了a = 1");
}
}
}
}
揮発性の解決秩序
揮発性のアトミック性を解決しません
volatile不能解决原子性问题
public class MyThread extends Thread {
public volatile static int a = 0;
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
a++;
}
System.out.println("修改完毕!");
}
}
public class TestSafeDemo {
public static void main(String[] args) throws InterruptedException {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.start(); //线程1 对a加了10000次
t2.start(); // 线程2 对a加了 10000次
Thread.sleep(1000);
System.out.println("获取a最终值:" + MyThread.a);
//总是不准确的。原因:两个线程访问a 的步骤不具有:原子性
}
}
揮発性の役割
。、変数の可視性を解決するための変数が変更されると、使用スレッドへのすべての変数は、最新の値になります
。この変数のないB。変数の秩序を解決するために、変数を加えた揮発一度、コンパイラが行うコードです再配列
アトミック操作を解決することはできませんC。プロセス変数、変数の操作は、依然として他のスレッドによって中断されていてもよいです
IV。 - アトム
。?原子タイプAとは何である
一般的なタイプです:クラスは、操作部材原子の操作作り、(例えばint型、整数、ダブル、ダブルなど)の原子をカプセル化します
?。アトミックアクションクラスBの
操作に基づく原子を増加または減少し、アトミックであることが保証、他のスレッドが「中断」の中ではないことを保証
するものアトミッククラスC。?
たとえば:
int型の変数操作するためのAtomicInteger原子クラス
原子クラスAtomicLongは、オペレーティングlong変数である
ブール変数操作のAtomicBooleanを「アトミッククラス」;
注:アトミッククラスは、アトミック性を解決することができるだけでなく、あなたが注文し、視認性を解決することができますjava.util.concurrent.atomic 定義次のパケット 「 可変 」 操作 「 アトミッククラス 」:1).java.util.concurrent.atomic.AtomicInteger ための: int型 の変数の操作 「 原子タイプ 」。2).java.util.concurrent.atomic.AtomicLong :のため に長い の変数操作 「 原子タイプ 」;3).java.util.concurrent.atomic.AtomicBoolean :用 ブール の変数操作 「 原子タイプ 」;
AtomicIntegerクラス
a.AtomicInteger是什么?
是对int类型变量进行操作的原子类
b.AtomicInteger的构造方法
public AtomicInteger(int num);
c.AtomicInteger的成员方法
public int getAndIncrement();//就相当于 变量++
public int incrementAndGet();//就相当于 ++变量
d.使用AtomicInteger改写案例
public class MyThread extends Thread {
public static AtomicInteger a = new AtomicInteger(0);
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
a.getAndIncrement();//相当于 a++ 先获取在自增1
}
System.out.println("修改完毕!");
}
}
public class TestSafeDemo {
public static void main(String[] args) throws InterruptedException {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.start(); //线程1 对a加了10000次
t2.start(); // 线程2 对a加了 10000次
Thread.sleep(1000);
System.out.println("获取a最终值:" + MyThread.a);
//总是不准确的。原因:两个线程访问a 的步骤不具有:原子性
}
}
安全でない クラス、コール: compareAndSwapInt() メソッドは、この方法のいくつかのパラメータ:VAR1は :着信 のAtomicIntegerは、 オブジェクトVAR2 : AtommicInteger 内部変数のオフセットアドレスVAR5 :前に撤退 のAtomicIntegerの 値。+ VAR4のVAR5 :予想される結果この方法は、使用しています 「 コンペア・アンド・スワップ (比較とスワップ)」 使用するメカニズム にvar1 と var2の 最初の捕捉メモリをAtomicInteger 値で、その後、渡された、以前に取得したの値 VAR5は 、いくつかの比較を行い、現在のメモリ値の比較と期待値であり、変更に関する合意場合は、同じである VAR5 + VAR4 、それ以外のサイクルは継続し、再び取得 のAtomicIntegerの 値を、次に比較します取引所、これまでの交流の成功まで。compareAndSwapInt() メソッドは、 「 スレッドセーフ 」 されます。私たちは、2つのスレッドが交互に動作していることを前提とし、それがどのように動作するかを参照してください。初期 のAtomicIntegerの 値 0スレッド 実行:VAR5 = this.getIntVolatile(VAR1、VAR2)を、結果が得られた:0スレッド Aが 中断されますスレッド Bの 実行: VAR5 = this.getIntVolatile(VAR1、VAR2); 結果が得られた: 0スレッド B 実行: this.compareAndSwapInt(VAR1、VAR2、VAR5、VAR5 + VAR4)スレッド Bは 、正常 のAtomicIntegerの 値に変更された 1スレッド 、回復運転を実行します。this.compareAndSwapInt(VAR1、VAR2、VAR5、VAR5 + VAR4)このとき、スレッド Aの 使用 VAR1 と VAR2 から のAtomicIntegerの 値が得られた: 1を 、着信 VAR5が ある 0 、比較は、復帰に失敗しました偽 、ループが続きます。スレッド 実行:VAR5 = this.getIntVolatile(VAR1、VAR2)を、結果が得られた:1スレッド の実行を:this.compareAndSwapInt(VAR1、VAR2、VAR5、VAR5 + VAR4)この時点で、スレッド Aの 使用 VAR1 と VAR2 から のAtomicIntegerの 取得した値: 1 、着信 VAR5 に 1 、より成功した、改変がそれらになりある VAR5 + VAR4 すなわち、 2 、 のAtomicIntegerの 値が変更された 2 、端部が。CASの 楽観的ロック:メカニズムとしても知られています。比較の結果のほとんどがあるので trueに 、直接変更。どこ意志をマルチスレッドのほんの一部つながる のCAS 再び失敗、およびサイクル。
AtomicIntegerArray类
従来のアレイ型アトミック操作: 1).java.util.concurrent.atomic.AtomicIntegetArray: のために INT -atomicアレイ動作。2).java.util.concurrent.atomic.AtomicLongArray :のため の長い -atomicアレイ動作。3).java.utio.concurrent.atomic.AtomicReferenceArray : -配列操作の種類にアトム参照。
クラスの非原子の配列は、マルチスレッドで問題が発生します
public class MyThread extends Thread {
public static int[] intArray = new int[1000];//不直接使用数组
@Override
public void run() {
for (int i = 0; i < intArray.length; i++) {
intArray[i]++;
}
}
}
public class TestDemo01 {
public static void main(String[] args) throws InterruptedException {
//创建1000个线程,每个线程为数组的每个元素+1
for (int i = 0; i < 1000; i++) {
new MyThread().start();
}
Thread.sleep(1000 * 5);//让所有线程执行完毕
System.out.println("主线程休息5秒醒来");
for (int i = 0; i < MyThread.intArray.length; i++) {
System.out.println(MyThread.intArray[i]);
}
}
}
打印结果:
1000,1000,1000,999,1000,1000,1000,1000,....
有个别元素是小于1000的,因为int[]是非原子类数组,不能保存原子性!!!
クラスの原子配列の使用は、問題を解決するために、原子性を確保するために
public class MyThread extends Thread {
public static int[] intArray = new int[1000];//不直接使用数组
public static AtomicIntegerArray arr = new AtomicIntegerArray(intArray);
@Override
public void run() {
// for (int i = 0; i < intArray.length; i++) {
// intArray[i]++;
// }
for (int i = 0; i < arr.length(); i++) {
arr.addAndGet(i, 1);//将i位置上的元素 + 1,相当于 ++数组[i]
}
}
}
public class TestDemo01 {
public static void main(String[] args) throws InterruptedException {
//创建1000个线程,每个线程为数组的每个元素+1
for (int i = 0; i < 1000; i++) {
new MyThread().start();
}
Thread.sleep(1000 * 5);//让所有线程执行完毕
System.out.println("主线程休息5秒醒来");
// for (int i = 0; i < MyThread.intArray.length; i++) {
// System.out.println(MyThread.intArray[i]);
// }
for (int i = 0; i < MyThread.arr.length(); i++) {
System.out.println(MyThread.arr.get(i));
}
}
}