黑马程序员--JAVA中的多线程

来源:互联网 发布:mysql unique查询 编辑:程序博客网 时间:2024/05/29 00:30

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

一、创建多线程

1、多线程的两种创建方式

   java.lang.Thread类的实例就是一个线程但是它需要调用java.lang.Runnable接口来执行,由于线程类本身就是调用的Runnable接口所以可以继承java.lang.Thread 类或者直接实现Runnable接口来重写run()方法实现线程。

2、继承Thread类的多线程创建方式

流程为

    1>定义一个类继承Thread类。
    2> 覆盖Thread类中的run方法。
    3>直接创建Thread的子类对象创建线程。
    4>调用start方法开启线程并调用线程的任务run方法执行。

示例:

 

class Demo extends Thread{      private String name ;      Demo(String name){             this.name = name;      }       public void run(){             for(int x = 0; x < 10; x++){                  System.out.println(name + "...x=" + x + "...ThreadName=" + Thread.currentThread ().getName());            }      }}class ThreadDemo{       public static void main(String[] args){            Demo d1 = new Demo("第一个");            Demo d2 = new Demo("2222");            d1.start(); //开启线程0,调用run方法。            d2.start();<pre name="code" class="java">                                //开启线程1,调用run方法。
for(int x = 0; x < 20; x++){ System.out.println("x = " + x + "...over..." + Thread.currentThread().getName()); } }}


3、实现Runnable接口的多线程创建方式

流程为

1> 定义类实现Runnable接口。
    2>覆盖接口中的run方法,将线程的任务代码封装到run方法中。
    3>通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。为什么?因为线程的任务都封装在Runnable接口子类对象的run方法中。所以要在线程对象创建时就必须明确要运行的任务。
    4>调用线程对象的start方法开启线程。

示例

 

//准备扩展Demo类的功能,让其中的内容可以作为线程的任务执行。//通过接口的形式完成。class Demo implements Runnable{//重写run方法,标志需要多线程实现的内容      public void run(){            show();      }      public void show(){             for(int x = 0; x < 20; x++){                  System.out.println(Thread.currentThread().getName() + "..." + x);             }      }}class ThreadDemo{       public static void main(String[] args){            Demo d = new Demo();            Thread t1 = new Thread(d);//创建带有开启线程方法Thread的对象            Thread t2 = new Thread(d);            t1.start();//开启线程0            t2.start();//开启线程1       }}

4、两种创建方式的比较

将线程的任务从线程的子类中分离出来,进行了单独的封装,按照面向对象的思想将任务封装成对象避免了Java单继承的局限性。所以,创建线程实现Runnable接口的方式较为常用。

二、多线程的安全问题

1、 线程安全问题产生的原因:

   1> 多个线程在操作共享的数据。
   2>操作共享数据的线程代码有多条。
   3>当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,    就会导致线程安全问题的产生。

2、线程安全问题的解决方案:

   1>同步代码块:

        格式: synchronized(对象){
                  需要被同步的代码;
                  } 

    2>同步函数:

      格式:在函数上加上synchronized修饰符即可。

示例: 需求:模拟火车站4个窗口同时卖100张票。

/* * 思路: * 有多个线程在操作共享的数据。 * 当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会导致线        程安全问题的产生 * 需要同步代码块解决 */class Ticket implements Runnable{      private int num = 100;      Object obj = new Object();      public void run(){             while(true ){             //添加同步代码块,同时只能有一个线程执行代码块内容                   synchronized(obj ){                         if(num > 0){                              System.out.println(Thread.currentThread().getName() + "...sale..." + num--);                        }                   }              }      }}class TicketDemo{       public static void main(String[] args){            Ticket t = new Ticket();            Thread t1 = new Thread(t);            Thread t2 = new Thread(t);            Thread t3 = new Thread(t);            Thread t4 = new Thread(t);            //开启多线程            t1.start();            t2.start();            t3.start();            t4.start();      }}


 

三、线程间的通讯

1、为什么需要线程间的通讯?

多个线程在处理统一资源,但是任务却不同,这时候就需要线程间通信。

2、如何解决线程间的通讯问题?

Java提供了3个非常重要的方法来巧妙地解决线程间的通信问题。这3个方法分别是:wait()notify()notifyAll()。它们都是Object类的最终方法,因此每一个类都默认拥有它们。虽然所有的类都默认拥有这3个方法,但是只有在synchronized关键字作用的范围内,并且是同一个同步问题中搭配使用这3个方法时才有实际的意义。

3、多线程间通讯需要注意的问题:

     1>这些方法都必须定义在同步中,因为这些方法是用于操作线程状态的方法。
     2>必须要明确到底操作的是哪个锁上的线程!

4、具体步骤:

下面这个程序演示了多个线程之间进行通信的具体实现过程。程序中用到了4个类,其中ShareData类用来定义共享数据和同步方法。在同步方法中调用了wait()方法和notify()方法,并通过一个信号量来实现线程间的消息传递。 

示例:生产者和消费者之间的消息传递过程

class ShareData  {      private char c;      private boolean isProduced = false; // 信号量      public synchronized void putShareChar(char c) // 具备同步的方法putShareChar()      {          if (isProduced) // 如果产品还未消费,则生产者等待          {              try              {                  wait(); // 生产者等待                }catch(InterruptedException e)              {                  e.printStackTrace();              }          }          this.c = c;          isProduced = true; // 标记已经生产          notify(); // 通知消费者已经生产,可以消费      }      public synchronized char getShareChar() // 同步方法getShareChar()      {          if (!isProduced) // 如果产品还未生产,则消费者等待          {              try              {                  wait(); // 消费者等待                }catch(InterruptedException e)              {                  e.printStackTrace();              }          }          isProduced = false; // 标记已经消费          notify(); // 通知需要生产          return this.c;      }  }  class Producer extends Thread // 生产者线程  {      private ShareData s;      Producer(ShareData s)      {          this.s = s;      }      public void run()      {          for (char ch = 'A'; ch <= 'D'; ch++)          {              try              {                  Thread.sleep((int)(Math.random()*3000));              }              catch(InterruptedException e)              {                  e.printStackTrace();              }              s.putShareChar(ch); // 将产品放入仓库              System.out.println(ch + " is produced by Producer.");          }      }  }  class Consumer extends Thread // 消费者线程  {      private ShareData s;      Consumer(ShareData s)      {          this.s = s;      }      public void run()      {          char ch;          do          {              try              {                  Thread.sleep((int)(Math.random()*3000));              }              catch(InterruptedException e)              {                  e.printStackTrace();              }                  ch = s.getShareChar(); // 从仓库中取出产品                  System.out.println(ch + " is consumed by Consumer. ");              }              while (ch != 'D');          }      }  class CommunicationDemo  {      public static void main(String[] args)      {          ShareData s = new ShareData();          new Consumer(s).start();          new Producer(s).start();      }  }



0 0
原创粉丝点击