java多线程知识点总结

来源:互联网 发布:js option被选中事件 编辑:程序博客网 时间:2024/05/16 08:26

多线程(英语:multithreading)

每个正在系统上运行的程序都是一个进程。每个进程包含一到多个线程。线程可以在程序里独立执行,由操作系统负责多个线程的调度和执行。

线程和进程的区别在于,子进程和父进程有不同的代码和数据空间,而多个线程则共享数据空间,每个线程有自己的执行堆栈和程序计数器为其执行上下文.多线程主要是为了节约CPU时间,充分利用了CPU的空闲时间片,线程的运行中需要使用计算机的内存资源和CPU。

优点

·使用线程可以把占据时间长的程序中的任务放到后台去处理
·用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度
·程序的运行速度可能加快
·在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下可以释放一些珍贵的资源如内存占用等等。

缺点

·如果有大量的线程,会影响性能,因为操作系统需要在它们之间切换。
·更多的线程需要更多的内存空间。
·线程可能会给程序带来更多“bug”,因此要小心使用。
·线程的中止需要考虑其对程序运行的影响。
·通常块模型数据是在多个线程间共享的,需要防止线程死锁情况的发生。


JAVA中的线程

一个Java应用总是从main()方法开始运行,mian()方法运行在一个线程内,它被称为主线程。

main方法其实也是一个线程。在java中所以的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。

 

java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个jVM实习在就是在操作系统中启动了一个进程。



java中创建线程的两种方式:继承Thread和实现Runnable接口
实现Runnable接口适用情况:资源共享+多个线程执行相同代码。
资源共享例子:购票系统,多个线程对共享资源进行读写。
执行相同代码例子:购票系统。new Thread(obj);obj是实现runnable的对象。

java多线程购票系统代码:
class SellThread implements Runnable         {            int tickets=100;//对共享资源的访问            public synchronized void sell()//用关键字sychronized,标志同步方法            {                  if(tickets>0)                  {                     try                     {                        Thread.sleep(10);                     }                     catch(Exception e)                     {                        e.printStackTrace();                     }                  }               }                    public void run()         {            while(true)            {               sell();            }         }}public class TicketSystem{   public static void main(String[] args)   {      SellThread st=new SellThread();      new Thread(st).start();//多个购票窗口执行相同操作      new Thread(st).start();//多个购票窗口执行相同操作      new Thread(st).start();//多个购票窗口执行相同操作   }}

继承Thread类适用情况:
多个线程执行不同的代码,则分别继承重写Thread类。
如:生产者和消费者例子。
很多后台服务程序并发控制的基本原理都可以归纳为生产者/消费者模式。
用wait() / notify()方法实现生产者消费者例子
(代码来自http://blog.csdn.net/monkey_d_meng/article/details/6251879,感谢原作者
[java] view plain copyimport java.util.LinkedList;    /**  * 仓库类Storage实现缓冲区  *   * Email:530025983@qq.com  *   * @author MONKEY.D.MENG 2011-03-15  *   */  public class Storage  {      // 仓库最大存储量      private final int MAX_SIZE = 100;        // 仓库存储的载体      private LinkedList<Object> list = new LinkedList<Object>();        // 生产num个产品      public void produce(int num)      {          // 同步代码段          synchronized (list)          {              // 如果仓库剩余容量不足              while (list.size() + num > MAX_SIZE)              {                  System.out.println("【要生产的产品数量】:" + num + "/t【库存量】:"                          + list.size() + "/t暂时不能执行生产任务!");                  try                  {                      // 由于条件不满足,生产阻塞                      list.wait();                  }                  catch (InterruptedException e)                  {                      e.printStackTrace();                  }              }                // 生产条件满足情况下,生产num个产品              for (int i = 1; i <= num; ++i)              {                  list.add(new Object());              }                System.out.println("【已经生产产品数】:" + num + "/t【现仓储量为】:" + list.size());                list.notifyAll();          }      }        // 消费num个产品      public void consume(int num)      {          // 同步代码段          synchronized (list)          {              // 如果仓库存储量不足              while (list.size() < num)              {                  System.out.println("【要消费的产品数量】:" + num + "/t【库存量】:"                          + list.size() + "/t暂时不能执行生产任务!");                  try                  {                      // 由于条件不满足,消费阻塞                      list.wait();                  }                  catch (InterruptedException e)                  {                      e.printStackTrace();                  }              }                // 消费条件满足情况下,消费num个产品              for (int i = 1; i <= num; ++i)              {                  list.remove();              }                System.out.println("【已经消费产品数】:" + num + "/t【现仓储量为】:" + list.size());                list.notifyAll();          }      }        // get/set方法      public LinkedList<Object> getList()      {          return list;      }        public void setList(LinkedList<Object> list)      {          this.list = list;      }        public int getMAX_SIZE()      {          return MAX_SIZE;      }  }  /**  * 生产者类Producer继承线程类Thread  *   * Email:530025983@qq.com  *   * @author MONKEY.D.MENG 2011-03-15  *   */  public class Producer extends Thread  {      // 每次生产的产品数量      private int num;        // 所在放置的仓库      private Storage storage;        // 构造函数,设置仓库      public Producer(Storage storage)      {          this.storage = storage;      }        // 线程run函数      public void run()      {          produce(num);      }        // 调用仓库Storage的生产函数      public void produce(int num)      {          storage.produce(num);      }        // get/set方法      public int getNum()      {          return num;      }        public void setNum(int num)      {          this.num = num;      }        public Storage getStorage()      {          return storage;      }        public void setStorage(Storage storage)      {          this.storage = storage;      }  }  /**  * 消费者类Consumer继承线程类Thread  *   * Email:530025983@qq.com  *   * @author MONKEY.D.MENG 2011-03-15  *   */  public class Consumer extends Thread  {      // 每次消费的产品数量      private int num;        // 所在放置的仓库      private Storage storage;        // 构造函数,设置仓库      public Consumer(Storage storage)      {          this.storage = storage;      }        // 线程run函数      public void run()      {          consume(num);      }        // 调用仓库Storage的生产函数      public void consume(int num)      {          storage.consume(num);      }        // get/set方法      public int getNum()      {          return num;      }        public void setNum(int num)      {          this.num = num;      }        public Storage getStorage()      {          return storage;      }        public void setStorage(Storage storage)      {          this.storage = storage;      }  }  /**  * 测试类Test  *   * Email:530025983@qq.com  *   * @author MONKEY.D.MENG 2011-03-15  *   */  public class Test  {      public static void main(String[] args)      {          // 仓库对象          Storage storage = new Storage();            // 生产者对象          Producer p1 = new Producer(storage);          Producer p2 = new Producer(storage);          Producer p3 = new Producer(storage);          Producer p4 = new Producer(storage);          Producer p5 = new Producer(storage);          Producer p6 = new Producer(storage);          Producer p7 = new Producer(storage);            // 消费者对象          Consumer c1 = new Consumer(storage);          Consumer c2 = new Consumer(storage);          Consumer c3 = new Consumer(storage);            // 设置生产者产品生产数量          p1.setNum(10);          p2.setNum(10);          p3.setNum(10);          p4.setNum(10);          p5.setNum(10);          p6.setNum(10);          p7.setNum(80);            // 设置消费者产品消费数量          c1.setNum(50);          c2.setNum(20);          c3.setNum(30);            // 线程开始执行          c1.start();          c2.start();          c3.start();          p1.start();          p2.start();          p3.start();          p4.start();          p5.start();          p6.start();          p7.start();      }  }  【要消费的产品数量】:50   【库存量】:0 暂时不能执行生产任务!  【要消费的产品数量】:30   【库存量】:0 暂时不能执行生产任务!  【要消费的产品数量】:20   【库存量】:0 暂时不能执行生产任务!  【已经生产产品数】:10    【现仓储量为】:10  【要消费的产品数量】:20   【库存量】:10    暂时不能执行生产任务!  【要消费的产品数量】:30   【库存量】:10    暂时不能执行生产任务!  【要消费的产品数量】:50   【库存量】:10    暂时不能执行生产任务!  【已经生产产品数】:10    【现仓储量为】:20  【要消费的产品数量】:50   【库存量】:20    暂时不能执行生产任务!  【要消费的产品数量】:30   【库存量】:20    暂时不能执行生产任务!  【已经消费产品数】:20    【现仓储量为】:0  【已经生产产品数】:10    【现仓储量为】:10  【已经生产产品数】:10    【现仓储量为】:20  【已经生产产品数】:80    【现仓储量为】:100  【要生产的产品数量】:10   【库存量】:100   暂时不能执行生产任务!  【已经消费产品数】:30    【现仓储量为】:70  【已经消费产品数】:50    【现仓储量为】:20  【已经生产产品数】:10    【现仓储量为】:30  【已经生产产品数】:10    【现仓储量为】:40  

看完上述代码,对wait() / notify()方法实现的同步有了了解。你可能会对Storage类中为什么要定义public void produce(int num);public void consume(int num);方法感到不解,为什么不直接在生产者类Producer和消费者类Consumer中实现这两个方法,却要调用Storage类中的实现呢?淡定,后文会有解释。我们先往下走。


wait()、notify()、notifyAll()是三个定义在Object类里的方法,可以用来控制线程的状态。

这三个方法最终调用的都是jvm级的native方法。随着jvm运行平台的不同可能有些许差异。
    如果对象调用了wait方法就会使
持有该对象的线程把该对象的控制权交出去,然后处于等待状态。
    如果对象调用了notify方法就会通知某个正在等待这个对象的控制权的线程可以继续运行。
    如果对象调用了notifyAll方法就会通知所有等待这个对象控制权的线程继续运行。



有朋友可能会有疑问:为何这三个不是Thread类声明中的方法,而是Object类中声明的方法(当然由于Thread类继承了Object类,所以Thread也可以调用者三个方法)?其实这个问题很简单,由于每个对象都拥有monitor(即锁),

所以

让当前线程等待某个对象的锁,当然应该通过这个对象来操作了。而不是用当前线程来操作,因为当前线程可能会等待多个线程的锁,如果通过线程来操作,就非常复杂了。


任何一个对象都有锁,wait(),notify(),notifyall()方法

当前线程必须拥有这个对象的monitor(即锁)

,如果调用某个对象的wait()方法,当前线程必须拥有这个对象的monitor(即锁),因此调用wait()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。


对象的控制权就是对象的锁。

注:拥有这个对象锁的线程才能运行运行其中synchronized方法,也即拥有这个对象控制权的线程才能执行synchrozied方法。进而,拥有了控制权,才能用wait()释放控制权

 


synchronized关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。

当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。

当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。

当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。

当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。


     四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。



1 0
原创粉丝点击