多线程通信-解决安全问题的代码示例(第一天)
多线程通信就是多个线程在操作同一个资源,但是操作的动作不同。eg.A线程存数据,B线程取数据,动作不一致,代码不一致,用两个run方法放在两个类中。
代码示例:
class Res {
string name;
string sex;
}
class Input implements Runnable
{
Res r = new Res();
public void run()
{
r.name;
r.sex;
}
}
class Output implements Runnable
{
Res r = new Res();
public void run()
{
r.name;
r.sex;
}
}//输入输出本应该使用同一个对象,可是却创建了两个对象,所以是不对的
修改1:
class Res {
String name;
String sex;
}
class Input implements Runnable
{
private Res r;//不搞对象,建立引用直接往里传
Input(Res r)
{
this.r = r
}
public void run()
{
int x=0;
while(true)
{
if(x==0)
{
r.name="mike";
r.sex="man";
}
else
{
r.name="丽丽";
r.sex="女";
}
x=(x+1)%2;
}
}
}
class Output implements Runnable
{
private Res r;
Output(Res r)
{
this.r = r;
}
public void run()
{
while(true)//打印越多越容易看到安全问题
{
Syetem.out.println(r.name"..."+r.sex)
}
}
}
class InputOutputDemo
{
public static void main(string[] args)
{
Res r = new Res();.//新建一堆煤
Input in = new Input(r);//建立两个运煤的车,一个运进来一个运进去
Output out = new Output(r);
Thread t1 = new Thread(in);//两个线程,就是把两辆车放在高速路上
Thread t2 = new Thread(out);
t1.start();//两个高速路开启
t2.start();
}
}
结果是这样的:人妖
小插曲:为了运行一下这个程序,顺带把怎么在eclipse上编写java程序学会了,还挺简单的。尼玛这程序运行半天都停不下来,电脑都呼呼的了,所以顺带百度了一下如何强制停止,就是点击一下控制台上的红色小方块,好方便,要是以前估计我会直接把eclipse关掉,好傻x.....,鼓励自己进步只是一点点,不要慌慢慢来
以上人妖的出现就是多线程的安全问题,产生这样结果的原因是输入被输出抢走,解决方式是同步。注意不能在方法前面加同步,否则就变成了单线程了。因为是两个线程所以必须输入输出两个都同步。输入输出的两把锁必须是唯一的,否则还是没办法同步。所以主要需要学习的就是synchronized括号里究竟应该添加啥?this可以吗?this是本类对象,还是两把锁了。Input.class或者Output.class都可以,因为它们都是唯一的,结果都是正确的,但把类的对象拿出来是不是太牵强?所以最好用r:资源,资源是唯一的。
修改2:
class Res {
String name;
String sex;
}
class Input implements Runnable
{
private Res r;//不搞对象,建立引用直接往里传
Input(Res r)
{
this.r = r
}
public void run()
{
int x=0;
while(true)
{
synchronized(r)//修改的地方
{
if(x==0)
{
r.name="mike";
r.sex="man";
}
else
{
r.name="丽丽";
r.sex="女";
}
x=(x+1)%2;
}
}
}
}
class Output implements Runnable
{
private Res r;
Output(Res r)
{
this.r = r;
}
public void run()
{
while(true)//打印越多越容易看到安全问题
{
synchronized(r)//修改的地方
{
Syetem.out.println(r.name"..."+r.sex)
}
}
}
}
class InputOutputDemo
{
public static void main(string[] args)
{
Res r = new Res();.//新建一堆煤
Input in = new Input(r);//建立两个运煤的车,一个运进来一个运进去
Output out = new Output(r);
Thread t1 = new Thread(in);//两个线程,就是把两辆车放在高速路上
Thread t2 = new Thread(out);
t1.start();//两个高速路开启
t2.start();
}
结果如下:正确了
以上学习内容均是对毕向东老师授课的学习笔记,讲课满满的东北口音,但是讲得非常清楚不罗嗦,推荐给大家,也希望自己能够完完整整的学习完,一定会有进步的!
线程间通信-等待唤醒机制(第二天)
以上结果其实并不是我们想要的,我们想输入一个就输出一个,也就是说结果不应该是相同的内容连着被打印出来
那出现上一张图片所显示的结果的原因是什么呢?在程序中input和output并没有被规定一个执行完就执行另一个,所以很有可能input执行完了继续执行,执行了好多次才轮到了output,同理,output也不能输出一次,执行的时候可能执行多次,一个值打印多遍。所以我们要在输入输出中加上标记,告诉输入输出是否应该继续执行还是等待,这就是今天要学习的等待唤醒机制。
修改3:
class Res {
String name;
String sex;
boolean flag = false;//添加的标记
}
class Input implements Runnable
{
private Res r;//不搞对象,建立引用直接往里传
Input(Res r)
{
this.r = r
}
public void run()
{
int x=0;
while(true)
{
synchronized(r)
{
if(r. flag)
wait();//wait()使用时抛出异常,所以修改成:try{wait();}catch(Exception e){} **用在同步里,必须标识出wiat()所在的线程,所属的锁。所以是r.wait(),wait的是持有r这个的线程
if(x==0)
{
r.name="mike";
r.sex="man";
}
else
{
r.name="丽丽";
r.sex="女";
}
x=(x+1)%2;
r.flag=true;//修改为真
notify();//叫醒,放弃执行资格 **同步会出现嵌套,所以notify时要唤醒的是r这个锁上的线程
}
}
}
class Output implements Runnable
{
private Res r;
Output(Res r)
{
this.r = r;
}
public void run()
{
while(true)//打印越多越容易看到安全问题
{
synchronized(r)
{
if(!r.flag)
try{r.wait();}catch{Exception e){};//非真就不执行
{
Syetem.out.println(r.name"..."+r.sex);
r.flag=true;//标记为真
r.notify();//唤醒
}
}
}
class InputOutputDemo
{
public static void main(string[] args)
{
Res r = new Res();.//新建一堆煤
Input in = new Input(r);//建立两个运煤的车,一个运进来一个运进去
Output out = new Output(r);
Thread t1 = new Thread(in);//两个线程,就是把两辆车放在高速路上
Thread t2 = new Thread(out);
t1.start();//两个高速路开启
t2.start();
}
}
备注:notifyAll(),wait(),notify()都用在同步中,因为要对持有监视器(锁)的线程操作,所以要使用在同步中,因为只有同步才具有锁的概念。
为什么这些操作线程的方法要定义在object类中?(锁可以是任意对象,任意对象能调用的方法当然应该定义在上帝里,,object类就是上帝)
因为这些方法在操作同步线程时,都必须要标记它们所操作线程只有的锁,只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒,不可以对不同锁上的线程进行唤醒。也就是说等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义在object类中。