Java如何实现多个线程之间共享数据

目录
  • 实现多个线程之间共享数据
    • 一、 如果每个线程执行的代码相同
    • 二、 如果每个线程执行的代码不同
  • 多线程之间共享数据的方式探讨
    • 方式一:代码一致
    • 方式二:代码不一致

实现多个线程之间共享数据

一、 如果每个线程执行的代码相同

可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如:卖票系统

class Ticket implements Runnable{
    private  int tick = 20;
    Object obj = new Object();
    public void run(){
        while(true){
            synchronized(obj){
                if(tick>0){
                    //只能try,因为run是复写了Runnable接口的run,接口的run没有抛
                    try{Thread.sleep(100);}catch(Exception e){}  //使用sleep不然执行每个线程都会占用完毕
                    System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
                }
            }
        }
    }
}  

class  TicketDemo
{
    public static void main(String[] args) {  

        //只建立了一个Ticket对象,内存中只有一个tick成员变量,所以是共享数据
        Ticket t = new Ticket();  

        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        Thread t3 = new Thread(t);
        Thread t4 = new Thread(t);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
} 

输出结果

Thread-0....sale : 20
Thread-1....sale : 19
 .......
Thread-3....sale : 2
Thread-3....sale : 1

二、 如果每个线程执行的代码不同

1、具体实现

将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个Runnable对象。每个线程对共享数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。

思想: 一个类提供数据和操作数据的同步方法,另外定义两个线程通过构造函数接收并操作数据,在主函数中直接创建线程对象,即可完成操作(可以实现两个内部类,不用构造方法传值,使用final定义data局部变量)

例如: 设计4个线程,其中两个线程每次对j增加1,另外两个线程每次对j减少1

public class MultyThreadShareMethod1 {
    public static void main(String[] args){
        //将数据封装到一个对象上,
        ShareData2 data1 = new ShareData2();  

        //在runnable的构造函数中直接传入去操作
        for(int i=0;i<2;i++){
        new Thread(new MyRunnable1(data1)).start();
        new Thread(new MyRunnable2(data1)).start();
        }
    }
}  

//封装共享数据和操作共享数据方法的类
class ShareData2{
    private int j = 10;
    public synchronized void increment() {
        j++;
        System.out.println(Thread.currentThread().getName()+" inc : "+j);
    }
    public synchronized void decrement() {
        j--;
        System.out.println(Thread.currentThread().getName()+" dec : "+j);
    }
}  

//增加的线程,需要传入一个共享数据
class MyRunnable1 implements Runnable {
    private ShareData2 data;
    public MyRunnable1(ShareData2 data) {
        this.data = data;
    }
    @Override
    public void run() {
        for(int i=0;i<10;i++){
        data.increment();
        }
    }
}  

//减少的线程,需要传入一个共享数据
class MyRunnable2 implements Runnable {
    private ShareData2 data;
    public MyRunnable2(ShareData2 data) {
        this.data = data;
    }
    @Override
    public void run() {
        for(int i=0;i<10;i++){
        data.decrement();
        }
    }
}  

输出结果

Thread-0 inc : 11
...
Thread-1 dec : 10

2、 技巧总结

要同步互斥的几段代码最好是分别放在几个独立的方法中,这些方法再放在同一个类中,这样比较容易实现它们之间的同步互斥或通信。

极端且简单的方式,即在任意一个类中定义一个static的变量,这将被所有线程共享。

多线程之间共享数据的方式探讨

方式一:代码一致

如果每个线程执行的代码相同,可以用一个 Runnable 对象,这个 Runnable 对象中存放那个共享数据(卖票系统可以这样做)。

public class MultiThreadShareData {
    public static void main(String[] args) {
        MyShareData shareData=new MyShareData();
        //放入不同的线程中
        new Thread(shareData).start();
        new Thread(shareData).start();
    }
}

class MyShareData implements Runnable {
        // 共享的数据
        private int count = 100;
        @Override
        public void run() {
            while (count > 0) {
                synchronized (this) {
                    if (count > 0) {
                        count--;
                        System.out.println(Thread.currentThread().getName() + " 减了1,count还剩:" + count);
                    }
                }
            }
        }
    }

方式二:代码不一致

如果每个线程执行的代码不同时,就需要不同的 Runnable 对象:

a. 将共享数据封装在一个对象中,然后将这个对象逐一传递给各个 Runnable 对象,每个线程对共享数据操作的方法也分配到这个对象中,这样容易实现针对该数据进行的各个操作的互斥通信。

public class MultiThreadShareData {
    private int shareData=0;
    public static void main(String[] args) {
        ShareData data = new ShareData();
        new Thread(new MyRunnable1(data)).start();
        new Thread(new MyRunnable2(data)).start();
    }
}

class MyRunnable1 implements Runnable{
    private ShareData data;
    public MyRunnable1(ShareData data){
        this.data=data;
    }

    @Override
    public void run() {
        for(int i=0;i<100;i++){
            //对数据进行增加
            this.data.increment();
        }
    }
}

class MyRunnable2 implements Runnable{
    private ShareData data;
    public MyRunnable2(ShareData data){
        this.data=data;
    }

    @Override
    public void run() {
        for(int i=0;i<100;i++){
            //对数据进行减少
            this.data.decrement();
        }
    }
}

/**
 将共享的数据封装成一个类
*/
class ShareData{
    //共享数据
    private int j=0;
    public synchronized void increment(){
        this.j++;
        System.out.println(Thread.currentThread().getName()+":j增加了1后j="+j);
    }

    public synchronized void decrement() {
        this.j--;
        System.out.println(Thread.currentThread().getName()+":j减少了1后j="+j);
    }
    public int getJ() {
        return j;
    }
}

b. 将 Runnable 对象作为某一个类的内部类,共享数据作为这个外部类的成员变量,每个线程对共享数据的操作方法也分配到外部类中,实现共享数据的互斥和通信操作,作为内部类的各个 Runnable 对象调用外部类的这些方法。

public class MultiThreadShareData {
    private int shareData=0;
    public static void main(String[] args) {
        MultiThreadShareData m=new MultiThreadShareData();
        //初始化Runnable对象
        MyRunnable1 myRunnable1 = m.new MyRunnable1();
        MyRunnable2 myRunnable2=m.new MyRunnable2();
        //开启线程
        new Thread(myRunnable1).start();
        new Thread(myRunnable2).start();
    }

    private synchronized void increment(){
        this.shareData++;
        System.out.println(Thread.currentThread().getName()+":shareData增加了1后shareData="+shareData);
    }

    private synchronized void decrement() {
        this.shareData--;
        System.out.println(Thread.currentThread().getName()+":shareData减少了1后shareData="+shareData);
    }

    /**
     * 作为内部类的Runnable对象
     */
    class MyRunnable1 implements Runnable{
        @Override
        public void run() {
            for(int i=0;i<100;i++){
                increment();
            }
        }
    }

    class MyRunnable2 implements Runnable{
        @Override
        public void run() {
            for(int i=0;i<100;i++){
                decrement();
            }
        }
    }
}

c. 以上两种方法的组合:将共享数据封装到一个对象中,每个线程对共享数据的操作方法也分配到对象中,对象作为外部类的成员变量或方法中的局部变量,每个线程的 Runnable 作为成员内部类或局部内部类。

 public class MultiThreadShareData {
   public static void main(String[] args) {
       ShareData data = new ShareData();
       new Thread(()->{
           for(int i=0;i<100;i++){
               data.increment();
           }
       }).start();

       new Thread(()->{
           for (int j=0;j<100;j++) {
               data.decrement();
           }
       }).start();
   }
}

/**
封装共享数据的对象
*/
class ShareData{
   //共享数据
   private int j=0;

   /**
    对共享数据进行操作的方法
   */
   public synchronized void increment(){
       this.j++;
       System.out.println(Thread.currentThread().getName()+":j增加了1后j="+j);
   }

   public synchronized void decrement() {
       this.j--;
       System.out.println(Thread.currentThread().getName()+":j减少了1后j="+j);
   }

   public int getJ() {
       return j;
   }
}

总之,要同步互斥的几段代码最好放在几个独立的方法中,这些方法再放入一个类中,这样比较容易实现它们之间的同步互斥和通信。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Java多线程编程之访问共享对象和数据的方法

    多个线程访问共享对象和数据的方式有两种情况: 1.每个线程执行的代码相同,例如,卖票:多个窗口同时卖这100张票,这100张票需要多个线程共享. 2.每个线程执行的代码不同,例如:设计四个线程,其中两个线程每次对j增加1,另外两个线程每次对j减少1. a.如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个对象中有共享数据.卖票就可以这样做,每个窗口都在做卖票任务,卖的票都是同一个数据(点击查看具体案例). b.如果每个线程执行的代码不同,就需要使用不同的Runnable对象,有

  • Java 多线程之间共享数据

    目录 1.线程范围的共享变量 2.使用Map实现线程范围内数据的共享 3.ThreadLocal实现线程范围内数据的共享 4.优化 5.实例 1.线程范围的共享变量 多个业务模块针对同一个static变量的操作 要保证在不同线程中 各模块操作的是自身对应的变量对象 public class ThreadScopeSharaData { private static int data = 0 ; public static void main(String[] args) { for(int i

  • Java编程多线程之共享数据代码详解

    本文主要总结线程共享数据的相关知识,主要包括两方面:一是某个线程内如何共享数据,保证各个线程的数据不交叉:一是多个线程间如何共享数据,保证数据的一致性. 线程范围内共享数据 自己实现的话,是定义一个Map,线程为键,数据为值,表中的每一项即是为每个线程准备的数据,这样在一个线程中数据是一致的. 例子 package com.iot.thread; import java.util.HashMap; import java.util.Map; import java.util.Random; /*

  • Java并发编程之线程之间的共享和协作

    一.线程间的共享 1.1 ynchronized内置锁 用处 Java支持多个线程同时访问一个对象或者对象的成员变量 关键字synchronized可以修饰方法或者以同步块的形式来进行使用 它主要确保多个线程在同一个时刻,只能有一个线程处于方法或者同步块中 它保证了线程对变量访问的可见性和排他性(原子性.可见性.有序性),又称为内置锁机制. 对象锁和类锁 对象锁是用于对象实例方法,或者一个对象实例上的 类锁是用于类的静态方法或者一个类的class对象上的 类的对象实例可以有很多个,但是每个类只有

  • Java线程间共享实现方法详解

    一.synchronize对象锁和类锁 synchronize为多线程关键字是一种同步锁,它可以修饰以下几种对象: 代码块:被修饰的代码块被称为同步代码块,作用的范围是{}里面的代码,作用的对象是调用这个代码块的对象 方法:被修饰的方法称为同步方法,作用的范围是整个方法,作用的对象是调用这个方法的对象 类:作用的范围是synchronize后面括号里的部分,作用的对象是当前这个类 1.对象锁 下面由一个栗子引入: public class TestSynchronize { //加了对象锁的方法

  • Java如何实现多个线程之间共享数据

    目录 实现多个线程之间共享数据 一. 如果每个线程执行的代码相同 二. 如果每个线程执行的代码不同 多线程之间共享数据的方式探讨 方式一:代码一致 方式二:代码不一致 实现多个线程之间共享数据 一. 如果每个线程执行的代码相同 可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如:卖票系统 class Ticket implements Runnable{ private int tick = 20; Object obj = new Object(); publi

  • Android编程实现两个Activity之间共享数据及互相访问的方法

    本文实例讲述了Android编程实现两个Activity之间共享数据及互相访问的方法.分享给大家供大家参考,具体如下: 本人从windows编程转过来学习Android开发,一直在想如果两个Activity之间能够像C#或delphi中的Form一样,可以直接访问其成员(字符.数值.成员对象等),并能调用其公开的方法,那应该比用Intent来传递数据直接方便的多,于是偿试了如下办法,测试基本没有问题,发出来大家讨论一下.本人学习android不久,幼稚的地方希望大家不要见笑 原理:假设有两个Ac

  • Python 进程之间共享数据(全局变量)的方法

    进程之间共享数据(数值型): import multiprocessing def func(num): num.value=10.78 #子进程改变数值的值,主进程跟着改变 if __name__=="__main__": num=multiprocessing.Value("d",10.0) # d表示数值,主进程与子进程共享这个value.(主进程与子进程都是用的同一个value) print(num.value) p=multiprocessing.Proc

  • vue+vuex+axios从后台获取数据存入vuex,组件之间共享数据操作

    在vue项目中组件间相互传值或者后台获取的数据需要供多个组件使用的情况很多的话,有必要考虑引入vuex来管理这些凌乱的状态,今天这边博文用来记录这一整个的过程,后台api接口是使用webpack-server模拟的接口,这个前面的文章中有提到,需要的可以去翻阅. 整个的流程是在组件的created中提交dispatch,然后通过action调用一个封装好的axios然后再触发mutation来提交状态改变state中的数据,然后在组件的计算属性中获取state的数据并渲染在页面上 首先新需要在项

  • android不同activity之间共享数据解决方法

    最近做局域网socket连接问题,要在多个activity之间公用一个socket连接,就在网上搜了下资料,感觉还是application方法好用,帖出来分享下! Android中在不同Activity中传递变量,通常使用Intent中Bundle添加变量的操作方法. 保存参数时: 复制代码 代码如下: Intent intent = new Intent(); intent.setClass(A.this, B.class); Bundle bundle = new Bundle(); bun

  • vue+vuex+axio从后台获取数据存入vuex实现组件之间共享数据

    在vue项目中组件间相互传值或者后台获取的数据需要供多个组件使用的情况很多的话,有必要考虑引入vuex来管理这些凌乱的状态,今天这边博文用来记录这一整个的过程,后台api接口是使用webpack-server模拟的接口,这个前面的文章中有提到,需要的可以去翻阅. 整个的流程是在组件的created中提交dispatch,然后通过action调用一个封装好的axios然后再触发mutation来提交状态改变state中的数据,然后在组件的计算属性中获取state的数据并渲染在页面上 首先新需要在项

  • 聊聊java多线程创建方式及线程安全问题

    什么是线程 线程被称为轻量级进程,是程序执行的最小单位,它是指在程序执行过程中,能够执行代码的一个执行单位.每个程序程序都至少有一个线程,也即是程序本身. 线程的状态 新建(New):创建后尚未启动的线程处于这种状态 运行(Runable):Runable包括了操作系统线程状态的Running和Ready,也就是处于此状态的线程有可能正在执行,也有可能正在等待着CPU为它分配执行时间. 等待(Wating):处于这种状态的线程不会被分配CPU执行时间.等待状态又分为无限期等待和有限期等待,处于无

  • Servlet实现共享数据JavaWeb组件的几种方法

    目录 一.Servlet简介 二.Servlet的运行过程 Servlet组件: 多个Servlet之间共享数据实现方案 转发与重定向 全局作用域对象 Servlet JavaWeb三大组件包括,Servlet组件(接受请求,响应数据),Filter组件(过滤,拦截请求),Listener组件(监听器),这三大组件构成了javaWeb核心内容,也是作为后端来说,JavaWeb最重要的内容. 一.Servlet简介 Servlet是sun公司提供的一门用于开发动态web资源的技术. Sun公司在其

  • Nodejs中解决cluster模块的多进程如何共享数据问题

    前述 nodejs在v0.6.x之后增加了一个模块cluster用于实现多进程,利用child_process模块来创建和管理进程,增加程序在多核CPU机器上的性能表现.本文将介绍利用cluster模块创建的多线程如何共享数据的问题. 进程间数据共享 首先举个简单的例子,代码如下: var cluster = require('cluster'); var data = 0;//这里定义数据不会被所有进程共享,各个进程有各自的内存区域 if (cluster.isMaster) { //主进程

随机推荐