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

本文主要总结线程共享数据的相关知识,主要包括两方面:一是某个线程内如何共享数据,保证各个线程的数据不交叉;一是多个线程间如何共享数据,保证数据的一致性。

线程范围内共享数据

自己实现的话,是定义一个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.ThreadScopeShareData$A.get(ThreadScopeShareData.java:29)
at com.iot.thread.ThreadScopeShareData$1.run(ThreadScopeShareData.java:21)
at java.lang.Thread.run(Thread.java:745)

具体原因还不知道

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;
	}
}

总结

以上就是本文关于Java编程多线程之共享数据代码详解的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

您可能感兴趣的文章:

  • Java Socket实现单线程通信的方法示例
  • JavaScrip单线程引擎工作原理分析
  • Java编程一道多线程问题实例代码
  • Java多线程join方法实例代码
  • JAVA多线程CountDownLatch使用详解
  • 关于Java多线程编程锁优化的深入学习
  • java编程多线程并发处理实例解析
  • Java编程实现多线程TCP服务器完整实例
  • Java语言多线程终止中的守护线程实例
  • 实例分析Java单线程与多线程
(0)

相关推荐

  • Java多线程join方法实例代码

    本文研究的主要是Java多线程中join方法的使用问题,以下文为具体实例. Thread的非静态方法join()让一个线程B"加入"到另外一个线程A的尾部.在A执行完毕之前,B不能工作.例如: Thread t = new MyThread(); t.start(); t.join(); 另外,join()方法还有带超时限制的重载版本. 例如t.join(5000);则让线程等待5000毫秒,如果超过这个时间,则停止等待,变为可运行状态. 线程的加入join()对线程栈导致的结果是线程

  • Java编程一道多线程问题实例代码

    前面几篇博文基本上总结了一下java并发里的一些内容,这篇博文主要从一个问题入手,看看都能用到前面总结的哪些并发技术去解决. 题目描述: 模拟一个场景:处理16条日志记录,每条日志记录打印时间需要1秒,正常情况下如果将这16条记录去部打完需要16秒,现在为了提高效率,准备开启4个线程去打印,4秒钟打印完,实现这个demo. 先来分析一下这个题目,关于这16条日志记录,我们可以在主线程中产生出来,这没用什么难度,关键是开启4个线程去执行,现在有两种思路:一种是日志的产生和打印日志的线程在逻辑上分开

  • 实例分析Java单线程与多线程

    线程:每一个任务称为一个线程,线程不能独立的存在,它必须是进程的一部分 单线程:般常见的Java应用程序都是单线程的,比如运行helloworld的程序时,会启动jvm进程,然后运行main方法产生线程,main方法也被称为主线程. 多线程:同时运行一个以上线程的程序称为多线程程序,多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的. 单线程代码例子: public class SingleThread { public static void main(String[] args

  • JAVA多线程CountDownLatch使用详解

    前序: 上周测试给开发的同事所开发的模块提出了一个bug,并且还是偶现. 经过仔细查看代码,发现是在业务中启用了多线程,2个线程同时跑,但是新启动的2个线程必须保证一个完成之后另一个再继续运行,才能消除bug. 什么时候用? 多线程是在很多地方都会用到的,但是我们如果想要实现在某个特定的线程运行完之后,再启动另外一个线程呢,这个时候CountDownLatch就可以派上用场了 怎么用? 先看看普通的多线程代码: package code; public class MyThread extend

  • Java语言多线程终止中的守护线程实例

    Java中线程分为两种类型:用户线程和守护(服务)线程.通过Thread.setDaemon(false)设置为用户线程;通过Thread.setDaemon(true)设置为守护线程;不设置则默认为用户线程. 结束单线程用 Thread.interrupt() 方法,多线程结束则需要设置守护线程.当不存在用户线程时,守护线程就会全部终结(可以理解为:守护线程是服务线程,用户线程是被服务线程,用户线程(被服务线程)全都没有了,服务线程便没有存在意义而自动终结) 例子: class StopThr

  • Java编程实现多线程TCP服务器完整实例

    相关Java类 Socket public class Socket extends Object ·功能:TCP客户端套接字 ·构造方法: Socket(InetAddress address, int port) 创建一个流套接字并将其连接到指定 IP 地址的指定端口号 ·常用方法: 1.getInetAddress 获得InetAddress的相关信息 2.getInputStream 获得此TCP连接的输入流 3.getOutPutStream 获得此TCP连接的输出流 ServerSo

  • 关于Java多线程编程锁优化的深入学习

    正文 并发环境下进行编程时,需要使用锁机制来同步多线程间的操作,保证共享资源的互斥访问.加锁会带来性能上的损坏,似乎是众所周知的事情.然而,加锁本身不会带来多少的性能消耗,性能主要是在线程的获取锁的过程.如果只有一个线程竞争锁,此时并不存在多线程竞争的情况,那么JVM会进行优化,那么这时加锁带来的性能消耗基本可以忽略.因此,规范加锁的操作,优化锁的使用方法,避免不必要的线程竞争,不仅可以提高程序性能,也能避免不规范加锁可能造成线程死锁问题,提高程序健壮性.下面阐述几种锁优化的思路. 一.尽量不要

  • java编程多线程并发处理实例解析

    本文主要是通过一个银行用户取钱的实例,演示java编程多线程并发处理场景,具体如下. 从一个例子入手:实现一个银行账户取钱场景的实例代码. 第一个类:Account.java 账户类: package cn.edu.byr.test; public class Account { private String accountNo; private double balance; public Account(){ } public Account(String accountNo,double

  • Java Socket实现单线程通信的方法示例

    本文实例讲述了Java Socket实现单线程通信的方法.分享给大家供大家参考,具体如下: 现在做Java直接使用Socket的情况是越来越少,因为有很多的选择可选,比如说可以用spring,其中就可以支持很多种远程连接的操作,另外jboss的remoting也是不错的选择,还有Apache的Mina等等,但是在有些时候一些特殊情况仍然逃脱不了直接写Socket的情况,比如公司内部一些莫名其妙的游戏规则. 废话不说了,下面就看看如果自己写Socket应该怎么做吧. 首先是写一个Server类,这

  • JavaScrip单线程引擎工作原理分析

    从基础的层面来讲,理解JavaScript的定时器是如何工作的是非常重要的.定时器的执行常常和我们的直观想象不同,那是因为JavaScript引擎是单线程的.我们先来认识一下下面三个函数是如何控制计时器的. 我们推荐阅读:雕虫无小技 JavaScript初学者的10个迷你技巧 复制代码 代码如下: var id = setTimeout(fn, delay); 初始化一个计时器,然后在指定的时间间隔后执行.该函数返回一个唯一的标志ID(Number类型),我们可以使用它来取消计时器. 复制代码

随机推荐