美文网首页
Java线程安全问题

Java线程安全问题

作者: AC编程 | 来源:发表于2023-07-31 08:34 被阅读0次

一、什么是线程安全问题

简单来说:多个线程同时操作同一个共享资源的时候可能会出现业务安全问题,称之为线程安全问题。

具体来说:由于操作系统中,线程的调度是抢占式执行的,或者说是随机的,这就造成线程调度执行时,线程的执行顺序是不确定的,虽然有一些代码在这种执行顺序不同的情况下也不会运行出错,但是还有一部分代码会因为执行顺序发生改变而受到影响,这就会造成程序出现Bug。对于多线程并发时会使程序出现bug的代码称作线程不安全的代码。
本质原因:线程在系统中的调度是无序的/随机的(抢占式执行)

二、线程不安全的原因

  • 抢占式执行:罪魁祸首

  • 多个线程同时修改同一个变量

  • 修改操作不是原子的:多行指令,如果指令前后有依赖关系,不能插入其他影响自身线程执行结果的指令。

  • 内存可见性:系统调用CPU执行线程内,一个线程对共享变量的修改,另一个线程能够立刻看到。

  • 指令重排序:程序执行的顺序按照代码的先后顺序执行(处理器可能会对指令进行重排序)

三、解决线程安全性

3.1 synchronized
3.1.1 同步代码块

同步代码块的出现就是为了解决线程的安全问题,其主要作用就是把操作共享数据的代码锁起来。

同步代码块格式:

synchronized (锁){
    操作共享数据的代码
}
  • 特点1:锁默认打开,有一个线程进去了,锁自动关闭
  • 特点2:里面的代码全部执行完毕,线程出来,锁自动打开
    synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁锁对象可以自己随意指定但是一定要是唯一的(建议:直接使用类的字节码文件)
3.1.2 同步方法

简单理解:就是把synchronized关键字加到方法上

同步方法格式:

修饰符 synchronized 返回值类型 方法名 (方法参数) {...}
同步方法的锁对象是什么呢?
this

静态同步方法格式:

修饰符 static synchronized 返回值类型 方法名(方法参数) {...}
同步静态方法的锁对象是什么呢?
类名.class
  • 特点1:同步方法是锁住方法里面所有的代码
  • 特点2:锁对象不能自己指定(this、字节码文件不用我们自己去写)
  • 特点3:非静态锁用this,静态锁用当前类的字节码文件对象
3.1.3 案列

电影院新上映一部电影准备买票,共开设了3个窗口,票的总量为100张,请用多线程实现。

方式一:

package com.liming.mysynchronized;

public class MyTread extends Thread{
    //表示这个类的所有对象可以共享ticket数据
    static int ticket = 0;

    @Override
    public void run() {
        while (true){
            // 同步代码块
            synchronized (MyTread.class){//锁对象,一定要是唯一的,同一个文件中字节码文件一定是唯一的
                if (ticket < 100){
                    ticket++;
                    System.out.println(getName()+"正在卖第"+ticket+"张电影票");
                }else {
                    break;
                }
            }
        }
    }
}

package com.liming.mysynchronized;

public class ThreadDemo {
    public static void main(String[] args) {
        MyTread t1 = new MyTread();
        MyTread t2 = new MyTread();
        MyTread t3 = new MyTread();
        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");
        t1.start();
        t2.start();
        t3.start();
    }
}

方式二:

package com.liming.mysynchronized;

public class MyRun implements Runnable{
    int tick = 0;
    @Override
    public void run() {
        while (true){
            synchronized (this){
                if (tick < 100){
                    tick++;
                    System.out.println(Thread.currentThread().getName()+"卖出了"+tick+"张电影票");
                }else {
                    break;
                }
            }
        }
    }
}

package com.liming.mysynchronized;

public class MyThread01 {
    public static void main(String[] args) {
        MyRun mr = new MyRun();
        Thread t1 = new Thread(mr);
        Thread t2 = new Thread(mr);
        Thread t3 = new Thread(mr);
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}
3.2 Lock锁

虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5之后提供了一个新的锁对象Lock锁。

3.2.1 Lock获取锁、释放锁
  • void lock():获取锁
  • void unlock():释放锁
    上述两个方法都需要去手动开启
3.2.2 Lock的构造方法

Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化ReentrantLock():创建一个Reentrantlock的实例。

代码演示:

package com.liming.mylock;

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

public class MyThread extends Thread{
    static int ticket = 0;
    static Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true){
            lock.lock(); //上锁
            try {
                if (ticket == 100){
                    break;
                }else {
                    Thread.sleep(10);
                    ticket++;
                    System.out.println(getName()+"卖了第"+ticket+"张票");
                }
            }catch (Exception e){
                e.printStackTrace();
            } finally {
                lock.unlock();//释放锁
            }
        }
    }
}

package com.liming.mylock;

public class ThreadDemo {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        MyThread t3 = new MyThread();
        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");
        t1.start();
        t2.start();
        t3.start();
    }
}

转载自:Java 线程安全问题

相关文章

  • volatile关键字

    线程安全问题 Java多线程带来的一个问题是数据安全问题,判断一段Java代码是否有线程安全问题可从以下几点入手:...

  • 线程安全问题

    线程安全问题 本篇主要讲解 线程安全问题,演示什么情况下会出现线程安全问题,以及介绍了 Java内存模型 、vol...

  • java多线程(壹)——线程与锁

    线程与线程安全问题 所有的线程安全问题都可以概况为三个点:原子性,可见性、有序性——————by Java多线程编...

  • java 线程安全问题的解决办法 和死锁

    线程安全问题的解决办法 线程 安全问题的解决方案:sun提供了线程同步机制让我们解决这类问题的。 java线程同步...

  • java同步机制的几种方式

    java同步机制的几种方式 出现线程安全问题: 如果存在多个线程对共享资源竞争,就可能发生线程安全问题。 一般解决...

  • 锁机制

    一、说说线程安全问题,什么是线程安全,如何保证线程安全 http://www.jasongj.com/java/t...

  • 简单聊聊 Java线程的并发

    哈喽大家好,上一篇文章我们聊了聊Java线程的基础知识,这一篇文章我们就来聊聊线程中的线程安全问题 线程安全问题 ...

  • 同步监视器锁定的释放问题

    在任何编程语言中,事关线程安全问题非常重要,而同步监测器是解决java多线程安全问题的关键,关于监测器锁定...

  • 关于AtomicInteger

    AtomicInteger JAVA原子操作的Interger类, 主要为解决多线程线程安全问题,今天拿来测试一下...

  • Java并发之synchronized

    一、前言序章   Java因为实现的是共享数据模型,在多线程操作共享数据时,会引起线程安全问题。Java为了解决线...

网友评论

      本文标题:Java线程安全问题

      本文链接:https://www.haomeiwen.com/subject/yrsgudtx.html