Java并发基础09. 多个线程间共享数据问题
先看一个多线程间共享数据的问题:
设计四个线程,其中两个线程每次对data增加1,另外两个线程每次对data减少1。
从问题来看,很明显涉及到了线程间通数据的共享,四个线程共享一个 data,共同操作一个 data。我们先把上面这个问题放在一边,慢慢分析多个线程之间共享数据的一些情况,从最简单开始,分类分析完了后,到时候也好解决上面这个问题了。
1. 每个线程执行的任务相同
这是最简单的一种情况,比如卖票,几个线程共同操作记录票数的那个变量,任务都是使它减一。针对这种情况,我们只需要写一个类实现 Runnable 接口即可,在 run() 方法中对这个票进行减一,然后将这个 Runnable 扔给多个线程去执行,自然它们就操作同一个data了。看一下代码:
public class MultiThreadShareData {
public static void main(String[] args) {
ShareData task = new ShareData(); //一个类实现了Runnable接口
for(int i = 0; i < 4; i ++) { //四个线程来卖票
new Thread(task).start();
}
}
}
class ShareData implements Runnable {
private int data = 100;
@Override
public void run() { //卖票,每次一个线程进来,先判断票数是否大于0
// while(data > 0) {
synchronized(this) {
if(data > 0) {
System.out.println(Thread.currentThread().getName() + ": " + data);
data--;
}
}
// }
}
}
这很好理解,也很容易实现,四个线程卖了4张票。运行结果为:
Thread-0: 100
Thread-3: 99
Thread-2: 98
Thread-1: 97
2. 每个线程执行不同的任务
就如上面那个题目所描述的,两个线程执行 data 增,两个线程执行 data 减。针对这种情况,我们要实现两个 Runnable 了,因为很明显有两个不同的任务了,一个任务执行 data 增,另一个任务执行 data 减。为了便于维护,可以将两个任务方法放到一个类中,然后将 data 也放在这个类中,然后传到不同的 Runnabl e中,即可完成数据的共享。如下:
public class MultiThreadShareData {
public static void main(String[] args) {
ShareData task = new ShareData(); //公共数据和任务放在task中
for(int i = 0; i < 2; i ++) { //开启两个线程增加data
new Thread(new Runnable() {
@Override
public void run() {
task.increment();
}
}).start();
}
for(int i = 0; i < 2; i ++) { //开启两个线程减少data
new Thread(new Runnable() {
@Override
public void run() {
task.decrement();
}
}).start();
}
}
}
class ShareData /*implements Runnable*/ {
private int data = 0;
public synchronized void increment() { //增加data
System.out.println(Thread.currentThread().getName() + ": before : " + data);
data++;
System.out.println(Thread.currentThread().getName() + ": after : " + data);
}
public synchronized void decrement() { //减少data
System.out.println(Thread.currentThread().getName() + ": before : " + data);
data--;
System.out.println(Thread.currentThread().getName() + ": after : " + data);
}
}
看一下打印结果:
Thread-0: before : 0
Thread-0: after : 1
Thread-1: before : 1
Thread-1: after : 2
Thread-2: before : 2
Thread-2: after : 1
Thread-3: before : 1
Thread-3: after : 0
这样写的好处是两个任务方法可以直接在方法名上进行同步操作,这种模式的好处在前面的博文中已经有说过了,封装的好。
最后总结一下,多个线程之间共享数据主要关注两点就行:一是什么任务?几个任务?二是几个线程?记住 一点:几个任务和几个线程是没有关系的!100个线程可以执行一个任务,也可以执行2个任务,3个任务……
如果只有一个任务,那说明多个线程执行一个任务,我们只要实现一个 Runnable 接口,把公共 data 放进 Runnable,把任务放进去 run() 中即可(任务注意要同步),然后开启N个线程去执行这个任务即可;如果有M个任务,那我们新建一个专门执行任务的类,把公共的 data 放进类中,把任务作为类中的同步方法即可,然后开启N个线程,每个线程中扔一个 Runnable,按照要求执行任务类中的方法即可。
到这里,读者应该能体会到任务和线程的分离了,这种思想也算是面向对象的一种吧,思路很清晰。
多个线程之间共享数据就总结这么多,如有问题,欢迎交流,我们共同进步~
Java并发基础09. 多个线程间共享数据问题的更多相关文章
- Disruptor 线程间共享数据无需竞争
队列的作用是缓冲 缓冲到 队列的空间里.. 线程间共享数据无需竞争 原文 地址 作者 Trisha 译者:李同杰 LMAX Disruptor 是一个开源的并发框架,并获得2011 Duke’ ...
- 详解 Qt 线程间共享数据(用信号槽方式)
使用共享内存.即使用一个两个线程都能够共享的变量(如全局变量),这样两个线程都能够访问和修改该变量,从而达到共享数据的目的. Qt 线程间共享数据是本文介绍的内容,多的不说,先来啃内容.Qt线程间共享 ...
- Qt学习:线程间共享数据(使用信号槽传递数据,必须提前使用qRegisterMetaType来注册参数的类型)
Qt线程间共享数据主要有两种方式: 使用共享内存.即使用一个两个线程都能够共享的变量(如全局变量),这样两个线程都能够访问和修改该变量,从而达到共享数据的目的: 使用singal/slot机制,把数据 ...
- 详解 Qt 线程间共享数据(使用signal/slot传递数据,线程间传递信号会立刻返回,但也可通过connect改变)
使用共享内存.即使用一个两个线程都能够共享的变量(如全局变量),这样两个线程都能够访问和修改该变量,从而达到共享数据的目的. Qt 线程间共享数据是本文介绍的内容,多的不说,先来啃内容.Qt线程间共享 ...
- java并发基础(五)--- 线程池的使用
第8章介绍的是线程池的使用,直接进入正题. 一.线程饥饿死锁和饱和策略 1.线程饥饿死锁 在线程池中,如果任务依赖其他任务,那么可能产生死锁.举个极端的例子,在单线程的Executor中,如果一个任务 ...
- JAVA多线程学习八-多个线程之间共享数据的方式
多个线程访问共享对象和数据的方式 如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,买票系统就可以这么做. 如果每个线程执行的代码不同,这 ...
- Java并发基础:进程和线程之由来
转载自:http://www.cnblogs.com/dolphin0520/p/3910667.html 在前面,已经介绍了Java的基础知识,现在我们来讨论一点稍微难一点的问题:Java并发编程. ...
- Java并发编程的艺术(六)——线程间的通信
多条线程之间有时需要数据交互,下面介绍五种线程间数据交互的方式,他们的使用场景各有不同. 1. volatile.synchronized关键字 PS:关于volatile的详细介绍请移步至:Java ...
- Java并发编程(八)线程间协作(上)
多线程并发执行时,不同的线程执行的内容之间可能存在一些依赖关系,比如线程一执行a()方法和c()方法,线程二执行b()方法,方法a()必须在方法b()之前执行,而方法c()必须在方法b()之后执行.这 ...
随机推荐
- 【05】openlayers 网格图层
效果: 创建地图: //创建地图 var map = new ol.Map({ //设置显示地图的视图 view: new ol.View({ projection: 'EPSG:4326', //投 ...
- 用 jQuery 实现表单验证(摘抄)——选自《锋利的jQuery》(第2版)第5章的例题 5.1.5 表单验证
5.1.5 表单验证 表单(form)作为 HTML 最重要的一个组成部分,几乎在每个网页上都有体现,例如用户提交信息.用户反馈信息和用户查询信息等,因此它是网站管理者与浏览者之间沟通的桥梁.在表单中 ...
- Unity 随机数与随机种子
随机数几乎应用于游戏开发的方方面面,例如,随机生成的地图,迷宫,怪物属性等,在Unity中,使用随机数非常方便: // // 摘要: // Return a random integer number ...
- react 新创建项目
1,先创建一个文件夹用于存放项目 2,运行cmd,路径选择到你创建的文件夹内 3, npm install -g create-react-app create-react-app ...
- 「CSS」常见的清除浮动方法
下面介绍几种清除浮动的方案,供大家参考: 使用额外的标签clear:both .parent {padding: 10px;width: 200px;background: red;} .child ...
- Ubuntu18.04 从头开始编译 Android Native WebRTC
本文详细记录Mac下使用PD虚拟机安装ubuntu18.4桌面版,编译Android Native WebRTC的过程. 注意如果仅仅是使用WebRTC没必要手动编译源码,直接用官方提供的预编译包即可 ...
- 小白学 Python 数据分析(18):Matplotlib(三)常用图表(上)
人生苦短,我用 Python 前文传送门: 小白学 Python 数据分析(1):数据分析基础 小白学 Python 数据分析(2):Pandas (一)概述 小白学 Python 数据分析(3):P ...
- Apex_2. LiveBos两个时间求相差天数、历时
(1)获取两个时间相差天数(没有上午下午区分) var d1=ABS_DATESTRING(FStartTime,'yyyy/MM/dd'); var d2=ABS_DATESTRING(FEndTi ...
- 一文讲清楚MySQL事务隔离级别和实现原理,开发人员必备知识点
经常提到数据库的事务,那你知道数据库还有事务隔离的说法吗,事务隔离还有隔离级别,那什么是事务隔离,隔离级别又是什么呢?本文就帮大家梳理一下. MySQL 事务 本文所说的 MySQL 事务都是指在 I ...
- 【开源】使用Angular9和TypeScript开发RPG游戏
RPG系统构造 通过对于斗罗大陆小说的游戏化过程,熟悉Angular的结构以及使用TypeScript的面向对象开发方法. 项目地址 人物 和其他RPG游戏类似,游戏里面的人物角色大致有这样的一些属性 ...