Javaマルチスレッド研究ノート(4)スレッドセーフの問題

スレッドセーフとは:

    マルチスレッド環境では、スレッドセーフは避けられません。Javaでは、synchronizeキーワードを使用してスレッドセーフの問題を解決できます。
スレッドの不安定さの例:

public class Example09 {
    
    
    public static void main(String[] args) {
    
    
        BusTicket3 busTicket = new BusTicket3();
        //开启四个线程
        new Thread(busTicket, "窗口1").start();
        new Thread(busTicket, "窗口2").start();
        new Thread(busTicket, "窗口3").start();
        new Thread(busTicket, "窗口4").start();
    }
}
class BusTicket3 implements Runnable {
    
    
    private int ticket = 10;//车票数量
    public void run() {
    
    
        while (ticket > 0) {
    
    
            //获取线程的名字
            String threadName = Thread.currentThread().getName();
            System.out.println(threadName + "正在发第" + ticket-- + "张票");
        }
    }
}

運用結果:
ここに画像の説明を挿入
    上記の運用結果からわかるように、4つのチケットウィンドウで同時に10枚のチケットを販売しますが、通常、同じチケットを2回販売することはありません。同じチケットが繰り返し販売される原因となるのは、スレッドの不安定さです。スレッドセーフは毎回発生するわけではなく、セキュリティ上の問題がないという意味でもありません。この問題を解決するために、synchronizeキーワードを使用します。

同期の2つの使用法:

1.コードブロックを同期します

public class Example09 {
    
    
    public static void main(String[] args) {
    
    
        BusTicket3 busTicket = new BusTicket3();
        //开启四个线程
        new Thread(busTicket, "窗口1").start();
        new Thread(busTicket, "窗口2").start();
        new Thread(busTicket, "窗口3").start();
        new Thread(busTicket, "窗口4").start();
    }
}
class BusTicket3 implements Runnable {
    
    
    private int ticket = 10;//车票数量
    Object lock = new Object();//定义一个对象,作为同步代码块的锁
    public void run() {
    
    
        while (ticket > 0) {
    
    
            synchronized (lock) {
    
    //定义同步代码块
                try {
    
    
                    Thread.sleep(500);
                } catch (Exception e) {
    
    
                    e.printStackTrace();
                }
                if (ticket > 0) {
    
    
                    //获取线程的名字
                    String threadName = Thread.currentThread().getName();
                    System.out.println(threadName + "正在发第" + ticket-- + "张票");
                } else {
    
    
                    break;
                }
            }
        }
    }
}

結果を自分で実行して、効果を確認できます

2.同期方法

public class Example09 {
    
    
    public static void main(String[] args) {
    
    
        BusTicket3 busTicket = new BusTicket3();
        //开启四个线程
        new Thread(busTicket, "窗口1").start();
        new Thread(busTicket, "窗口2").start();
        new Thread(busTicket, "窗口3").start();
        new Thread(busTicket, "窗口4").start();
    }
}
class BusTicket3 implements Runnable {
    
    
    private int ticket = 10;//车票数量
    public void run() {
    
    
       while (ticket > 0) {
    
    
           try {
    
    
               sellTicket();
               if (ticket <= 0) break;
           }catch (Exception e){
    
    
               e.printStackTrace();
           }
        }
    }
    //使用synchronized修饰卖票的方法
    private synchronized void sellTicket() throws Exception{
    
    
        if (ticket > 0){
    
    
            Thread.sleep(500);
            //获取线程的名字
            String threadName = Thread.currentThread().getName();
            System.out.println(threadName + "正在发第" + ticket-- + "张票");
        }
    }
}

ロックの使用法:

    LockはJava1.6で導入されました。Lockの導入により、ロックの操作性が向上します。これはどういう意味ですか?つまり、手動でロックを取得して解放する必要がある場合、タイムアウト取得の取得と同期機能を中断することもできますが、使用の観点からは、ロックは明らかに同期されていないため、便利ですばやく使用できます。

ロックAPI

ここに画像の説明を挿入

ロック方式の使用

例:

public class Example10 {
    
    
    public static void main(String[] args) {
    
    
        BusTicket4 busTicket = new BusTicket4();
        //开启两个线程
        new Thread(busTicket, "窗口1").start();
        new Thread(busTicket, "窗口2").start();
    }
}
//BusTicket4实现Runnable接口
class BusTicket4 implements Runnable {
    
    
    private Lock lock = new ReentrantLock(); // ReentrantLock是Lock的子类
    private int ticket = 10;//车票数量
    public void run() {
    
    
        lock.lock();//加锁
        for (int i = 1; i <= 5; i++) {
    
    
            System.out.println(Thread.currentThread().getName() + "在出售第" + i + "张");
            try {
    
    
                Thread.sleep(100);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }
        lock.unlock();//释放锁
    }
}

演算結果:
ここに画像の説明を挿入

tryLock()メソッドの使用

tryLock()、ロックはアイドル状態のときにのみ取得できます(ロックが取得されない場合、ロックは待機せず、ロックが取得されない場合、lock()はブロック状態になります)、戻り値がブール型の場合、ロックの取得はtrueです。それ以外の場合はfalseです。
例:

public class Example11 {
    
    
    public static void main(String[] args) {
    
    
        BusTicket5 busTicket = new BusTicket5();
        //开启两个线程
        new Thread(busTicket, "窗口1").start();
        new Thread(busTicket, "窗口2").start();
    }
}

//BusTicket4实现Runnable接口
class BusTicket5 implements Runnable {
    
    
    private Lock lock = new ReentrantLock(); // ReentrantLock是Lock的子类
    private int ticket = 10;//车票数量
    public void run() {
    
    
        if (lock.tryLock()) {
    
    //lock.tryLock()返回值是一个Boolean值
            System.out.println(Thread.currentThread().getName() + ":获得锁");
            for (int i = 1; i <= ticket; i++) {
    
    
                System.out.println(Thread.currentThread().getName() + "在出售第" + i + "张");
                try {
    
    
                    Thread.sleep(100);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
            lock.unlock();
        }else {
    
    
            System.out.println(Thread.currentThread().getName() + ":未获得锁");
        }
    }
}

演算結果:
ここに画像の説明を挿入

tryLock(長時間、TimeUnit単位)メソッドの使用

    ロックが使用可能な場合、このメソッドはすぐに値trueを返します。ロックが使用できない場合、現在のスレッドはスレッドスケジューリングに対して無効になり、次の3つの条件のいずれかが発生する前に休止状態になります。

  • 現在のスレッドがロックを取得します。
  • 他のいくつかのスレッドは、現在のスレッドを中断します。
  • 待機時間が経過したら、false
    インスタンスを返します。
public class Example12 {
    
    
    public static void main(String[] args) {
    
    
        BusTicket6 busTicket = new BusTicket6();
        //开启两个线程
        new Thread(busTicket, "窗口1").start();
        new Thread(busTicket, "窗口2").start();
        new Thread(busTicket, "窗口3").start();
    }
}

//BusTicket4实现Runnable接口
class BusTicket6 implements Runnable {
    
    
    private Lock lock = new ReentrantLock(); // ReentrantLock是Lock的子类
    private int ticket = 10;//车票数量

    public void run() {
    
    
        try {
    
    
            if (lock.tryLock(1000, TimeUnit.MICROSECONDS)) {
    
    
                System.out.println(Thread.currentThread().getName() + ":获得锁");
                for (int i = 1; i <= ticket; i++) {
    
    
                    System.out.println(Thread.currentThread().getName() + "在出售第" + i + "张");
                    try {
    
    
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                }
                lock.unlock();
            } else {
    
    
                System.out.println(Thread.currentThread().getName() + ":未获得锁");
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }
}

いい加減にして!

おすすめ

転載: blog.csdn.net/qq_42494654/article/details/109521304