Java线程安全问题

2023-07-31  本文已影响0人  AC编程

一、什么是线程安全问题

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

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

二、线程不安全的原因

三、解决线程安全性

3.1 synchronized
3.1.1 同步代码块

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

同步代码块格式:

synchronized (锁){
    操作共享数据的代码
}
3.1.2 同步方法

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

同步方法格式:

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

静态同步方法格式:

修饰符 static synchronized 返回值类型 方法名(方法参数) {...}
同步静态方法的锁对象是什么呢?
类名.class
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获取锁、释放锁
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 线程安全问题

上一篇下一篇

猜你喜欢

热点阅读