(JAVA)-线程的安全问题

发布时间:2023年12月21日

让我们先来用一个常见的需求来进行思考:电影院三个窗口共同卖出100张票。

我们很容易想到可以用三个线程来模拟三个窗口解决这个问题

public class test {
    public static void main(String[] args) {
        MyThread t1=new MyThread("t1");
        MyThread t2=new MyThread("t2");
        MyThread t3=new MyThread("t3");
t1.start();
t2.start();
t3.start();
    }



}
 class MyThread extends Thread {
     public MyThread() {

     }

     public MyThread(String name) {
         super(name);
     }

    static int ticket=0;//代表票数,static表示三个窗口共享

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

我们看看结果发现他会有重复的票,甚至有出现超出范围的票,这是因为可能在ticket在自增之后,cpu的执行权随时会被其他线程抢走,而多个线程共享一个ticket变量。

为了避免产生这种线程安全问题,提供了三种解决方案

1.同步代码块

同步代码块能将共享数据代码锁起来,当在某个线程中执行这段代码块时,该线程会获取我们指定的对象的锁,从而使得其他线程无法同时访问该代码块。

synchronized(锁对象){
操作共享数据的代码
}

1.锁默认打开,当有一个线程进去,线程自动关闭

2.代码执行完毕,线程出来,锁自动打开

3.锁对象可以是任意的,但要是相同唯一的,一般用当前文件的字节码文件对象。


 class MyThread extends Thread {
     public MyThread() {

     }

     public MyThread(String name) {
         super(name);
     }
     static int ticket=0;//代表票数

    @Override
    public void run() {
while(true) {
    synchronized (MyThread.class) {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        ticket++;
        if (ticket > 100) {
            break;
        } else {
            System.out.println(currentThread().getName() + "正在卖第" + ticket + "张票");
        }
    }
}
    }
}

2.同步方法

如果想要把一个方法里面的代码全部锁起来,就可以用同步方法

格式:

修饰符 synchronized 返回值 方法名(){};

1.同步方法是锁住方法里面的所有代码

2.锁对象不能自己指定(修饰非静态方法:this? ? 静态:当前类的字节码文件对象)

因此当用于 线程继承Thread类 这种创造线程方法时仍然不能解决安全问题。

class MyRunable implements Runnable{
    int ticket=0;
    @Override
    public void run() {
        while(true){

            if (extracted()) break;

        }
    }

    private synchronized boolean extracted() {
        if(ticket==100)
            return true;
        else{
            ticket++;
            System.out.println(Thread.currentThread().getName()+"在卖第"+ticket+"张票");
        }
        return false;
    }
}


public class test {
    public static void main(String[] args) {
        MyRunable mr=new MyRunable();
        Thread t1=new Thread(mr);
        Thread t2=new Thread(mr);
        Thread t3=new Thread(mr);
        t1.setName("t1");
        t1.setName("t2");
        t1.setName("t3");
        t1.start();
        t2.start();
        t3.start();
    }
}

3.lock锁

lock能够进行手动的获得锁和释放锁

1.lock是接口,不能直接实例化,下文采用他的实现类Reentrantlock来实例化

2.创建lock对象要用static关键字修饰(使多个线程公共用一把锁)

3.关锁代码不能跳过,否则程序不能停止,可以使用finally代码实现

public class test {
    public static void main(String[] args) {
        MyThread t1=new MyThread("t1");
        MyThread t2=new MyThread("t2");
        MyThread t3=new MyThread("t3");
        t1.start();
        t2.start();
        t3.start();
    }

}
class MyThread extends Thread {
    public MyThread() {

    }

    public MyThread(String name) {
        super(name);
    }
    static int ticket=0;//代表票数
static Lock lock=new ReentrantLock();
    @Override
    public void run() {
 try {
        while(true) {
            lock.lock();
           
                Thread.sleep(10);
           
            ticket++;
            if (ticket > 100) {
               break;
            } else {
                System.out.println(currentThread().getName() + "正在卖第" + ticket + "张票");
            }
 } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }finally{
            lock.unlock();
}
        }
        }
    }

文章来源:https://blog.csdn.net/fdvvg/article/details/135132241
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。