美文网首页
多线程操作同一对象的问题

多线程操作同一对象的问题

作者: 何几时 | 来源:发表于2020-11-25 19:13 被阅读0次

1. 抢票代码

package demo01_threadCreation;

// 多个线程操作同一个对象的情况下,线程不安全,数据紊乱
public class TestThread4 implements Runnable{

    // 票数
    private int ticketNum = 10;

    @Override
    public void run() {
        while (true)
        {
            if (ticketNum<=1) {
                break;
            }
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNum--+"票");
            // Thread.currentThread().getName() 得到当前线程的名字
        }
    }

    public static void main(String[] args) {
        TestThread4 tt = new TestThread4();
        new Thread(tt, "小明").start();
        new Thread(tt, "小红").start();
        new Thread(tt, "小强").start();
    }
}
==================terminal===================
小红拿到了第10票
小红拿到了第7票
小红拿到了第6票
小红拿到了第5票
小红拿到了第4票
小红拿到了第3票
小红拿到了第2票
小红拿到了第1票
小明拿到了第8票
小强拿到了第9票

分析:两个线程很有可能会同时操作同一个变量,这是很危险的行为,这时我们就要建立一个互斥锁

1.1 抢票代码改进版

package demo01_threadCreation;

// 多个线程操作同一个对象的情况下,线程不安全,数据紊乱
public class TestThread4 implements Runnable{

    // 票数
    private int ticketNum = 10;
    private boolean isLock = false;

    @Override
    public void run() {
        while (true)
        {
            if (ticketNum<=2) {
                break;
            }
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (!isLock)
            {
                isLock = true;
                System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNum--+"票");
            }
            // Thread.currentThread().getName() 得到当前线程的名字
            isLock = false;
        }
    }

    public static void main(String[] args) {
        TestThread4 tt = new TestThread4();
        new Thread(tt, "小明").start();
        new Thread(tt, "小红").start();
        new Thread(tt, "小强").start();
    }
}
==========terminal=============
小强拿到了第10票
小红拿到了第9票
小明拿到了第8票
小强拿到了第7票
小红拿到了第6票
小明拿到了第5票
小明拿到了第4票
小红拿到了第4票
小强拿到了第3票
小明拿到了第2票

问题:为甚么还是会存在重票?这里重票真的意味着两个线程同时在写同一个内存吗?
回答:这还是线程安全问题,也就是即使是以类变量(static)定义一个锁,或者用 boolean 类型的成员变量来作为线程锁,实际上都是不可行的,还是会出现同一时间有几个线程同时写入同一个地址。

2.0 synchronized 线程同步

参考这篇博客:https://www.cnblogs.com/littlepage/p/11655265.html#%E4%B8%80%E3%80%81%E7%BA%BF%E7%A8%8B%E7%9A%84%E5%AE%9A%E4%B9%89

概念:线程同步指的是,当有一个线程在对内存进行操作时,其他线程不能操作这块内存(也就是写入操作),直至到该线程结束操作,其他线程才能对改内存地址进行操作。
多线程同时访问同一块内存地址会有线程安全问题,加锁就很有必要了。

抢票改进代码2.0
public synchronized void run(){...}

package demo01_threadCreation;

// 多个线程操作同一个对象的情况下,线程不安全,数据紊乱
public class TestThread4_1 implements Runnable{

    // 票数
    private static int ticketNum = 10;

    @Override
    public synchronized void run() {
        while (true)
        {
            if (ticketNum<=0) {
                break;
            }
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNum--+"票");

            // Thread.currentThread().getName() 得到当前线程的名字

        }
    }

    public static void main(String[] args) {
        TestThread4_1 tt = new TestThread4_1();
        new Thread(tt, "小明").start();
        new Thread(tt, "小红").start();
        new Thread(tt, "小强").start();
    }
}
===============terminal====================
小强拿到了第10票
小明拿到了第9票
小明拿到了第8票
小强拿到了第7票
小红拿到了第6票
小红拿到了第5票
小强拿到了第5票
小强拿到了第4票
小红拿到了第3票
小明拿到了第2票
小红拿到了第1票

在方法名前加 synchronized ,实际上等同于

package demo01_threadCreation;

// 多个线程操作同一个对象的情况下,线程不安全,数据紊乱
public class TestThread4_1 implements Runnable{

    // 票数
    private static int ticketNum = 10;

    @Override
    public void run() {
        synchronized(TestThread4_1.class){
            while (true)
            {
                if (ticketNum<=0) {
                    break;
                }
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNum--+"票");

                // Thread.currentThread().getName() 得到当前线程的名字

            }
        }


    }

    public static void main(String[] args) {
        TestThread4_1 tt = new TestThread4_1();
        new Thread(tt, "小明").start();
        new Thread(tt, "小红").start();
        new Thread(tt, "小强").start();
    }
}

这个MyThread.class是一个互斥锁(mutex),C程序中,我们mutex的实现是,指定一个信号量,对其进行pv操作实现锁机制,Java中的对象锁,深纠底层,原理是锁是存在对象头里面的,什么是Java对象头?Java对象头是一个与对象本身无关的一个额外区域,我们在使用锁的时候,实际上对Java对象头内部指针进行了操作。

Q:pv操作是什么呢?
A:PV操作是一种实现进程互斥与同步的有效方法。PV操作与信号量的处理相关,P表示通过的意思,V表示释放的意思。

相关文章

  • 线程安全和非线程安全

    二者如何取舍非线程安全是指多线程操作同一个对象可能会出现问题。而线程安全则是多线程操作同一个对象不会有问题。线程安...

  • 还不理解java synchronized?看这篇文章就够了

    Java多线程编程中,经常涉及到多个线程操作同一个对象的问题,为了保证操作结果符合预期,需要保证操作对象线程安全。...

  • Atomic原子操作和volatile非原子性

    首先,我们要理解什么叫原子操作,原子操作可以理解为:在多线程操作同一对象时,在非程序代码加锁状况下,保证被操作对象...

  • 多线程操作同一对象的问题

    1. 抢票代码 分析:两个线程很有可能会同时操作同一个变量,这是很危险的行为,这时我们就要建立一个互斥锁 1.1 ...

  • iOS多线程下的数据安全

    多线程操作共享资源的问题 在多线程的环境下,共享的资源可能会被多个线程共享,也就是多个线程可能会操作同一块资源. ...

  • iOS 锁

    锁被使用在多线程中,避免多个线程对同一公共资源的操作 测试对象 LockObj LockObj.h LockObj...

  • Java多线程---同步

    同步:就是开启多线程的时候,如果需要对同一个对象进行操作,这个时候可能会同时对其进行修改,那么需要先把这个对象进行...

  • Java实现多线程数据同步的几种方法

    1. 应用背景 程序在设计当中如果采取多线程操作的时候,如果操作的对象是一个的话,由于多个线程共享同一块内存空间,...

  • Java基础知识02- 线程

    多线程通信 :什么是多线程通信 ?怎么进行通信 ? 多线程通信就是多个线程同时操作同一个全局变量,但是操作的动作不...

  • 330,GCD栅栏函数dispatch_barrier使用注意(

    1、多线程操作同一数据进行 多读单写 线程安全控制;2、多线程执行不同任务的前后时序控制; 思考一个问题,串行队列...

网友评论

      本文标题:多线程操作同一对象的问题

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