Java多线程学习笔记(四)线程安全问题

什么是线程安全:

    在多线程环境下,线程安全是避免不了的,在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-- + "张票");
        }
    }
}

运行结果:
在这里插入图片描述
    从上面的运行结果可以看出来,四个售票窗口同时卖十张车票,正常是同一张车票不会卖出去两次。正是用于线程不安全导致这同一张车票售出重复问题。线程安全并不是每次都会发生,不发生并不代表不存在安全问题。我们使用synchronize关键字解决这个问题。

synchronize两种用法:

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的用法:

    Lock是在Java1.6被引入进来的,Lock的引入让锁有了可操作性,什么意思?就是我们在需要的时候去手动的获取锁和释放锁,甚至我们还可以中断获取以及超时获取的同步特性,但是从使用上说Lock明显没有synchronized使用起来方便快捷。

Lock锁的API

在这里插入图片描述

lock方法的使用

实例:

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()未获得锁会进入阻塞状态 ),返回值为boolean类型,获取锁则为 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() + ":未获得锁");
        }
    }
}

运行结果:
在这里插入图片描述

扫描二维码关注公众号,回复: 12610058 查看本文章

tryLock​(long time, TimeUnit unit)方法的使用

    如果锁定可用,则此方法立即返回值true。如果锁不可用,则当前线程将被禁用以进行线程调度,并且在发生以下三种情况之一之前处于休眠状态:

  • 当前线程获取锁。
  • 其他一些线程中断当前线程。
  • 等待时间过去了,返回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