黑马程序员 多线程(二)

本文详细介绍了Java并发编程的核心概念,包括同步方法、死锁问题、线程间通信及线程安全问题的解决方法。通过实例分析了同步方法与同步代码块的使用场景,探讨了静态方法的锁机制,并引入了多线程环境下数据一致性问题的解决方案。文章还涉及线程安全问题的改进策略,如使用wait和notify方法进行线程间的协作。最后,讨论了Java中线程的控制与管理,包括停止线程的方式和Thread类中常用方法的作用。
摘要由CSDN通过智能技术生成

------- android培训java培训、期待与您交流! ----------


1:同步方法
  A:当一个方法的方法体都被同步代码块给包围起来了,
    我们就可以使用另外一种方式来表示。就是同步方法。
  B:同步方法的使用格式非常简单。就是在方法声明上加同步关键字。
  C:同步方法的锁是什么呢?如何测试的呢?
    同步方法的锁是this对象。
    通过在run里面同时使用同步方法和同步代码块来测试的。
  D:静态方法的锁是什么呢?
    静态方法的锁是当前类的class文件描述类对象

    思考:单例设计模式懒汉式为什么会出现线程安全问题。
 class Student
 {
  private Student(){}
 
  private static Student s = null;
 
  public static Student getInstance()
  {
   if(s==null)
   {
    s = new Student();
   }
   return s;
  }
 }

 由于其存在着线程安全问题,所以,我们考虑改进。改进如下
 class Student
 {
  private Student(){}
 
  private static Student s = null;
 
  public static synchronized Student getInstance()
  {
   if(s==null)
   {
    s = new Student();
   }
   return s;
  }
 }

 那么,我们到底是使用同步方法还是使用同步代码块呢?

 练习:
  有两个老师选择学生,每个老师选择一名男生一名女生,然后由第二个老师选,
  反复如此。选择完毕后,我们可以打印一下看每个老师选择的男生和女生人数是否相等。
  用多线程模拟一下。

  class Student
  {
   private int boy = 0;
   private int girl = 0;

   void add()
   {
    boy++;
    girl++;
    System.out.println(boy+"***"+girl+":"+(boy==girl));
   }
  }

  class Teacher implements Runnable
  {
   Student s = new Student();

   public void run()
   {
    while(true)
    {
     s.add();
    }
   }
  }

  class StudentTest
  {
   public static void main(String[] args)
   {
    Teacher t = new Teacher();

    Thread t1 = new Thread(t);
    Thread t2 = new Thread(t);

    t1.start();
    t2.start();
   }
   
  }


2:死锁问题
 A:产生原因
  A锁和B锁互相等待。
 B:解决方案
  不写出死锁的代码。


3:线程间的通信(交互)
 有一个学生对象,我们分别用两个线程进行输入和输出数据。

 第一版本:
 class Student
 {
  String name;
  int age;
 }

 class InputStudent implements Runnable
 {
  Student s = new Student();

  public void run()
  {
   s.name = "Jack";
   s.age = 20;
  }
 }

 class OutputStudent implements Runnable
 {
  Student s = new Student();

  public void run()
  {
   sop(s.name+"***"+s.age);
  }
 }

 class StudentTest
 {
  public static void main(String[] args)
  {
   InputStudent is = new InputStudent();
   OutputStudent os = new OutputStudent();

   Thread t1 = new Thread(is);
   Thread t2 = new Thread(os);

   t1.start();
   t2.start();
  }
 }

 第一版本的问题:
  第一个:学生对象是个资源对象,是被输入和输出线程共享的。
          而现在,他们确实两个对象。
  第二个:线程的操作,一般只有多次执行才能较好的看到效果。
          所以,我们在run方法里面加入循环
 第二版本:
 class InputStudent implements Runnable
 {
  private Student s;

  public InputStudent(){}

  public InputStudent(Student s)
  {
   this.s = s;
  }

  public void run()
  {
   int x = 0;
   while(true)
   {
    if(x%2==0)
    {
     s.name = "Jack";
     s.age = 20;
    }
    else
    {
     s.name = "Rose";
     s.age = 25;
    }
    x++;
   }
  
  }
 }

 class OutputStudent implements Runnable
 {
  private Student s;

  public OutputStudent(){}

  public OutputStudent(Student s)
  {
   this.s = s;
  }

  public void run()
  {
   while(true)
   {
    sop(s.name+"***"+s.age);
   }
  }
 }

 class StudentTest
 {
  public static void main(String[] args)
  {
   Student s = new Student();

   InputStudent is = new InputStudent(s);
   OutputStudent os = new OutputStudent(s);

   Thread t1 = new Thread(is);
   Thread t2 = new Thread(os);

   t1.start();
   t2.start();
  }
 }

 第二版问题:
  第一个:数据出现了不匹配的问题。
  思考,怎么产生的?由于多线程操作的随机性导致的。

  解决方案:加入同步,并且用s这个对象锁。
            对多个线程都要加锁,并且是同一把锁。
 
 第三版:
 class InputStudent implements Runnable
 {
  private Student s;

  public InputStudent(){}

  public InputStudent(Student s)
  {
   this.s = s;
  }

  public void run()
  {
   int x = 0;
   while(true)
   {
    synchronized(s)
    {
     if(x%2==0)
     {
      s.name = "Jack";
      s.age = 20;
     }
     else
     {
      s.name = "Rose";
      s.age = 25;
     }
    }
    x++;
   }
  
  }
 }

 class OutputStudent implements Runnable
 {
  private Student s;

  public OutputStudent(){}

  public OutputStudent(Student s)
  {
   this.s = s;
  }

  public void run()
  {
   while(true)
   {
    synchronized(s)
    {
     sop(s.name+"***"+s.age);
    }
   }
  }
 }

 到此为止,我们解决了线程的安全问题。但是这个程序还是有不足之处。
 第三版的问题:
  数据打印是一片一片的。所以,需要改进。
  改进的时候,我们思考到了两种方案:sleep和wait。
  最终选择了wait。


 第四版:
 class Student
 {
  String name;
  int age;
  boolean flag = false;
 }

 class InputStudent implements Runnable
 {
  private Student s;

  public InputStudent(){}

  public InputStudent(Student s)
  {
   this.s = s;
  }

  public void run()
  {
   int x = 0;
   while(true)
   {
    synchronized(s)
    {
     if(s.flag)
     {
      try
      {
       s.wait();
      }
      catch(InterruptedException e)
      {
       e.printStackTrace();
      }
     
     }

     if(x%2==0)
     {
      s.name = "Jack";
      s.age = 20;
     }
     else
     {
      s.name = "Rose";
      s.age = 25;
     }

     //改变标记。唤醒线程
     s.flag = true;
     s.notify();
    }
    x++;
   }
  
  }
 }

 class OutputStudent implements Runnable
 {
  private Student s;

  public OutputStudent(){}

  public OutputStudent(Student s)
  {
   this.s = s;
  }

  public void run()
  {
   while(true)
   {
    synchronized(s)
    {
     if(!s.flag)
     {
      try
      {
       s.wait();
      }
      catch(InterruptedException e)
      {
       e.printStackTrace();
      }
     }

     sop(s.name+"***"+s.age);
     s.flag = false;
     s.notify();
    }
   }
  }
 }


 wait:让线程等待
 notify:唤醒单个线程
 notifyAll:唤醒所有线程

 举例:抓人游戏。


4:wait和sleep的区别
 A:对于时间的指定
  sleep()必须指定时间。
  wait()方法有重载的形式,可以指定时间,也可以不指定时间。
 B:对执行权和锁的释放
  sleep释放执行权,不释放锁。
  wait释放执行权,释放锁。


5:停止线程
 A:run自动结束。
 B:stop方法。但是已经过时了。被interrupt替代。
 C:可以通过控制循环条件来结束。


6:Thread类中的几个方法
 A:interrupt():中断线程,并抛出一个异常
 B:setDaemon(boolean b):设置线程为守护线程。(坦克大战)
 C:join():加入线程,具有优先执行权
 D:setPriority(int num):设置线程优先级。线程的优先级从1到10,默认为5。
 E:toString():返回该线程的字符串表示形式,包括线程名称、优先级和线程组。
 F:yield():给其他线程让路,让多个线程间的操作趋近于平衡。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值