一、什么是线程安全

        当多个线程访问共享资源时,每个线程都会各自对共享资源进程操作,导致数据不一致,造成程序不能正确的得到结果,此时需要让多个线程排队访问共享资源,让线程安全,才能保证数据安全的被访问。

二、实现线程安全

        让线程同步(线程排队访问共享资源),保证线程安全,其中的细节都在注释里。

        (1)使用synchronized关键字,把共享资源上锁,让每个线程访问之前排队。

                1.同步方法

        案例为:模拟买票

package com.synchronizeddd;

public class Ticket01 implements Runnable {
    // 总票数
    private int count = 100;
    // 购买票数
    private int nums = 0;


    // 重写run()方法
    @Override
    public void run() {
        while (true) {

            // 为了只让黄牛抢一张票 抢到返回true 
            // 执行return结束run()方法 也就结束了黄牛线程
            if (rob())
                return;

        }
    }

    // 同步方法
    private synchronized boolean rob() {

        if (count > 0) {
            count--;
            nums++;
            if (Thread.currentThread().getName().equals("黄牛党")) {
                System.out.println(Thread.currentThread().getName() + "抢了第" + nums + "张票,还剩" + count + "张。");
                // 为了结束黄牛线程
                return true;
            }
            System.out.println(Thread.currentThread().getName() + "抢了第" + nums + "张票,还剩" + count + "张。");
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

        }

        return false;
    }
}

        测试类:

package com.synchronizeddd;

public class Test01 {
    public static void main(String[] args) {
        // 创建线程任务 
        Ticket01 ticket = new Ticket01();

        // 创建线程
        Thread threadHN = new Thread(ticket, "黄牛党");
        Thread thread1 = new Thread(ticket, "小白");
        Thread thread2 = new Thread(ticket, "Ikun");

        // 线程就绪
        threadHN.start();
        thread1.start();
        thread2.start();

    }
}

        运行结果:

Java LRUMap保证线程安全 java如何保证线程安全_jvm

  

                2.同步代码块

                案例与上相同

package com.synchronizeddd;

public class Ticket implements Runnable {
    // 总票数
    private int count = 100;
    // 购买票数
    private int nums = 0;


    // 重写run()方法 
    @Override
    public void run() {
        while (true) {
            /* 保证线程安全 使用synchronized 实现同步代码块
             synchronized(obj){} obj为一个锁对象  锁对象要唯一  
             保证每个线程都是用同一个锁 若多个锁不能保证同步
             this在对象中指向自己 在此处指向线程任务对象
             在测试类创建线程时 每个线程的线程任务对象都是同一个 故满足锁对象唯一 可参考测试类
             */
            synchronized (this) {
                if (count > 0) {
                    count--;
                    nums++;
                    if (Thread.currentThread().getName().equals("黄牛党")){
                        System.out.println(Thread.currentThread().getName() + "抢了第" + nums + "张票,还剩" + count + "张。");
                        // 结束run()方法 即黄牛的线程
                        return;
                    }
                    System.out.println(Thread.currentThread().getName() + "抢了第" + nums + "张票,还剩" + count + "张。");
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }

                }
            }

        }
    }
}

        测试类:

package com.synchronizeddd;

public class Test {
    public static void main(String[] args) {
        // 创建线程任务 下面创建的线程都是使用此线程任务 保证锁对象唯一
        Ticket ticket = new Ticket();
        
        // 创建线程
        Thread threadHN = new Thread(ticket, "黄牛党");
        Thread thread1 = new Thread(ticket, "小白");
        Thread thread2 = new Thread(ticket, "Ikun");

        // 线程就绪
        threadHN.start();
        thread1.start();
        thread2.start();

    }
}

         运行结果:

Java LRUMap保证线程安全 java如何保证线程安全_Java LRUMap保证线程安全_02

 

        (2)使用Lock锁

        案例同上

package com.lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyLock01 implements Runnable {
    // 总票数
    private int count = 100;
    // 购买票数
    private int nums = 0;

    //使用静态引用 确保锁对象唯一
    static Lock lock = new ReentrantLock();

    // 重写run()方法
    @Override
    public void run() {
        while (true) {
            // 调用lock()方法  进行上锁  
            // 让若把锁放在循环外 则开不了锁 因为死循环没结束
            lock.lock();

            if (count > 0) {
                // 修改票数
                count--;
                nums++;

                // 黄牛党只能给一张票 不能让他们太嚣张
                if (Thread.currentThread().getName().equals("黄牛党")) {
                    System.out.println(Thread.currentThread().getName() + "抢了第" + nums + "张票,还剩" + count + "张。");
                    // 结束run()方法 即黄牛的线程
                    // 但注意 return结束方法后 并没有开锁 会造成死锁
                    // 结束之前需要开锁
                    lock.unlock();
                    return;
                }

                // 普通人
                System.out.println(Thread.currentThread().getName() + "抢了第" + nums + "张票,还剩" + count + "张。");

                //模拟网络延迟
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

            // 开锁 当一个线程访问结束后 一个开锁让其他线程进来访问
            lock.unlock();

        }

    }
}

        测试类:

package com.lock;

import com.synchronizeddd.Ticket;

public class Test01 {
    public static void main(String[] args) {
        // 创建线程任务
        MyLock01 ticket = new MyLock01();

        // 创建线程
        Thread threadHN = new Thread(ticket, "黄牛党");
        Thread thread1 = new Thread(ticket, "小白");
        Thread thread2 = new Thread(ticket, "Ikun");

        // 线程就绪
        threadHN.start();
        thread1.start();
        thread2.start();

    }
}

         运行结果:

Java LRUMap保证线程安全 java如何保证线程安全_开发语言_03