Java-多线程(学习笔记)

多线程通信-解决安全问题的代码示例(第一天)

多线程通信就是多个线程在操作同一个资源,但是操作的动作不同。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类中。


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值