关于java:Java-多线程二线程安全

39次阅读

共计 3452 个字符,预计需要花费 9 分钟才能阅读完成。

一、前言

<font face= 黑体 > 多线程的实现形式咱们曾经讲完了,明天咱们来讲 线程平安

二、线程平安

2.1、线程平安概述

<font face= 黑体 > 如果有多个线程在同时运行,而这些线程可能会同时拜访某一共享变量,这样就会产生线程平安问题。

<font face= 黑体 > 咱们通过一个卖票案例来演示线程平安问题:

public class RunnableImpl implements Runnable {

    // 定义一个多线程共享的票源
    private int ticket = 100;

    // 卖票
    @Override
    public void run() {while (true) {if (ticket > 0) {System.out.println(Thread.currentThread().getName() + "--> 正在卖第" + ticket + "张票");
                ticket--;
            }
        }
    }
}

<font face= 黑体 > 测试类:

public class TestSellTicket {

    // 创立三个线程,同时开启,对共享的票进行发售
    public static void main(String[] args) {
        // 创立 Runnable 接口的实现类
        RunnableImpl runnable = new RunnableImpl();
        // 创立 Thread
        Thread t0 = new Thread(runnable);
        Thread t1 = new Thread(runnable);
        Thread t2 = new Thread(runnable);
        t0.start();
        t1.start();
        t2.start();}
}

<font face= 黑体 > 测试后果如下所示:

<font face= 黑体 > 能够看到运行后果中呈现了反复的票,这就是线程不平安。线程平安问题都是由全局变量及动态变量引起的。若每个线程中对全局变量、动态变量只有读操作,而无写操作,一般来说,这个全局变量是线程平安的;若有多个线程同时执行写操作,个别都须要思考线程同步,否则的话就可能呈现线程平安的问题。

2.2、线程同步

<font face= 黑体 > 当咱们应用多个线程拜访同一资源的时候,且多个线程中对资源有写的操作,就容易呈现线程平安问题。

<font face= 黑体 > 要解决上述多线程并发拜访一个资源的安全性问题: 也就是解决反复票与不存在票问题,Java 中提供了同步机制 (synchronized) 来解决。

<font face= 黑体 > 根据上述卖票案例简述:

窗口 1 线程进入操作的时候,窗口 2 和窗口 3 线程只能在外等着,窗口 1 操作完结,窗口 1 和窗口 2 和窗口 3 才有机会进入代码去执行。也就是说在某个线程批改共享资源的时候,其余线程不能批改该资源,期待批改结束同步之后,能力去争夺 CPU 资源,实现对应的操作,保障了数据的同步性,解决了线程不平安的景象。

<font face= 黑体 > 为了保障每个线程都能失常执行原子操作,Java 引入了线程同步机制。次要有以下三种形式:

  1. <font face= 黑体 > 同步代码块;
  2. <font face= 黑体 > 同步办法;
  3. <font face= 黑体 > 锁机制;

2.2.1、同步代码块

<font face= 黑体 >synchronized 关键字能够用于办法中的某个区块中,示意只对这个区块的资源履行互斥拜访。

<font face= 黑体 > 格局:

synchronized(同步锁){须要同步操作的代码}

<font face= 黑体 > 留神:

  1. <font face= 黑体 > 代码块中的锁对象能够是任意对象;
  2. <font face= 黑体 > 然而必须保障多个线程应用的锁对象是同一个;
  3. <font face= 黑体 > 锁对象的作用就是将同步代码块锁住,只容许一个线程在同步代码块中执行;

<font face= 黑体 > 利用同步代码块实现线程平安的具体代码实现如下所示:

public class RunnableImpl implements Runnable {

    // 定义一个多线程共享的票源
    private int ticket = 100;

    // 创立一个锁对象
    Object object = new Object();

    // 卖票
    @Override
    public void run() {while (true) {
            // 创立同步代码块
            synchronized (object) {if (ticket > 0) {
                    // 模仿卖票工夫
                    try {Thread.sleep(10);
                    } catch (InterruptedException e) {e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName() + "--> 正在卖第" + ticket + "张票");
                    ticket--;
                }
            }
        }
    }
}

<font face= 黑体 > 同步技术的原理:

<font face= 黑体 > 同步中的线程,没有执行结束不会开释锁,同步外的线程没有锁进不去同步,所以就能够保障同步中始终只有一个线程在执行,保障了线程平安。然而程序会频换的判断锁、获取锁和开释锁导致程序的效率升高。

2.2.2、同步办法

<font face= 黑体 > 应用 synchronized 润饰的办法, 就叫做同步办法, 保障 A 线程执行该办法的时候, 其余线程只能在办法外等着。

<font face= 黑体 > 格局:

public synchronized void method(){可能会产生线程平安问题的代码}

<font face= 黑体 > 利用同步办法实现线程平安的具体代码实现如下所示:

public class RunnableImpl implements Runnable {

    // 定义一个多线程共享的票源
    private int ticket = 100;

    // 卖票
    @Override
    public void run() {while (true) {sellTicket();
        }
    }

    /**
     * 定义一个同步办法
     */
    public synchronized void sellTicket() {if (ticket > 0) {

            try {Thread.sleep(10);
            } catch (InterruptedException e) {e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName() + "--> 正在卖第" + ticket + "张票");
            ticket--;
        }
    }
}

2.2.3、Lock 锁

<font face= 黑体 >java.util.concurrent.locks.Lock 机制提供了比 synchronized 代码块和 synchronized 办法更宽泛的锁定操作,同步代码块 / 同步办法 具备的性能 Lock 都有, 除此之外更弱小, 更体现面向对象。

<font face= 黑体 >Lock 锁也称同步锁,加锁与开释锁办法化了,如下:

  1. <font face= 黑体 >public void lock() : 获取锁;
  2. <font face= 黑体 >public void lock() : 开释同步锁;

<font face= 黑体 >Lock 锁应用步骤:

  1. <font face= 黑体 > 在成员地位创立一个 ReentrantLock 对象;
  2. <font face= 黑体 > 在可能呈现平安问题的代码前调用 Lock 接口中的办法 lock();
  3. <font face= 黑体 > 在可能呈现平安问题的代码前调用 Lock 接口中的办法 unLock();

<font face= 黑体 > 利用 Lock 锁实现线程平安的具体代码实现如下所示:

public class RunnableImpl implements Runnable {

    // 定义一个多线程共享的票源
    private int ticket = 100;

    // 在成员地位创立一个 ReentrantLock 对象;Lock l = new ReentrantLock(); 

    // 卖票
    @Override
    public void run() {while (true) {l.lock();
            if (ticket > 0) {

                try {Thread.sleep(10);
                } catch (InterruptedException e) {e.printStackTrace();
                }

                System.out.println(Thread.currentThread().getName() + "--> 正在卖第" + ticket + "张票");
                ticket--;
            }
            l.unlock();}
    }
}

三、小结

<font face= 黑体 >多线程 的线程平安咱们曾经讲完了,下一节咱们来讲 线程状态

四、源码

<font face= 黑体 > 文章中用到的所有源码已上传至 github,有须要的能够去下载。

正文完
 0