java基础多线程之共享数据

简介: java基础巩固笔记5-多线程之共享数据<br> 线程范围内共享数据<br> ThreadLocal类<br> 多线程访问共享数据<br> 几种方式<br> 本文主要总结线程共享数据的相关知识,主要包括两方面:一是某个线程内如何共享数据,保证各个线程的数据不交叉;一是多个线程间如何共享数据,保证数据的一致性。<br><br><br> 线程范围内共享数据<br><br><br>
java基础巩固笔记5-多线程之共享数据
线程范围内共享数据
ThreadLocal类
多线程访问共享数据
几种方式
本文主要总结线程共享数据的相关知识,主要包括两方面:一是某个线程内如何共享数据,保证各个线程的数据不交叉;一是多个线程间如何共享数据,保证数据的一致性。


线程范围内共享数据


自己实现的话,是定义一个Map,线程为键,数据为值,表中的每一项即是为每个线程准备的数据,这样在一个线程中数据是一致的。


例子


package com.iot.thread;


import java.util.HashMap;
import java.util.Map;
import java.util.Random;


/**
 * Created by brian on 2016/2/4.
 */
public class ThreadScopeShareData {
    //准备一个哈希表,为每个线程准备数据
    private  static Map<Thread,Integer> threadData = new HashMap<>();
    public static void main(String[] args) {
        for(int i=0;i<2;i++){
            new Thread(
                    new Runnable() {
                @Override
                public void run() {
                    int data = new Random().nextInt();
                    threadData.put(Thread.currentThread(),data);
                    System.out.println(Thread.currentThread()+" put data:"+data);
                    new A().get();
                    new B().get();
                }
            }).start();
        }
    }
   static  class A{
        public void get(){
            int data = threadData.get(Thread.currentThread());
            System.out.println("A from "+Thread.currentThread()+" get data "+data);
        }
    }


    static  class B{
        public void get(){
            int data = threadData.get(Thread.currentThread());
            System.out.println("B from "+Thread.currentThread()+" get data "+data);
        }
    }
}


上述代码偶尔会报异常:


Exception in thread "Thread-0" java.lang.NullPointerException
    at com.iot.thread.ThreadScopeShareDataA.get(ThreadScopeShareData.java:29)atcom.iot.thread.ThreadScopeShareData1.run(ThreadScopeShareData.java:21)
    at java.lang.Thread.run(Thread.java:745)
1
2
3
4
5
具体原因还不知道


ThreadLocal类


API:


java.lang:Class ThreadLocal<T>
单变量
使用ThreadLocal类型的对象代替上面的Map即可


多变量
定义一个对象来封装多个变量,然后在ThreadLocal中存储整个对象


多变量时,最好将ThreadLocal类放在数据类的内部,数据类采用单例模式,这样,新建对象和获取对象都会更方便,同时封装性更强。


示例代码:


package com.iot.thread;


import java.util.Random;


/**
 * Created by brian on 2016/2/4.
 */
public class ThreadLocalTest {
    private  static ThreadLocal<Integer> threadInger = new ThreadLocal<>();
    public static void main(String[] args) {
        for(int i=0;i<2;i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    int data = new Random().nextInt(100);
                    threadInger.set(data);
                    System.out.println(Thread.currentThread()+" put data:"+data);
                    MyThreadScopeData.getThreadInstance().setName(Thread.currentThread().toString());
                    MyThreadScopeData.getThreadInstance().setAge(data%10);
                    new A().get();
                    new B().get();
                }
            }).start();
        }
    }
    static  class A{
        public void get(){
            int data = threadInger.get();
            System.out.println("A from "+Thread.currentThread()+" get data "+data);
            MyThreadScopeData myThreadScopeData = MyThreadScopeData.getThreadInstance();
            System.out.println("A from "+myThreadScopeData);


        }
    }


    static  class B{
        public void get(){
            int data = threadInger.get();
            System.out.println("B from "+Thread.currentThread()+" get data "+data);
            MyThreadScopeData myThreadScopeData = MyThreadScopeData.getThreadInstance();
            System.out.println("B from "+myThreadScopeData);
        }
    }
}


/**
 * 将多变量封装起来的数据类
 * 单例模式,内置ThreadLocal类型变量
 */
class MyThreadScopeData{


    private MyThreadScopeData(){}


    private static ThreadLocal<MyThreadScopeData> data = new ThreadLocal<>();


    public static  MyThreadScopeData getThreadInstance(){
        MyThreadScopeData instance = data.get();
        if(instance == null){
            instance = new MyThreadScopeData();
            data.set(instance);
        }
        return instance;
    }






    private String name;
    private int age;
    public String getName() {
        return name;
    }


    public void setName(String name) {
        this.name = name;
    }


    public int getAge() {
        return age;
    }


    public void setAge(int age) {
        this.age = age;
    }


    @Override
    public String toString() {
        String reVal = super.toString()+"-{name,age}"+":{"+getName()+","+getAge()+"}";
        return reVal;
    }
}




多线程访问共享数据


几种方式


线程执行代码相同,使用同一Runnable对象,Runnable对象中有共享数据
线程执行代码不同,将共享数据封装在另一对象中(操作数据的方法也在该对象完成),将这个对象逐一传递给各个Runnable对象。[本质:共享数据的对象作为参数传入Runnable对象]
线程执行代码不同,将Runnable对象作为某一个类的内部类,共享数据作为这个外部类的成员变量(操作数据的方法放在外部类)。[本质:不同内部类共享外部类数据]
结合上两种方式,将共享数据封装在另一对象中(操作数据的方法也在该对象完成),该对象作为这个外部类的成员变量,将Runnable对象作为内部类
最后一种方式的示例:


设计5个线程,其中三个线程每次对j增加1,另外两个线程对j每次减少1


package com.iot.thread;


/**
 * Created by brian on 2016/2/4.
 */
public class MutiThreadShareData {


    private static MutiShareData mutiShareData = new MutiShareData();


    public static void main(String[] args) {


        for(int i=0;i<3;i++){
            new Thread(
                    new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(Thread.currentThread()+":{j from "+ mutiShareData.getJ()+" + to: "+mutiShareData.increment()+"}");
                        }
                    }
            ).start();
        }


        for(int i=0;i<2;i++){
            new Thread(
                    new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(Thread.currentThread()+":{j from "+ mutiShareData.getJ()+" - to: "+mutiShareData.decrement()+"}");
                        }
                    }
            ).start();
        }
    }


}


/**
 * 将共享数据封装在另一对象中(操作数据的方法也在该对象完成)
 */
class MutiShareData{
    private int j = 0;
    public synchronized  int increment(){
        return  ++j;
    }
    public synchronized int  decrement(){
        return --j;
    }


    public synchronized int getJ() {
        return j;
    }


    public synchronized void setJ(int j) {
        this.j = j;
    }
}
目录
打赏
0
0
0
0
43
分享
相关文章
|
2月前
|
【Java并发】【线程池】带你从0-1入门线程池
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是编写高端CRUD应用。2025年我正在沉淀中,博客更新速度加快,期待与你一起成长。 线程池是一种复用线程资源的机制,通过预先创建一定数量的线程并管理其生命周期,避免频繁创建/销毁线程带来的性能开销。它解决了线程创建成本高、资源耗尽风险、响应速度慢和任务执行缺乏管理等问题。
192 60
【Java并发】【线程池】带你从0-1入门线程池
|
6天前
|
【源码】【Java并发】从InheritableThreadLocal和TTL源码的角度来看父子线程传递
本文涉及InheritableThreadLocal和TTL,从源码的角度,分别分析它们是怎么实现父子线程传递的。建议先了解ThreadLocal。
35 4
【源码】【Java并发】从InheritableThreadLocal和TTL源码的角度来看父子线程传递
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现
博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现
Java网络编程,多线程,IO流综合小项目一一ChatBoxes
**项目介绍**:本项目实现了一个基于TCP协议的C/S架构控制台聊天室,支持局域网内多客户端同时聊天。用户需注册并登录,用户名唯一,密码格式为字母开头加纯数字。登录后可实时聊天,服务端负责验证用户信息并转发消息。 **项目亮点**: - **C/S架构**:客户端与服务端通过TCP连接通信。 - **多线程**:采用多线程处理多个客户端的并发请求,确保实时交互。 - **IO流**:使用BufferedReader和BufferedWriter进行数据传输,确保高效稳定的通信。 - **线程安全**:通过同步代码块和锁机制保证共享数据的安全性。
82 23
|
1月前
|
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
当我们创建一个`ThreadPoolExecutor`的时候,你是否会好奇🤔,它到底发生了什么?比如:我传的拒绝策略、线程工厂是啥时候被使用的? 核心线程数是个啥?最大线程数和它又有什么关系?线程池,它是怎么调度,我们传入的线程?...不要着急,小手手点上关注、点赞、收藏。主播马上从源码的角度带你们探索神秘线程池的世界...
140 0
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
Java爬虫获取微店快递费用item_fee API接口数据实现
本文介绍如何使用Java开发爬虫程序,通过微店API接口获取商品快递费用(item_fee)数据。主要内容包括:微店API接口的使用方法、Java爬虫技术背景、需求分析和技术选型。具体实现步骤为:发送HTTP请求获取数据、解析JSON格式的响应并提取快递费用信息,最后将结果存储到本地文件中。文中还提供了完整的代码示例,并提醒开发者注意授权令牌、接口频率限制及数据合法性等问题。
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
160 14
深潜数据海洋:Java文件读写全面解析与实战指南
通过本文的详细解析与实战示例,您可以系统地掌握Java中各种文件读写操作,从基本的读写到高效的NIO操作,再到文件复制、移动和删除。希望这些内容能够帮助您在实际项目中处理文件数据,提高开发效率和代码质量。
52 4
Java 面试必问!线程构造方法和静态块的执行线程到底是谁?
大家好,我是小米。今天聊聊Java多线程面试题:线程类的构造方法和静态块是由哪个线程调用的?构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节有助于掌握Java多线程机制。下期再见! 简介: 本文通过一个常见的Java多线程面试题,详细讲解了线程类的构造方法和静态块是由哪个线程调用的。构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节对掌握Java多线程编程至关重要。
70 13
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等