Android-多线程安全问题-synchronized

先看一个售票案例Demo,多线程程序对共享数据操作引发的安全问题:

package android.java.thread09;

/**
 * 售票线程
 */
class Booking implements Runnable {

    /**
     * 模拟票的总算 10张票
     */
    private int ticket = 10;

    @Override
    public void run() {

        while (true) {

            if (ticket > 0) {
                // 让线程在这里停一下,会更加容易复现线程的安全问题,就算不加这行代码,安全问题依然有
                try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }

                System.out.println("名称:" + Thread.currentThread().getName() + "窗口卖出第" + ticket + "张票");
                ticket--;
            }

        }

    }
}

/**
 * 售票案例
 */
public class BookingTest {

    public static void main(String[] args) {

        /**
         * 定义Runnable实现类Booking,此实现类Booking不是线程,此实现类Booking给四个Thread去执行的
         */
        Runnable booking = new Booking();

        // 实例化线程对象
        Thread thread1 = new Thread(booking); // 此实现类Booking给Thread去执行的
        Thread thread2 = new Thread(booking); // 此实现类Booking给Thread去执行的
        Thread thread3 = new Thread(booking); // 此实现类Booking给Thread去执行的
        Thread thread4 = new Thread(booking); // 此实现类Booking给Thread去执行的

        // 开启启动线程
        thread1.start(); // 启动第Thread-0窗口 执行卖票任务
        thread2.start(); // 启动第Thread-1窗口 执行卖票任务
        thread3.start(); // 启动第Thread-2窗口 执行卖票任务
        thread4.start(); // 启动第Thread-3窗口 执行卖票任务

    }

}

打印的日志结果,注意:?? 没有打印的日志结果都不同,这是CPU对线程非常快速的切换造成的,哪个线程先有执行权 就执行哪个线程 都是随机的

名称:Thread-0窗口卖出第10张票
名称:Thread-3窗口卖出第9张票
名称:Thread-1窗口卖出第8张票
名称:Thread-2窗口卖出第7张票
名称:Thread-0窗口卖出第6张票
名称:Thread-3窗口卖出第5张票
名称:Thread-2窗口卖出第4张票
名称:Thread-1窗口卖出第4张票
名称:Thread-2窗口卖出第2张票
名称:Thread-0窗口卖出第1张票
名称:Thread-3窗口卖出第1张票
名称:Thread-1窗口卖出第-1张票
名称:Thread-2窗口卖出第-2张票

CPU的随机性,到底切换到哪个线程,到底执行哪个线程代码的多少行,等等,都是随机的

 

分析原因,为什么会出现以上日志打印的各个情况呢,为什么会出现 0张票 -1张票   这种情况呢?,看以下CPU执行线程的随机性就明白了


通过以上画图分析原因,造成安全问题的有以下两个因素:

  1.线程任务中,发现有共享数据,例如:ticket。

  2.多线程操作共享数据,例如:ticket。

造成的原因是:Thread-0在对共享数据操作过程中,CPU执行了Thread-1对共享数据操作,   相当于:我在数钱,突然我出去有事了,然后有个人拿了500块钱,等我在回来数钱的时候,就已经发生是数据安全问题


解决多线程安全问题,synchronize 加同步代码块,同步代码块:synchronize{ 操作共享数据的代码 }

package android.java.thread09;

/**
 * 售票线程
 */
class Booking implements Runnable {

    /**
     * 模拟票的总算 10张票
     */
    private int ticket = 10;

    @Override
    public void run() {

        while (true) {

            /**
             * 加入了同步代码块的代码synchronized,
             * 不管CPU如何疯狂的切换执行,
             * 只要同步代码块里面的代码没有执行完,
             * 就不准其他线程进来执行
             * 这样就保证了多线程操作共享数据的安全新
             */
            synchronized (Booking.class) { // 同步操作共享数据的代码

                if (ticket > 0) {

                    // 让线程在这里停一下,会更加容易复现线程的安全问题,就算不加这行代码,安全问题依然有
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println("名称:" + Thread.currentThread().getName() + "窗口卖出第" + ticket + "张票");
                    ticket--;
                }
            }

        }

    }
}

/**
 * 售票案例
 */
public class BookingTest {

    public static void main(String[] args) {

        /**
         * 定义Runnable实现类Booking,此实现类Booking不是线程,此实现类Booking给四个Thread去执行的
         */
        Runnable booking = new Booking();

        // 实例化线程对象
        Thread thread1 = new Thread(booking); // 此实现类Booking给Thread去执行的
        Thread thread2 = new Thread(booking); // 此实现类Booking给Thread去执行的
        Thread thread3 = new Thread(booking); // 此实现类Booking给Thread去执行的
        Thread thread4 = new Thread(booking); // 此实现类Booking给Thread去执行的

        // 开启启动线程
        thread1.start(); // 启动第Thread-0窗口 执行卖票任务
        thread2.start(); // 启动第Thread-1窗口 执行卖票任务
        thread3.start(); // 启动第Thread-2窗口 执行卖票任务
        thread4.start(); // 启动第Thread-3窗口 执行卖票任务

    }

}

以下日志结果,是CPU随机执行到哪个线程,就哪个线程打印,CPU执行线程的随机性很重要

以下日志结果,是CPU随机执行到哪个线程,就哪个线程打印,CPU执行线程的随机性很重要

以下日志结果,是CPU随机执行到哪个线程,就哪个线程打印,CPU执行线程的随机性很重要

.........

原文地址:https://www.cnblogs.com/android-deli/p/10227614.html

时间: 2024-07-30 09:10:12

Android-多线程安全问题-synchronized的相关文章

java多线程以及Android多线程

Java 多线程 线程和进程的区别 线程和进程的本质:由CPU进行调度的并发式执行任务,多个任务被快速轮换执行,使得宏观上具有多个线程或者进程同时执行的效果. 进程:在操作系统来说,一个运行的程序或者说一个动态的指令集合通常对应一个进程Process,它是系统进行资源分配和调度的一个独立单位,也是拥有系统资源的基本单位.进程是系统中独立存在的实体,它可以拥有自己独立的资源,拥有自己私有的地址空间,进程之间不能直接访问其他进程的地址空间. 线程:线程是CPU调度的基本单位,也就是说在一个进程中可以

无废话Android之smartimageview使用、android多线程下载、显式意图激活另外一个activity,检查网络是否可用定位到网络的位置、隐式意图激活另外一个activity、隐式意图的配置,自定义隐式意图、在不同activity之间数据传递(5)

1.smartimageview使用 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"

Java Tread多线程(2)多线程安全问题

作者 :卿笃军 原文地址:http://blog.csdn.net/qingdujun/article/details/39348093 本文演示,Tread多线程安全问题,以及一种解决多线程安全方式(线程同步). 1)一个线程不安全的Demo 2)线程同步 一.小Demo演示引出线程安全问题: package thread.runable1.qdj; //1.定义类实现Runnable接口 class RunDemo1 implements Runnable { private int x =

Android多线程分析之四:MessageQueue的实现

罗朝辉 (http://blog.csdn.net/kesalin) CC 许可,转载请注明出处 在前面两篇文章<Android多线程分析之二:Thread的实现>,<Android多线程分析之三:Handler,Looper的实现>中分别介绍了 Thread 的创建,运行,销毁的过程以及 Thread与 Handler,Looper 之间的关联:Thread 在其 run() 方法中创建和运行消息处理循环 Looper,而 Looper::loop() 方法不断地从 Messag

Android多线程分析之二:Thread的实现

Android多线程分析之二:Thread 罗朝辉 (http://blog.csdn.net/kesalin) CC 许可,转载请注明出处 在前文<Android多线程分析之一:使用Thread异步下载图像>中演示了如何使用 Thread 处理异步事务.示例中这个 Java Thread 类都是位于 Framework 层的类,它自身是通过 JNI 转调 dalvik 里面的 Thread 相关方法实现的.因此要分析 Androd 中的线程,就需要分析这两层中的与线程相关的代码,这就是本文要

Android多线程研究(4)——从一道面试题说起

有一道这种面试题:开启一个子线程和主线程同一时候运行,子线程输出10次后接着主线程输出100次,如此重复50次.先看以下代码: package com.maso.test; /** * * @author Administrator * 两个线程,当中是一个主线程,第一个线程先运行输出10次,主线程接着运行输出100次,如此重复50次 */ public class ThreadTest3 implements Runnable{ private static Test test; @Overr

Android多线程研究(3)——线程同步和互斥及死锁

为什么会有线程同步的概念呢?为什么要同步?什么是线程同步?先看一段代码: package com.maso.test; public class ThreadTest2 implements Runnable{ private TestObj testObj = new TestObj(); public static void main(String[] args) { ThreadTest2 tt = new ThreadTest2(); Thread t1 = new Thread(tt,

Android多线程研究(9)——线程锁Lock

在前面我们在解决线程同步问题的时候使用了synchronized关键字,今天我们来看看Java 5.0以后提供的线程锁Lock. Lock接口的实现类提供了比使用synchronized关键字更加灵活和广泛的锁定对象操作,而且是以面向对象的方式进行对象加锁. @Override public void run() { while(true){ Lock lock = new ReentrantLock(); try { lock.lock(); Thread.sleep(new Random()

Android多线程文件下载器

本应用实现的是输入文件的网络的地址,点击按钮开始下载,下载过程中有进度条和后面的文本提示进度, 下载过程中按钮不可点击,防止重复的下载,下载完毕后会进行Toast的提示显示, 并且回复按钮的可点击性,进度条也会清空,当然如果下载中途结束应用进程就会进行进度的保存, 下次下载同样的文件时就会从进度记录进行下载,节省流量和时间 应用需要的应用权限: 访问网络权限 <uses-permission android:name="android.permission.INTERNET"/&