JAVA-初步认识-第十四章-线程间通信-示例
Posted 照破山河万朵
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVA-初步认识-第十四章-线程间通信-示例相关的知识,希望对你有一定的参考价值。
一. 引言
之前讲述了线程的基本使用,卖票和存钱。卖票相当于把资源都释放出来,被别人获取到。而存钱,则是把数据都存进去。
现在,我们将线程进行了改变。以前是多个线程在执行同一个动作,无论是继承还是实现,都是一个run方法。换句话说,就是一个线程任务,多线程在同时执行一个任务。只不过它们是分别存放在了自己不同的线程栈里面,再进行统一运行,但是动作是一样的,比如说四个人都是卖票。
现在要讲述的是线程间通信。何谓通信,线程还是多个,但是运行的任务却变得不一样了。有个特点,它们处理的资源是一样的。
以前是处理同一个资源,同一个动作,现在不是了。
现在把线程间通信给大家讲述一下。
二. 线程通信程序的构建和问题
具体的内容我们是清楚地,但是叫通信有点奇怪。
先讲述具体的例子,再将例子抽象成程序。
用现实语句描述是这样的,Resource中有name和sex,input负责不断的更新name和sex,而output负责将原先的name和sex输出。为了更好的处理这两个动作,它们二者可以相互切换。
现在怎么将这个情况变成程序呢?
我们是对Resource中的name和sex进行操作,数据比较多,对其进行封装成对象,这样方便处理。
input在输入的同时,output在输出,两者同时操作。如果output不及时输出,那么将有部分数据被覆盖。这样一来就涉及多线程技术了。在这里的多线程中,input和output的任务还不一样。同时运行是没有问题的,但是任务完全不一样。任务不一样,就意味着我们要进行单独地封装。有两个任务对象,就表明有两个run方法,要分别封装在两个类当中(为什么任务也要单独封装成类?设定在资源类中不行么?我知道了,应该是两个run方法不能在同一个类中。如果可以的话,就是重载,重载怎么调用?)。因此到这里,有三个类,Resource类,input类,output类。
三个类中,我们先完善了输出类。输出不是一次就行的,因此设计了while(true),至于为什么是这样设计,以后会讲述到。在输出时,输出的是name和sex,但是这两者都是其他类的,因此要创建对象来调用。
紧接着,我们看一下输入,先不做切换,只是做一下简单的动作。
输入也不是一次的,也是多次。同时也要创建对象来接收name和sex数据。但这这样写不合适,因为输入和输出用的是两个资源,各自有各自的new Resource();这个就是问题。我们要求的是输入和输出是同一个资源。
有人说干脆静态,将变量name和sex静态(我猜说的变量的静态),之前卖票的时候讨论过这个问题,认为是设置成对象比较靠谱。只是静态变量不是很好。
现在资源不一样怎么办呢?将其变成单例,这个是可以的。但是单例一样有个局限性,就是只能出现一个对象,就和说的卖票一样,一个对象还是一百张票(我觉着可能是限制性太大了,只能是一个对象?)
可以肯定的是在输入类中,不能new对象(要和输出保持同一个对象),但是还要操作资源。可行的方法就是传参递,把参数传递进来。
怎么做呢?搞个函数传递。在外面把对象建立好,分别传递给输入和输出。(这个外面建立的对象,貌似要起的作用挺多,同时照顾两个类的操作)
能接收参数的方法有两种形式,要么是一般方法值,要么是构造函数。我们这是任务,处理资源,这就意味着任务一对象初始化就是资源,用构造函数就可以了。一构造对象,就需要有东西,就必须在对象里初始化完毕,就跟我们说线程一样,线程对象一创建,就必须有任务。这个思想来自于线程。
这里为了实现输入的特点,在这里面实现切换,输入两个名字,实现间隔切换,好像输入很多人名字这种效果。
怎么切换呢?
对于x的两种清楚,分开来赋值。那么要怎么切换x呢?
有人说x%2,这样可以实现0和1的切换,想法挺好,x得++。记住了,你想模以2变化,你必须实现x的变化,x不变化它怎么模以2变化呢?还要将这个值记下来。整体的循环是在while(true)的方法体内。
现在开始描述主函数。首先要有资源,没有资源什么都没法做(我的理解是要用来传递用的),Resource r=new Resource();接下来要有任务,接着就是任务对象,任务也有资源处理,Input in=new Input(r); 资源有了,任务有了,你得有路径,Thread t1=new Thread(in),
创建资源→创建任务→创建线程,执行任务→开启线程。有任务的时候,必须明确资源,有路径的时候,必须明确任务。
先要停止,ctrl+alt+c。DOS结果出现了问题,丽丽后面有nan,而mike后面有女女女女女。
出现了问题,我们要去解决,但是首先我们必须知道这个问题怎么来的。
三. 了解线程通信程序问题的原因和解决措施
这里专业术语叫做线程安全问题。
成因怎么分析?
在线程运行的代码中是否有共享数据?是否有多条语句在操作共享数据?name和sex是资源的内容,而资源是共享的,在这出现的问题。r是资源,我在操作资源中的两个属性,r.name和r.sex,而且在多条语句中操作,if中有,else中也有。
在争抢的过程中,input先输入了mike nan,第二次刚输入丽丽,cpu执行权就被output拿走了,就是在这出现的问题。(这是否就意味着要引入同步?)
DOS结果还是出现了问题,(我觉着是同步代码块括的范围太广了?)
现在是加了同步,却没有解决问题,是我同步加的不对,还是同步解决不了这个问题?
怎么解决问题?当你碰到线程安全问题时,你想要的无非是同步解决,可以你加同步了,问题依旧怎么办?要去考虑同步的前提。
前提合并完就是一句话,一个锁里面是否有多个线程。换句话说,就是多个线程是否都在同一个锁当中。
我们看上面的截图,是同步,但是同步代码块里面就一个线程,为什么么?输出线程不在同步代码块中,所以我们应该保证输出线程也应该在同步里面。你不在同步,光同步输入有什么用?什么意思啊,你输入一半的时候,我不能过去取。
还是有问题,现在是将线程往同步里面放,但是这里用的是不同的锁。现在我们就不写obj了,不好使了。有的人说用this,this白扯,这是两个类,而this代表本类。有人说,用静态锁,输入类中写Input.class,输出写Output.class. 这和扯一样,
用Input.class是可以,如果用Resource.class绝对是没问题,它是唯一的。在这里不需要用class字节码文件对象,在这只需要保证对象唯一就能用,正好资源就是唯一的,两者处理的都是同一个资源,往里放r就可以了。
DOS运行的结果全是一种,有问题。
再操作的过程中除了if,else语句需要同步外,还有语句在操作资源,
下图中的输出语句也在操作资源,下面这句和if同时操作资源。
我们应该将同步放在里面,
为什么这么说?如果将同步放置在while(true)的外面,那么线程进来后,就出不去了,一直在循环。
这样改之后,就没有问题了。
现在问题没有了,但是结果是一大片,一大片。为什么会是连续的一片?
输入线程它获取到执行权的时候,它不会只扔一次,它能执行好多次,那就意味着输入线程拿到执行权先往里面放了一个mike nan,放完以后,它还拿着执行权,紧跟着就赋值了丽丽 女,这就导致前一个数据被覆盖了。接着又往里赋值mike nan,丽丽 女,等赋值的差不多了,执行权切到输出,资源里面要么是mike nan,要么是丽丽 女,由于是同步,肯定不会出现mike 女之类的,输出的时候,它取得是最后赋值的数据,由于拿着执行权,因此输出也不是一次。
以上是关于JAVA-初步认识-第十四章-线程间通信-示例的主要内容,如果未能解决你的问题,请参考以下文章
JAVA-初步认识-第十四章-线程间通信-等待唤醒机制-代码优化
JAVA-初步认识-第十四章-线程间通信-多生产者多消费者问题-JDK1.5解决办法
JAVA-初步认识-第十四章-线程间通信-多生产者多消费者问题-JDK1.5新特性解决办法-范例
JAVA-初步认识-第十四章-线程间通信-多生产者多消费者问题-JDK1.5新特性-Condition