多线程同步(安全机制)——Java多线程(2)

线程安全:
如果有多个线程同时运行一段代码,并且运行结果与单线程运行结果是一样的,那么就说是线程安全的。

1.1 线程不安全案例

通过下面这个买票的案例我们会发现会出现两种情况

  • 一张票卖了多次的情况
  • 出现负票的情况
public class Client3 {
    public static void main(String[] args) {
        Ticket t = new Ticket();
        //同时开启三个售票窗口
        Thread thread = new Thread(t);
        Thread thread2 = new Thread(t);
        Thread thread3 = new Thread(t);
        thread.start();
        thread2.start();
        thread3.start();
    }
}

class Ticket implements Runnable {
    //定义100张票
    private int ticket = 100;
    @Override
    public void run() {
        while (true) {
            if (ticket < 0) {
                return;
            } else {
                try {
                    //模拟出票动作
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().toString() + "正在卖: " + ticket--);
            }
        }
    }
}
1.2 通过线程同步的方式解决

当我们使用多个线程对资源进行写操作的时候,容易出现安全性问题,这里我们可以通过使用java的同步机制Synchronized来解决

1.2.1 同步代码块

Synchronized可以对使用的代码块进行互斥访问,这里我们只需要把代码区块添加synchronized即可实现同步


class Ticket implements Runnable {
    //定义100张票
    private int ticket = 100;
    //添加一把锁
    private Object lock = new Object();
    @Override
    public void run() {
        while (true) {
            //lock的类型是任意的
            synchronized (lock) {
                if (ticket < 0) {
                    return;
                } else {
                    try {
                        //模拟出票动作
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().toString() + "正在卖: " + ticket--);
                }
            }
        }
    }
}
1.2.2 同步方法

public class Ticket implements Runnable {
    //定义100张票
    private int ticket = 100;
    @Override
    public void run() {
        while (true){
            try {
                selTicket();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    /**
    * 谁调用这个方法,锁对象就是谁(this)
    */
    public synchronized void selTicket() throws InterruptedException {
        if (ticket > 0) {
            Thread.sleep(100);
        } else {
            return;
        }
        System.out.println("窗口" + Thread.currentThread().toString() + "票" + ticket--);
    }
}
1.2.3 Lock锁

提供了比synchronized代码块和synchronized方法更广泛的锁定操作, 同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象

/**
 * Lock接口中的方法
 *      lock
 *      unlock
 *  使用步骤:
 *      Lock接口的实现类ReentrantLock
 *      1. 在成员创建ReentrantLock的实现类
 *      2. 在可能出现线程安全的位置开始调用lock()
 *      3. 在可能出现线程安全的结束位置调用lock()
 */
public class RunnableImpl implements Runnable {
    Lock lock = new ReentrantLock();
    private int ticket = 20;
    @Override
    public void run() {
        while (true){
            lock.lock();
            if (ticket >0 ){
                try {
                    Thread.sleep(100);
                    System.out.println("当前的线程"+Thread.currentThread().toString()+" "+ticket--);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    lock.unlock();
                }
            }
        }
    }
}


  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 饿汉式——线程安全的单例模式 这是一种最简单的实现方式。在类加载的时候就创建了实例,因此保证了线程安全。缺点是无论是否需要这个对象,都会在程序启动时被加载,从而浪费了一定的空间。 ```java public class Singleton { //创建 Singleton 的一个对象 private static Singleton instance = new Singleton(); //让构造函数为 private,这样该类就不会被实例化 private Singleton(){} //获取唯一可用的对象 public static Singleton getInstance(){ return instance; } } ``` 2. 懒汉式——线程不安全的单例模式 这种方式虽然达到了按需初始化的目的,但却带来了线程不安全的问题,如果多个线程同时调用 `getInstance()` 方法,那么就会创建多个实例。 ```java public class Singleton { private static Singleton instance; private Singleton(){} public static Singleton getInstance(){ if(instance == null) { instance = new Singleton(); } return instance; } } ``` 3. 懒汉式——线程安全的单例模式 使用 `synchronized` 关键字可以解决线程安全问题,但是这样每次调用 `getInstance()` 方法都会进行同步,影响程序的性能。 ```java public class Singleton { private static Singleton instance; private Singleton(){} public static synchronized Singleton getInstance() { if(instance == null) { instance = new Singleton(); } return instance; } } ``` 4. 双重校验锁——线程安全的单例模式 这是一种比较好的实现方式,使用了双重校验锁,既保证了线程安全,又实现了按需初始化,同时也减少了同步开销。 ```java public class Singleton { private volatile static Singleton instance; private Singleton(){} public static Singleton getInstance() { if(instance == null) { synchronized (Singleton.class) { if(instance == null) { instance = new Singleton(); } } } return instance; } } ``` 5. 静态内部类——线程安全的单例模式 使用静态内部类的方式可以在调用 `getInstance()` 方法时才真正创建对象,达到最佳的按需初始化效果,并且也保证了线程安全。 ```java public class Singleton { private Singleton(){} private static class SingletonHolder{ private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance(){ return SingletonHolder.INSTANCE; } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值