day11_多线程(线程生命周期,Runnable)


 

线程概述:

/*
1.进程:是一个正在执行中的程序
  
  每一个进程的执行都有一个执行顺序.该顺序是一个执行路径
          或者叫一个控制单元.
        进程用于封装控制单元

2.线程:就是进程中的一个独立的控制单元
        线程在控制着进程的执行.

一个进程中至少有一个线程

JVM启动的时候会有一个进程java.exe.

该进程中至少有一个线程负责java程序的执行
而且这个线程运行的代码存在于main方法中
该线程称之为主线程

扩展:其实更细说明JVM,JVM启动不止一个线程,还有负责垃圾回收机制的线程.
     这是因为,当某个对象不被引用时,会变成垃圾

多线程存在的意义:
   如果没有多线程,只有主线程.不停的执行过程中,当堆内存产生n多垃圾
   无法在堆内存分配空间,这时候主程序必须停掉,回头把垃圾处理

   反之,一边执行,一边扔垃圾(多线程)-->提高了效率
*/



class ThreadDemo 
{
    public static void main(String[] args) 
    {
         
        System.out.println("Hello World!");
    }
}

创建线程:

/*
1.如何在自定义代码中,自定义一个线程??
  通过对api的查找,java已经提供了对线程这类事物的描述->Thread类
创建线程的一种方式:继承Thread类.
步骤:
 1.定义类继承Thread
 2.复写Thread类中的run();
    目的:将自定义代码存储在run方法中,让线程运行
 3.调用线程的start方法
    作用:①启动线程②JVM调用该线程的 run 方法。

发现每次运行结果均不相同:
 因为多个线程都在获取cpu的执行权.cpu执行到谁,谁就运行
  明确一点,在某一个时刻,只能有一个线程在运行.(多核除外)
cpu在做着快速的切换(实际上cpu在切换进程中的线程),宏观看上去是同时运行的效果

我们可以形象把多线程的运行行为在互相争夺cpu的执行权



这就是多线程的一个特性:随机型.谁抢到谁执行,至于执行多长,cpu说的算.

(画一个示意图)
*/
class Demo extends Thread
{
    public void run()
    {
      for(int i=0;i<10;++i)
      System.out.println("demo run----"+i);
    }
}

class ThreadDemo2 
{
    public static void main(String[] args) 
    {
         
    Demo d=new Demo();//创建一个线程,还没有执行
     d.start();
    //d.run();//仅仅是对象调用run方法.线程创建了,并没有运行.(只有主线程)
    for(int i=0;i<10;++i)
      System.out.println("Hello world----"+i);
    }
}
/*
以上主线程和new Demo()线程
即使主线程先执行完,还有new Demo()
java.exe(进程)还在,执行new Demo()线程
*/


/*
为什么要覆盖run方法呢??

    Thread类用于描述线程
    该类就定义了一个功能,用于存储线程要运行的代码.该存储功能就是run方法

    也就是说Thread类中的run方法,用于存储线程要运行的代码
    主线程要运行的代码存储在main方法中

*/

其中一种打印结果:

ThreadDemo2

关于以上两个线程的示意图:
线程

小练习:

/*
练习:
创建两个线程,和主线程交替执行.

线程都有自己默认的名称
    Thread-编号 该编号从0开始

currentThread:返回对当前正在执行的线程对象的引用
 static Thread currentThread() 
getName():获取线程名称
 String getName()

设置线程名称:setName()或者利用构造函数
void setName(String name)
均可查API文档
*/
class Test extends Thread
{
    //自定义线程名称
    Test(String name)
    {
      super(name);
    }
    public void run()
    {
        for(int x=0;x<60;x++)
         System.out.println(this.getName()+" test run..."+x);
        //在这里同样可以使用currentThread.getName(),通用方法
        
    }
}
class ThreadTest
{
 public static void main(String[] args)
 {
   Test t1=new Test("one");
   Test t2=new Test("two");
   t1.start();
   t2.start();
  for(int x=0;x<60;++x)
   System.out.println("main"+x);
 
 } 


}

ThreadTest

线程的生命周期:

线程生命周期3

/*
以下均摘自李刚老师的疯狂java(略有改动):

新建和就绪状态:
    当程序使用new关键字创建了一个线程之后,该线程就处于新建状态,此时它和其他
    java对象一样,仅仅由JVM为其分配了内存,并初始化了其成员变量的值.此时的线程对象
    没有表现出任何线程的动态特征,程序也不会执行线程执行体中的代码.

    当对象调用了start()方法之后,该线程
    处于就绪状态(万事具备,只欠CPU),JVM会为其创建方法调用栈和程序计数器,处于这个状态的
    线程并没有开始运行,它只是表示该线程可以运行了.至于该线程何时开始运行,取决于JVM里线程调度
    器的调度.

运行和阻塞状态:
    如果处于就绪状态的线程获得了CPU,开始执行run方法的线程执行体,则该线程处于运行状态.
    如果计算机只有一个CPU,在任何时刻只有一条线程处于运行状态
    但当线程数大于处理器数时,依然会有多条线程在同一个CPU上轮换的现象

    当一个线程开始运行后,它不可能一直处于运行状态(除非它的线程执行体足够短,瞬间就执行结束了)
    ,线程在运行过程中需要被中断,目的是使其他线程获得执行的机会,线程调度的细节取决于底层平台所采用
    的策略(windows下的JVM会调用windows底层内容)
*/

 

由售票例子引出线程第二种创建方式:

/*
需求:简单的卖票程序.
多个窗口买票.

创建线程的第二种方式:实现Runable接口

步骤:
1.定义类实现Runnable接口
2.覆盖Runnable接口中的run方法
   将线程要运行的代码存放在该run方法中

3.通过Thread类建立线程对象.
4.将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数
   
   为什么要将Runnable接口的子类对象传递给Thread的构造函数??
   
   因为,自定的run方法 所属的对象 是Runnable接口的子类 对象
   所以要让线程去执行 指定对象 的run方法.(就必须明确该run方法所属的对象.)

5.调用Thread类的start方法开启线程并调用Runnable接口子类对象 的run方法
*/

/*
class Ticket extends Thread
{
    //private static int ticket=100;//让四个对象共享100张票
    private int ticket=100;    //一般不定义static,生命周期过长                            
    public void run()
    {
       while(true)
         if(ticket>0)
           System.out.println(currentThread().getName()+"Sale :"+ticket--);
       
    }
}
*/
class Ticket implements Runnable
{
      private int ticket=100;//ticket此时被共享        
        public void run()
        { 
            while(true)         
            if(ticket>0)
             System.out.println(Thread.currentThread().getName()+" Sale :"+ticket--);
                
        }


}

class TicketDemo
{
  public static void main(String[] args)
  {
        //new Ticket().start();
        //new Ticket().start();
        //new Ticket().start();
        //new Ticket().start();
         //以上相当于每台卖了100张,不符合实际
         /*
         Ticket t=new Ticket();
         t.start();
         t.start();
         t.start();
         t.start();
         */
     //以上会有运行提示:无效线程状态异常
     //这是因为已经启动线程,并且从就绪状态到运行状态
     //又启动了线程
   
   
   Ticket t=new Ticket();
   //注意这个对象不是线程对象,和Thread没关系
 
/*
  new Thread().start();//执行的为Thread类中的run方法->方法体中无内容->什么也没执行
  */

new Thread(t).start(); new Thread(t).start(); new Thread(t).start(); new Thread(t).start(); //四个线程对象,同一个Runnable子类对象,自始至终改变的是该runnable子类对象中的ticket } } /* 实现方式和继承方式有什么区别??? 实现方式好处:避免了单继承的局限性 在定义线程时,建议使用实现方式. 两种方式区别: 继承Thread:线程代码存放Thread子类run方法中. 实现Runnable:线程代码存放在接口的子类的run方法. 例如: person<-student student不能再继承Thread 如果想让student中部分代码在线程中执行,可以实现Runnable,并复写run方法 */
/*
运行结果的打印顺序
是由于多核造成的,先出来的不一定被先打印
*/

 

Ticket1

posted @ 2013-03-16 13:53  伊秋  阅读(571)  评论(0编辑  收藏  举报