Java基础之线程安全

回顾

在上一篇 Java基础之多线程编程,咱们讲解了多线程的实现,运行起来彷佛也没什么问题,可是咱们若加一段代码java

class Window implements Runnable{//实现接口
    int ticket=100;
    @Override
    public void run() {
        while (true){
            if(ticket>0){
                try {
                    Thread.currentThread().sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"售票:票号为:"+ ticket--);
            }else{
                break;
            }
        }
    }
}
Window w=new Window();
        Thread t1=new Thread(w);
        Thread t2=new Thread(w);
        Thread t3=new Thread(w);
        t1.setName("窗口1");
        t2.setName("窗口2");
        t1.start();
        t2.start();
复制代码

相比与上一篇咱们的代码里多了编程

Thread.currentThread().sleep(10);
复制代码

sleep()方法使得当前线程阻塞10毫秒,咱们看代码运行效果安全

票卖超了,怎么回事?按代码逻辑来看,好像并没什么问题?

这就是存在 线程不安全bash

问题分析

那么咱们分析下如何出现的这个现象? 咱们假设有两个卖票线程:线程A和线程B ,此时余票还有1张,看下图多线程

文字解释下,当ticket=1时,线程A进入if判断内,接着线程A进入sleep状态,紧接着线程B得到cpu执行权开始执行, 此时ticket=1,进入if判断内 也开始sleep,而后线程A的sleep结束 恢复,开始执行,并把ticket--,此时ticket=0,而后线程B恢复, 打印tickect为0,ticket再次-1.变成了-1. 这就时三个窗口同时卖票,票卖超的缘由,也称线程不安全。ide

线程安全

上面咱们分析了致使线程不安全出现的缘由?那怎么解决呢?post

  • 咱们但愿一个线程操做共享数据结束之后,其余的线程才有机会参与共享数据的操做。

线程安全是多线程编程时的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会经过同步机制保证各个线程均可以正常且正确的执行,不会出现数据污染等意外状况。ui

用java代码来实现,主要有两种方法:this

线程的同步机制

方法一 :同步代码块

格式以下 使用synchronized关键字spa

synchronized(同步监视器){
    //须要被同步的代码块(操做共享数据的代码块)
}
复制代码

以上同步监视器 又称为“锁”,锁须要惟一,代码以下

class Window implements Runnable{//实现接口
    int ticket=100;
    @Override
    public void run() {
        while (true){
            synchronized (this){
                if(ticket>0){
                    try {
                        Thread.currentThread().sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"售票:票号为:"+ ticket--);
                }
            }
        }
    }
}
复制代码

执行后以下,正常!

方法二 :同步方法

将操做共享数据的代码 提取到一个方法内 而后用synchronized 修饰

synchronized void show(){
    //操做共享数据的代码
}
复制代码

修改卖票程序用同步方法实现以下:

class Window implements Runnable{//实现接口
    int ticket=100;
    public synchronized void show(){
        if(ticket>0){
            try {
                Thread.currentThread().sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"售票:票号为:"+ ticket--);
        }
    }
    @Override
    public  void run() {
        while (true){
            this.show();
        }
    }
}

复制代码

注意在同步方法实现中 锁默认为this 也须要惟一, 咱们用图解释下线程安全下两个线程如何操做共享数据的:

由上图咱们知道,一旦遇到操做共享数据时,线程老是同步执行的。

总结

  • 遇到多个线程操做共享数据时就会出现线程不安全问题
  • 同步方法或者同步代码块 都是为了让线程同步执行
  • 同步方法和同步代码块都须要一个对象 做为锁,这个锁要确保惟一性

喜欢本文的朋友们,欢迎长按下图关注订阅号"个人编程笔记",收看更多精彩内容~~

相关文章
相关标签/搜索