Java学習日記26日目

スレッドAPI

睡眠の遮断

スレッドは静的メソッドを提供します。

  • 静的ボイドスリープ(長いミリ秒)
  • このメソッドを実行しているスレッドを、指定されたミリ秒間ブロック状態にします。タイムアウト後、スレッドは自動的に RUNNABLE 状態に戻り、タイム スライスが再度取得されて同時実行されるのを待ちます。
package thread;

public class SleepDemo {
    
    
    public static void main(String[] args) {
    
    
        System.out.println("程序开始了!");
        try {
    
    
            Thread.sleep(5000);//主线程阻塞5秒钟
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("程序结束了!");
    }
}

sleep メソッドは例外 (InterruptedException) を処理します。

スレッドがスリープ ブロックのプロセスでスリープ メソッドを呼び出すとき、スレッドの中断() メソッドが呼び出されるとき、スリープ メソッドはこの例外をスローしてスリープ ブロックを中断します。

package thread;

/**
 * sleep方法要求必须处理中断异常:InterruptedException
 * 当一个线程调用sleep方法处于睡眠阻塞的过程中,它的interrupt()方法被调用时
 * 会中断该阻塞,此时sleep方法会抛出该异常。
 */
public class SleepDemo2 {
    
    
    public static void main(String[] args) {
    
    
        Thread lin = new Thread(){
    
    
            public void run(){
    
    
                System.out.println("林:刚美完容,睡一会吧~");
                try {
    
    
                    Thread.sleep(9999999);
                } catch (InterruptedException e) {
    
    
                    System.out.println("林:干嘛呢!干嘛呢!干嘛呢!都破了像了!");
                }
                System.out.println("林:醒了");
            }
        };

        Thread huang = new Thread(){
    
    
            public void run(){
    
    
                System.out.println("黄:大锤80!小锤40!开始砸墙!");
                for(int i=0;i<5;i++){
    
    
                    System.out.println("黄:80!");
                    try {
    
    
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
    
    
                    }
                }
                System.out.println("咣当!");
                System.out.println("黄:大哥,搞定!");
                lin.interrupt();//中断lin的睡眠阻塞
            }
        };
        lin.start();
        huang.start();
    }
}
デーモンスレッド

デーモン スレッドは、バックグラウンド スレッドとも呼ばれます。

  • デーモン スレッドは、setDaemon(boolean on) メソッドを呼び出す通常のスレッドによって設定されるため、作成は通常のスレッドと変わりません。
  • デーモンスレッドの終了タイミングは、通常のスレッド、つまりプロセスの終了とは少し異なります。
  • プロセスの終了: プロセス内のすべての通常のスレッドが終了すると、プロセスは終了し、実行中のすべてのデーモン スレッドはこの時点で強制終了されます。
package thread;

/**
 * 守护线程
 * 守护线程是通过普通线程调用setDaemon(true)设置而转变的。因此守护线程创建上
 * 与普通线程无异。
 * 但是结束时机上有一点不同:进程结束。
 * 当一个java进程中的所有普通线程都结束时,该进程就会结束,此时会强制杀死所有正在
 * 运行的守护线程。
 */
public class DaemonThreadDemo {
    
    
    public static void main(String[] args) {
    
    
        Thread rose = new Thread(){
    
    
            public void run(){
    
    
                for(int i=0;i<5;i++){
    
    
                    System.out.println("rose:let me go!");
                    try {
    
    
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
    
    
                    }
                }
                System.out.println("rose:啊啊啊啊啊啊AAAAAAAaaaaa....");
                System.out.println("噗通");
            }
        };

        Thread jack = new Thread(){
    
    
            public void run(){
    
    
                while(true){
    
    
                    System.out.println("jack:you jump!i jump!");
                    try {
    
    
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
    
    
                    }
                }
            }
        };
        rose.start();
        jack.setDaemon(true);//设置守护线程必须在线程启动前进行
        jack.start();

    }
}

通常、特定のスレッドのタスクがいつ停止しても気にしない場合は、常に実行できますが、プログラムの主要な作業が終了したらそれに従う必要があるため、このようなタスクはデーモン スレッドでの実行に適しています (たとえば、GC はデーモン スレッドで実行されます)。

マルチスレッド同時実行の安全性の問題

当多个线程并发操作同一临界资源,由于线程切换时机不确定,导致操作临界资源的顺序出现混乱严重时可能导致系统瘫痪.
临界资源:操作该资源的全过程同时只能被单个线程完成.
package thread;

/**
 * 多线程并发安全问题
 * 当多个线程并发操作同一临界资源,由于线程切换的时机不确定,导致操作顺序出现
 * 混乱,严重时可能导致系统瘫痪。
 * 临界资源:同时只能被单一线程访问操作过程的资源。
 */
public class SyncDemo {
    
    
    public static void main(String[] args) {
    
    
        Table table = new Table();
        Thread t1 = new Thread(){
    
    
            public void run(){
    
    
                while(true){
    
    
                    int bean = table.getBean();
                    Thread.yield();
                    System.out.println(getName()+":"+bean);
                }
            }
        };
        Thread t2 = new Thread(){
    
    
            public void run(){
    
    
                while(true){
    
    
                    int bean = table.getBean();
                    /*
                        static void yield()
                        线程提供的这个静态方法作用是让执行该方法的线程
                        主动放弃本次时间片。
                        这里使用它的目的是模拟执行到这里CPU没有时间了,发生
                        线程切换,来看并发安全问题的产生。
                     */
                    Thread.yield();
                    System.out.println(getName()+":"+bean);
                }
            }
        };
        t1.start();
        t2.start();
    }
}

class Table{
    
    
    private int beans = 20;//桌子上有20个豆子

    public int getBean(){
    
    
        if(beans==0){
    
    
            throw new RuntimeException("没有豆子了!");
        }
        Thread.yield();
        return beans--;
    }
}

同期されたキーワード

同期を使用するには 2 つの方法があります
  • メソッドが変更されました。この時点でメソッドは同期メソッドになります。
  • 同期ブロック。キューに入れる必要があるコードフラグメントをより正確にロックできます。
同期方法

メソッドが synchronized で変更される場合、このメソッドは「同期メソッド」と呼ばれます。つまり、メソッド内で複数のスレッドを同時に実行することはできません。それらは 1 つずつ順番に実行することしかできません。同じ重要なリソースを同時に操作するプロセスを同期実行に変更することで、同時実行セキュリティの問題を効果的に解決できます。

package thread;

/**
 * 多线程并发安全问题
 * 当多个线程并发操作同一临界资源,由于线程切换的时机不确定,导致操作顺序出现
 * 混乱,严重时可能导致系统瘫痪。
 * 临界资源:同时只能被单一线程访问操作过程的资源。
 */
public class SyncDemo {
    
    
    public static void main(String[] args) {
    
    
        Table table = new Table();
        Thread t1 = new Thread(){
    
    
            public void run(){
    
    
                while(true){
    
    
                    int bean = table.getBean();
                    Thread.yield();
                    System.out.println(getName()+":"+bean);
                }
            }
        };
        Thread t2 = new Thread(){
    
    
            public void run(){
    
    
                while(true){
    
    
                    int bean = table.getBean();
                    /*
                        static void yield()
                        线程提供的这个静态方法作用是让执行该方法的线程
                        主动放弃本次时间片。
                        这里使用它的目的是模拟执行到这里CPU没有时间了,发生
                        线程切换,来看并发安全问题的产生。
                     */
                    Thread.yield();
                    System.out.println(getName()+":"+bean);
                }
            }
        };
        t1.start();
        t2.start();
    }
}

class Table{
    
    
    private int beans = 20;//桌子上有20个豆子

    /**
     * 当一个方法使用synchronized修饰后,这个方法称为同步方法,多个线程不能
     * 同时执行该方法。
     * 将多个线程并发操作临界资源的过程改为同步操作就可以有效的解决多线程并发
     * 安全问题。
     * 相当于让多个线程从原来的抢着操作改为排队操作。
     */
    public synchronized int getBean(){
    
    
        if(beans==0){
    
    
            throw new RuntimeException("没有豆子了!");
        }
        Thread.yield();
        return beans--;
    }
}
同期ブロック

同期の範囲を効果的に縮小すると、同時実行の安全性を確保するという前提の下で、可能な限り同時実行効率を向上させることができ、同期ブロックは、実行のために複数のスレッドをキューに入れる必要があるコード フラグメントをより正確に制御できます。

文法:

synchronized(同步监视器对象){
    
    
   需要多线程同步执行的代码片段
}
同期モニター オブジェクトはロックされたオブジェクトです。同期ブロック内のコードが複数のスレッドによって同期的に実行されるようにするには、複数のスレッドから見える同期モニター オブジェクトが同じである必要があります。
package thread;

/**
 * 有效的缩小同步范围可以在保证并发安全的前提下尽可能提高并发效率。
 *
 * 同步块
 * 语法:
 * synchronized(同步监视器对象){
 *     需要多个线程同步执行的代码片段
 * }
 * 同步块可以更准确的锁定需要多个线程同步执行的代码片段来有效缩小排队范围。
 */
public class SyncDemo2 {
    
    
    public static void main(String[] args) {
    
    
        Shop shop = new Shop();
        Thread t1 = new Thread(){
    
    
            public void run(){
    
    
                shop.buy();
            }
        };
        Thread t2 = new Thread(){
    
    
            public void run(){
    
    
                shop.buy();
            }
        };
        t1.start();
        t2.start();
    }
}

class Shop{
    
    
    public void buy(){
    
    
        /*
            在方法上使用synchronized,那么同步监视器对象就是this。
         */
//    public synchronized void buy(){
    
    
        Thread t = Thread.currentThread();//获取运行该方法的线程
        try {
    
    
            System.out.println(t.getName()+":正在挑衣服...");
            Thread.sleep(5000);
            /*
                使用同步块需要指定同步监视器对象,即:上锁的对象
                这个对象可以是java中任何引用类型的实例,只要保证多个需要排队
                执行该同步块中代码的线程看到的该对象是"同一个"即可
             */
            synchronized (this) {
    
    
//            synchronized (new Object()) {//没有效果!
                System.out.println(t.getName() + ":正在试衣服...");
                Thread.sleep(5000);
            }

            System.out.println(t.getName()+":结账离开");
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }

    }
}
静的メソッドで同期されたメソッドを使用する

静的メソッドで synchronized を使用すると、そのメソッドは同期メソッドになります。静的メソッドはクラスに属しているため、同期効果が必要です。

静的メソッドで使用される同期監視オブジェクトは、現在のクラスのクラスオブジェクト(Classのインスタンス)です。

注: クラス オブジェクトは、後のリフレクション ナレッジ ポイントで紹介されます。

package thread;

/**
 * 静态方法上如果使用synchronized,则该方法一定具有同步效果。
 */
public class SyncDemo3 {
    
    
    public static void main(String[] args) {
    
    
        Thread t1 = new Thread(){
    
    
            public void run(){
    
    
                Boo.dosome();
            }
        };
        Thread t2 = new Thread(){
    
    
            public void run(){
    
    
                Boo.dosome();
            }
        };
        t1.start();
        t2.start();
    }
}
class Boo{
    
    
    /**
     * synchronized在静态方法上使用是,指定的同步监视器对象为当前类的类对象。
     * 即:Class实例。
     * 在JVM中,每个被加载的类都有且只有一个Class的实例与之对应,后面讲反射
     * 知识点的时候会介绍类对象。
     */
    public synchronized static void dosome(){
    
    
            Thread t = Thread.currentThread();
            try {
    
    
                System.out.println(t.getName() + ":正在执行dosome方法...");
                Thread.sleep(5000);
                System.out.println(t.getName() + ":执行dosome方法完毕!");
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }
    }
}
同期ブロックが静的メソッドで使用される場合、指定されたロック オブジェクトは通常、現在のクラスのクラス オブジェクトでもあります。
class Boo{
    
    
    public static void dosome(){
    
    
        /*
            静态方法中使用同步块时,指定同步监视器对象通常还是用当前类的类对象
            获取方式为:类名.class
         */
        synchronized (Boo.class) {
    
    
            Thread t = Thread.currentThread();
            try {
    
    
                System.out.println(t.getName() + ":正在执行dosome方法...");
                Thread.sleep(5000);
                System.out.println(t.getName() + ":执行dosome方法完毕!");
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }
    }
}
ミューテックス

複数のスレッドが異なるコード フラグメントを実行するが、これらのコード フラグメントを同時に実行できない場合は、相互排他として設定する必要があります。

synchronized を使用して複数のコード フラグメントをロックし、指定された同期モニターが同じである場合、これらのコード フラグメントは相互排他的になります。

package thread;

/**
 * 互斥锁
 * 当使用synchronized锁定多个不同的代码片段,并且指定的同步监视器对象相同时,
 * 这些代码片段之间就是互斥的,即:多个线程不能同时访问这些方法。
 */
public class SyncDemo4 {
    
    
    public static void main(String[] args) {
    
    
        Foo foo = new Foo();
        Thread t1 = new Thread(){
    
    
            public void run(){
    
    
                foo.methodA();
            }
        };
        Thread t2 = new Thread(){
    
    
            public void run(){
    
    
                foo.methodB();
            }
        };
        t1.start();
        t2.start();
    }
}
class Foo{
    
    
    public synchronized void methodA(){
    
    
        Thread t = Thread.currentThread();
        try {
    
    
            System.out.println(t.getName()+":正在执行A方法...");
            Thread.sleep(5000);
            System.out.println(t.getName()+":执行A方法完毕!");
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }
    public synchronized void methodB(){
    
    
        Thread t = Thread.currentThread();
        try {
    
    
            System.out.println(t.getName()+":正在执行B方法...");
            Thread.sleep(5000);
            System.out.println(t.getName()+":执行B方法完毕!");
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }
}

要約する

デーモン スレッドと通常のスレッドの違い: デーモン スレッドは、通常のスレッドから setDaemon(true) を呼び出すことによって設定されます。

主な違いは、Java プロセス内のすべての通常のスレッドが終了するとプロセスが終了し、まだ実行中のすべてのデーモン スレッドが終了前に強制終了されることです。

焦点: マルチスレッド同時実行のセキュリティ問題

  • マルチスレッド同時実行の安全性の問題とは次のとおりです。

    複数のスレッドが同じ重要なリソース上で同時に動作する場合、スレッド切り替えのタイミングが不確実であるため、実行順序が混乱します。

    解決:

    同時操作を同期操作に変更すると、マルチスレッドの同時実行セキュリティの問題を効果的に解決できます。

  • 同期と非同期の概念: 同期と非同期はどちらもマルチスレッドの実行方法です。

    マルチスレッド実行は非同期実行、マルチスレッド実行は順次実行されます。これが同期実行です。

  • synchronized の 2 つの使用法

    1. メソッド上で直接宣言する このときのメソッドは同期メソッドと呼ばれ、同期メソッドは同時に 1 つのスレッドのみ実行できます。

    2. 同期ブロックを推奨します。同期ブロックは、同期して実行する必要があるコード フラグメントをより正確に制御できます。

    同期の範囲を効果的に縮小すると、同時実行の安全性を確保することを前提として同時実行効率を向上させることができます。

  • 同期モニター オブジェクトの選択:

    同期メンバメソッドの場合、同期監視オブジェクトは指定できません。

    同期された静的メソッドの場合、同期モニター オブジェクトは指定できません。指定できるのはクラス オブジェクトのみです。

    同期ブロックの場合、同期モニター オブジェクトを自分で指定する必要があります。選択原則は次のとおりです。

    1. 参照型である必要があります

    2. 同期ブロックを実行する必要がある複数のスレッドから認識されるオブジェクトは同じである必要があります。

  • 相互排除

    複数のコード フラグメントが複数の synchronized で修飾され、指定された同期モニターがすべて同じオブジェクトである場合、これらのコード フラグメントは相互に排他的であり、複数のスレッドがこれらのコード フラグメントに対して同時に実行することはできません。

おすすめ

転載: blog.csdn.net/weixin_43121885/article/details/130315215