ホームページ >Java >&#&チュートリアル >Java マルチスレッド: Thread クラスを使用します。

Java マルチスレッド: Thread クラスを使用します。

王林
王林転載
2023-04-25 14:52:071330ブラウズ

    Thread クラスの基本的な使用法

    1. サブクラスを作成し、Thread から継承し、run メソッドをオーバーライドします。

    class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("hello thread");
        }
    }
    public class Demo1 {
        public static void main(String[] args) {
            // 最基本的创建线程的办法.
            Thread t = new MyThread();
            //调用了start方法才是真正的在系统中创建了线程,执行run方法
            t.start();
        }
    }

    2. クラスを作成し、Runnable インターフェイスを実装し、Runnable インスタンスを作成して Thread

    class MyRunnable implements Runnable{
        @Override
        public void run() {
            System.out.println("hello");
        }
    }
    public class Demo3 {
        public static void main(String[] args) {
            Thread t = new Thread(new MyRunnable());
            t.start();
        }
    }

    3. 匿名の内部クラス

    匿名の内部クラスが作成され、Thread クラスから継承し、run メソッドをオーバーライドして、匿名の内部クラス

    public class Demo4 {
        public static void main(String[] args) {
            Thread t = new Thread(){
                @Override
                public void run() {
                    System.out.println("hello");
                }
            };
            t.start();
        }
    }

    new の Runnable のインスタンスを作成します。クラスと同時に、 new によって生成された Runnable インスタンスが Thread

    public class Demo5 {
        public static void main(String[] args) {
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello");
                }
            });
            t.start();
        }
    }

    4.lambda 式のコンストラクターに渡されます。lambda 式は Runnable

    public class Demo6 {
        public static void main(String[] args) {
            Thread t = new Thread(() ->{
                System.out.println("hello");
            });
            t.start();
        }
    }

    Thread を置き換えます。インジケーター

    1.isDaemon();

    バックグラウンド スレッドのバックグラウンド スレッドがプロセスの終了に影響しないかどうか、バックグラウンド スレッドがプロセスの終了に影響しないかどうか

    2.isAlive();

    生きていますか? start を呼び出す前に、システムに対応するスレッドがありません。スレッドは、run メソッドの実行後に破棄されます。t オブジェクトは、まだ存在します

    3.isinterrupted();

    中断されるかどうか

    runとstartの違い

    run はタスクの内容を記述する通常のメソッドですが、start は特別なメソッドであり、システムの内部でスレッドが作成されます。

    #スレッドを中断します

    スレッドを停止する鍵は、対応する run メソッドの実行を終了させることです。メイン スレッドの場合、メイン メソッドは実行が完了した後にのみ終了します

    1手動でフラグを設定します

    スレッド内でこのフラグを制御するとスレッドの終了に影響を与える可能性がありますが、ここでは複数のスレッドが仮想空間を共有するため、isQuitはメインスレッドによって変更され、isQuitはtスレッドによって判断されます。は同じ値です

    public class Demo10 {
        // 通过这个变量来控制线程是否结束.
        private static boolean isQuit = false;
        public static void main(String[] args) {
            Thread t = new Thread(() -> {
                while (!isQuit) {
                    System.out.println("hello thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            t.start();
    
            // 就可以在 main 线程中通过修改 isQuit 的值, 来影响到线程是否退出
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // main 线程在 5s 之后, 修改 isQuit 的状态.
            isQuit = true;
        }
    }

    2。スレッドの組み込みフラグを使用して決定します。

    Thread.interruted() これは静的メソッド Thread.currentThread() です。 .isInterrupted() これはインスタンス メソッドであり、currentThread が現在のスレッドのインスタンスを取得できます。 ##

    public class Demo7 {
        public static void main(String[] args)  {
            Thread t = new Thread(() -> {
                while(!Thread.currentThread().isInterrupted()){
                    System.out.println("hello");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        // 当触发异常之后, 立即就退出循环~
                        System.out.println("这是收尾工作");
                        break;
                    }
                }
            });
            t.start();
            try{
                Thread.sleep(5000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            // 在主线程中, 调用 interrupt 方法, 来中断这个线程.
            // t.interrupt 的意思就是让 t 线程被中断!!
            t.interrupt();
        }
    }

    このメソッドを呼び出すと t.interrupt() になることに注意してください。

    1) t スレッドの準備ができている場合は、スレッドのフラグ ビットを true に設定します2) t スレッドがブロック状態 (スリープ) にある場合、InterruptExeception がトリガーされます。

    スレッド待機

    複数のスレッド間のスケジューリング順序は不確実です。場合によっては、スレッド間の順序を制御する必要があります。スレッド待機は、スレッドの順序を制御する手段です。ここで待機しているスレッドは、スレッドが終了する順序を制御するだけで済みます。

    どのスレッドが参加し、どのスレッドがブロックされ、対応するスレッドが実行を完了するまで待機します。


    t.join();

    このメソッドを呼び出すスレッドはメインスレッドです。オブジェクト t を呼び出すと、メインスレッドは待機させられます。 tのために。 コードは結合行に達すると停止し、最初に t が終了してから main が続行されます。

    t.join(10000);

    join は 1 つのパラメーターを備えた別のバージョンを提供します。パラメーターは 10 秒の待機時間です。10 秒を超えると、結合は行われます

    Thread.currentThread()

    現在のスレッドのアプリケーションを取得するまで待ちます。currentThread を呼び出すスレッドが、

    このコードでは、スレッドは Thread メソッドを継承することによって作成されます。現時点では、 run メソッドで this を通じて直接取得できるのは、現在の Thread インスタンス

    public class Demo4 {
        public static void main(String[] args) {
            Thread t = new Thread(){
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                    System.out.println(this.getName());
                }
            };
            t.start();
        }
    }

    ですが、これは Thread タイプを指しているのではなく、Runnable を指しています。単純なタスクです。name 属性では、Thread.currentThread()
    public class Demo5 {
        public static void main(String[] args) {
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    //err
                    //System.out.println(this.getName());
                    //right
                    System.out.println(Thread.currentThread().getName());
                }
            });
            t.start();
        }
    }

    プロセス ステータス

    システム レベルの場合:

    を通じてのみスレッド名を取得できます。

    Ready
    • ブロッキング
    • ##Java の Thread クラスはさらに洗練されています。

    ##NEW:

    Thread オブジェクトは作成されましたが、start はまだ呼び出されていません
    • TERMINATED:

      オペレーティング システム のスレッドは実行されて破棄されましたが、Thread オブジェクトはまだ取得された状態です
    • RUNNABLE:

      Ready 状態、この状態のスレッドは準備完了キュー内にあり、いつでも CPU にスケジュールできます
    • ##TIME_WAITING:スリープが呼び出されると、この状態に入ります。 join (タイムアウト時間) BLOCKED: 現在のスレッドはロックを待機しています。結果的にブロックされます。

    • WAITING:現在のスレッドはウェイクアップを待機しています

    • 状態遷移図:

    线程安全问题

    定义:操作系统中线程调度是随机的,导致程序的执行可能会出现一些bug。如果因为调度随机性引入了bug线程就是不安全的,反之则是安全的。
    解决方法:加锁,给方法直接加上synchronized关键字,此时进入方法就会自动加锁,离开方法就会自动解锁。当一个线程加锁成功的时候,其他线程尝试加锁就会触发阻塞等待,阻塞会一直持续到占用锁的线程把锁释放为止。

    synchronized public void increase() {
            count++;
    }

    线程不安全产生的原因:

    • 1.线程是抢占式执行,线程间的调度充满随机性。

    • 2.多个线程对同一个变量进行修改操作

    • 3.针对变量的操作不是原子的

    • 4.内存可见性也会影响线程安全(针对同一个变量t1线程循环进行多次读操作,t2线程少次修改操作,t1就不会从内存读数据了而是从寄存器里读)

    • 5.指令重排序,也是编译器优化的一种操作,保证逻辑不变的情况下调整顺序,解决方法synchronized。

    内存可见性解决方法:

    • 1.使用synchronized关键字 使用synchronized不光能保证指令的原子性,同时也能保证内存的可见性。被synchronized包裹起来的代码编译器就不会从寄存器里读。

    • 2.使用volatile关键字 能够保证内存可见性,禁止编译器作出上述优化,编译器每次执行判定相等都会重新从内存读取。

    synchronized用法

    在java中每个类都是继承自Object,每个new出来的实例里面一方面包含自己安排的属性,另一方面包含了“对象头”即对象的一些元数据。加锁操作就是在这个对象头里面设置一个标志位。

    1.直接修饰普通的方法

    使用synchronized的时候本质上是对某个“对象”进行加锁,此时的锁对象就是this。加锁操作就是在设置this的对象头的标志位,当两个线程同时尝试对同一个对象加锁的时候才有竞争,如果是两个线程在针对两个不同对象加锁就没有竞争。

    class Counter{
    	public int count;
    	synchronized public void increase(){
    	count++;
    	}
    }

    2.修饰一个代码块

    需要显示制定针对那个对象加锁(java中的任意对象都可以作为锁对象)

    public void increase(){
        synchronized(this){
        count++;
        }
    }

    3.修饰一个静态方法

    相当于针对当前类的类对象加锁,类对象就是运行程序的时候。class文件被加载到JVM内存中的模样。

    synchronized public static void func(){
    }

    或者

    public static void func(){
        synchronized(Counter.class){
    
        }
    }

    监视器锁monitor lock

    可重入锁就是同一个线程针对同一个锁,连续加锁两次,如果出现死锁就是不可重入锁,如果不会死锁就是可重入的。因此就把synchronized实现为可重入锁,下面的例子里啊连续加锁操作不会导致死锁。可重入锁内部会记录所被哪个线程占用也会记录加锁次数,因此后续再加锁就不是真的加锁而是单纯地把技术给自增。

    synchronized public void increase(){
        synchronized(this){
            count++;
        }
    }

    死锁的其他场景

    • 1.一个线程一把锁

    • 2.两个线程两把锁

    • 3.N个线程M把锁(哲学家就餐问题,解决方法:先拿编号小的筷子)

    死锁的四个必要条件(前三个都是锁本身的特点)

    • 1.互斥使用,一个锁被另一个线程占用后其他线程就用不了(锁的本质,保证原子性)

    • 2.不可抢占,一个锁被一个线程占用后其他线程不可把这个锁给挖走

    • 3.请求和保持,当一个线程占据了多把锁之后,除非显示的释放否则这些锁中都是该线程持有的

    • 4.环路等待,等待关系成环(解决:遵循固定的顺序加锁就不会出现环路等待)

    java线程类:

    • 不安全的:ArrayList,LinkedList,HashMap,TreeMap,HashSet,TreeSet,StringBuilder

    • 安全的:Vector,HashTable,ConcurrentHashMap,StringBuffer,String

    volatile

    禁止编译器优化保证内存可见性,产生原因:计算机想执行一些计算就需要把内存的数据读到CPU寄存器中,然后再从寄存器中计算写回到内存中,因为CPU访问寄存器的速度比访问内存快很多,当CPU连续多次访问内存结果都一样,CPU就会选择访问寄存器。

    JMM(Java Memory Model)Java内存模型

    就是把硬件结构在java中用专业的术语又重新抽象封装了一遍。

    工作内存(work memory)其实指的不是内存,而是CPU寄存器。
    主内存(main memeory)这才是主内存。
    原因:java作为一个跨平台编程语言要把硬件细节封装起来,假设某个计算机没有CPU或者内存同样可以套到上述模型中。

    寄存器,缓存和内存之间的关系

    CPU从内存取数据太慢,因此把数据直接放到寄存器里来读,但寄存器空间太紧张于是又搞了一个存储空间,比寄存器大比内存小速度比寄存器慢比内存快称为缓存。寄存器和缓存统称为工作内存。

    寄存器,缓存和内存之间的关系图

    Java マルチスレッド: Thread クラスを使用します。

    • 存储空间:CPU

    • 速度:CPU>L1>L2>L3>内存

    • 成本:CPU>L1>L2>L3>内存

    volatile和synchronized的区别

    • volatile只是保证可见性不保证原子性,只是处理一个线程读和一个线程写的过程。

    • synchronized都能处理

    wait和notify

    等待和通知处理线程调度随机性问题的,join也是一种控制顺序的方式更倾向于控制线程结束。wait和notify都是Object对象的方法,调用wait方法的线程就会陷入阻塞,阻塞到有线程通过notify来通知。

    public class Demo9 {
        public static void main(String[] args) throws InterruptedException {
            Object object = new Object();
            System.out.println("wait前");
            object.wait();
            System.out.println("wait后");
        }
    }

    wait内部会做三件事;

    • 1.先释放锁

    • 2.等待其他线程的通知

    • 3.收到通知后重新获得锁并继续往下执行

    因此想用wait/notify就得搭配synchronized

    public class Demo9 {
        public static void main(String[] args) throws InterruptedException {
            Object object = new Object();
            synchronized (object){
                System.out.println("wait前");
                object.wait();
                System.out.println("wait后");
            }
        }
    }

    注意:wait notify都是针对同一对象来操作的,例如现在有一个对象o,有10个线程都调用了o.wait,此时10个线程都是阻塞状态。如果调用了o.notify就会把10个线程中的一个线程唤醒。而notifyAll就会把所有10个线程全都给唤醒,此时就会竞争锁。

    以上がJava マルチスレッド: Thread クラスを使用します。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

    声明:
    この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。