【java并发】线程范围内共享数据

  假设现在有个公共的变量data,有不同的线程都可以去操作它,如果在不同的线程对data操作完成后再去取这个data,那么肯定会出现线程间的数据混乱问题,因为A线程在取data数据前可能B线程又对其进行了修改,下面写个程序来说明一下该问题:

public class ThreadScopeShareData {

    private static int data = 0;//公共的数据

    public static void main(String[] args) {
        for(int i = 0; i < 2; i ++) { //开启两个线程
            new Thread(new Runnable() {

                @Override
                public void run() {
                    int temp = new Random().nextInt();
                    System.out.println(Thread.currentThread().getName() + " has put a data: " + temp); //打印出来为了看效果
                    data = temp; //操作数据:赋新值

                    new TestA().getData();
                    new TestB().getData();
                }
            }).start();
        }
    }

    static class TestA {
        public void getData() {
            System.out.println("A get data from " + Thread.currentThread().getName() + ": " + data);//取出公共数据data
        }
    }

    static class TestB {
        public void getData() {
            System.out.println("B get data from " + Thread.currentThread().getName() + ": " + data);
        }
    }
}

  来看一下打印出来的结果:

Thread-0 has put a data: -1885917900

Thread-1 has put a data: -1743455464

A get data from Thread-0: -1743455464

A get data from Thread-1: -1743455464

B get data from Thread-1: -1743455464

B get data from Thread-0: -1743455464

  从结果中可以看出,两次对data赋的值确实不一样,但是两个线程最后打印出来的都是最后赋的那个值,说明Thread-0拿出的数据已经不对了,这就是线程间共享数据带来的问题。

  当然,我们完全可以使用synchronized关键字将run()方法中的几行代码给套起来,这样每个线程各自执行完,打印出各自的信息,这是没问题的,确实可以解决上面的线程间共享数据问题。但是,这是以其他线程被阻塞为代价的,即Thread-0在执行的时候,Thread-1就被阻塞了,必须等待Thread-0执行完了才能执行。

  那么如果我想两个线程同时跑,并且互不影响各自取出的值,该怎么办呢?这也是本文所要总结的重点,解决该问题的思想是:虽然现在都在操作公共数据data,但是不同的线程本身对这个data要维护一个副本,这个副本不是线程间所共享的,而是每个线程所独有的,所以不同线程中所维护的data是不一样的,最后取的时候,是哪个线程,我就从哪个线程中取该data。

  基于上面这个思路,我再把上面的程序做一修改,如下:

public class ThreadScopeShareData {

    private static int data = 0;//公共的数据
    //定义一个Map以键值对的方式存储每个线程和它对应的数据,即Thread:data
    private static Map<Thread, Integer> threadData = new HashMap<Thread, Integer>();

    public static void main(String[] args) {
        for(int i = 0; i < 2; i ++) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    int temp = new Random().nextInt();
                    System.out.println(Thread.currentThread().getName() + " has put a data: " + temp); //打印出来为了看效果
                    threadData.put(Thread.currentThread(), temp); //向Map中存入本线程data数据的一个副本
                    data = temp; //操作数据:赋新值
                    new TestA().getData();
                    new TestB().getData();
                }
            }).start();
        }
    }

    static class TestA {
        public void getData() {
            System.out.println("A get data from " + Thread.currentThread().getName() + ": "
                + threadData.get(Thread.currentThread())); //取出各线程维护的那个副本
        }
    }

    static class TestB {
        public void getData() {
            System.out.println("B get data from " + Thread.currentThread().getName() + ": "
                + threadData.get(Thread.currentThread()));
        }
    }
}

  上面程序中维护了一个Map,键值对分别是线程和它的数据,那么在操作data的时候,先把各自的数据保存到这个Map中,这样每个线程保存的肯定不同,当再取的时候,根据当前线程对象作为key来取出对应的data副本,这样不同的线程之间就不会相互影响了。来看一下执行结果:

Thread-0 has put a data: 1817494992

Thread-1 has put a data: -1189758355

A get data from Thread-0: 1817494992

B get data from Thread-1: -1189758355

A get data from Thread-0: 1817494992

B get data from Thread-1: -1189758355

  就是线程范围内共享数据,即同一个线程里面这个数据是共享的,线程间是不共享的。

  这让我联想到了学习数据库的时候用到的ThreadLocal,操作数据库需要connection,如果当前线程中有就拿当前线程中存的connection,否则就新建一个放到当前线程中,这样就不会出现问题,因为每个线程本身共享了一个connection,它不是线程间共享的。这也很好理解,这个connection肯定不能共享,假设A和B用户都拿到这个connection并开启了事务,现在A开始转账了,但是钱还没转好,B转好了关闭了事务,那么A那边就出问题了。

  线程范围内共享数据的问题就总结这么多吧~



—–乐于分享,共同进步!

—–更多文章请看:http://blog.csdn.net/eson_15

时间: 2024-08-01 09:41:16

【java并发】线程范围内共享数据的相关文章

Java线程与并发库高级应用-线程范围内共享数据ThreadLocal类

1.线程范围内共享变量 1.1 前奏: 使用一个Map来实现线程范围内共享变量 public class ThreadScopeShareData { static Map<Thread, Integer> dataMap = new HashMap<Thread, Integer>(); public static void main(String[] args) { for (int i = 0; i < 2; i++) { new Thread(new Runnable

如何实现线程范围内共享数据 -- ThreadLocall类及其应用技巧

目标:如何保证各自线程上的数据是独立的,即A线程上数据只能被A线程操作 1:示例线程共享变量 我们先来看一个反例 package com.prepare.study; import java.util.Random; /** * @author: yinlm * @Date: Created in 2018/4/18 * @Description:多个线程数据混乱的示例 */ public class ThreadTest2 { // static 修饰 表示这是个全局变量 private st

Java中线程范围内共享问题

本宝宝新手,勿喷!直接上代码了, 线程范围内共享问题:各个线程之间共享同一块数据,一个数据损坏就全部损坏,需要的可以运行一下! public class Threads { private static HashMap<Thread, Integer> data = new HashMap<Thread,Integer>(); public static void main(String[] args) { for (int i = 0; i < 2; i++) { new

Java 并发 线程同步

Java 并发 线程同步 @author ixenos 同步 1.异步线程本身包含了执行时需要的数据和方法,不需要外部提供的资源和方法,在执行时也不关心与其并发执行的其他线程的状态和行为 2.然而,大多数实际的多线程应用中,两个或两个以上的线程需要共享对同一数据的存取,这将产生同步问题(可见性和同步性的丢失) 比如两个线程同时执行指令account[to] += amount,这不是原子操作,可能被处理如下: a)将account[to]加载到寄存器 b)增加amount c)将结果写回acco

Java 并发 线程属性

Java 并发 线程属性 @author ixenos 线程优先级 1.每当线程调度器有机会选择新线程时,首先选择具有较高优先级的线程 2.默认情况下,一个线程继承它的父线程的优先级 当在一个运行的线程A里,创建另一个线程B的时候,那么A是父线程,B是子线程.当在一个运行的线程A里,创建线程B,然后又创建了线程C,这时候虽然B比C创建早,可是B并不是C的父线程,而A是B和C的父线程. 3.线程的优先级高度依赖于系统,当虚拟机依赖于宿主机平台的线程实现机制时,Java线程的优先级被映射到宿主机平台

Java 并发 线程的优先级

Java 并发 线程的优先级 @author ixenos 低优先级线程的执行时刻 1.在任意时刻,当有多个线程处于可运行状态时,运行系统总是挑选一个优先级最高的线程执行,只有当线程停止.退出或者由于某些原因不执行的时候,低优先级的线程才可能被执行 2.两个优先级相同的线程同时等待执行时,那么运行系统会以round-robin的方式选择一个线程执行(即轮询调度,以该算法所定的)(Java的优先级策略是抢占式调度!) 3.被选中的线程可因为一下原因退出,而给其他线程执行的机会: 1) 一个更高优先

Java 并发 线程的生命周期

Java 并发 线程的生命周期 @author ixenos 线程的生命周期 线程状态: a)     New 新建 b)     Runnable 可运行 c)     Running 运行 (调用getState()时显示为Runnable) d)     Blocked 阻塞 i.          I/O阻塞 (不释放锁) I/O操作完成解除阻塞,进入Runnable状态 ii.          同步阻塞(不释放锁) 运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会

Java多个线程之间处理共享数据的方式

题目要求: 有4个线程,其中两个线程每次对x加1,另外两个每次对x减1,如何实现? 分析: x就是这4个线程要处理的共享数据,不同种线程有不同的处理方式,但操作的数据是共同的,联想到"窗口买票问题",但是卖票只是单纯的减. 所以,多个线程访问共享对象或数据的方式,先看,如果每个线程执行的代码相同,那么就可以使用同一个Runnable对象,这个对象中有被共享的数据,如买票问题. 如果每个线程执行的代码不同,肯定需要不用的Runnable对象,有两种方式实现这些不同的Runnable对象之

Java并发-线程安全性

首先了解一下多线程的概念 多线程:两段或以上的代码同时进行,多个顺序执行流. 并发和并行的区别 并发:做一下这个做一下那个. 并行:同时进行. 线程和进程的区别 进程:资源分配的基本单位,运行中的程序.进程中包括多个线程,线程们共享进程的资源. 线程:处理器调度的基本单位. 线程的状态:  线程创建的方法: (1)继承Thread类 (2)实现Runnable接口 (3)匿名内部类.Lambda表达式 (4)带返回值的线程 要想使对象是线程安全的,需要采用同步机制来协同,如果无法实现,那么可能会