java多线程

 

重点:多线程的概念

     线程创建的两种方式

     线程的生命周期及状态转换

     线程的调度

     线程的安全和同步

     多线程通信

  • 多线程的概念

指一个应用程序中有多条并发执行的线索,每条线索都被称作一个线程,他们会交替进行,彼此间可以进行通信。

  • 线程创建的两种方式
    1. 继承java.lang包下的Thread类,覆盖Thread类的    run()方法,在run()方法中实现运行在线程上的代码
  • class test  
    {
    	public static void main (String[] args) 
    	{
    	   myThread myThread=new myThread();//创建线程myThread的线程对象
    	   myThread.start();//开启线程
    	   while(true){//通过死循环打印输出
    	       System.out.println("main方法在执行");
    	   }
    	}
    }
    class myThread extends Thread{
        public void run(){
            while(true){//通过死循环打印输出
                 System.out.println("myThread方法在执行");
            }
        }
    }
    

    局限性:Java只支持单继承,一个类一旦继承了某个父类就无法再继承Thread类,为了克服这种弊端,Thread类提供另一个构造方法Thread(Runnable target),其中Runnable是一个接口,只有一个 run()方法,当Thread(Runnable target)构造方法创建对象时,只需为该方法传递一个实现Runnable接口的实例对象,这样创建的线程将调用实现了Runnable接口中的run()方法作为运行代码,不需要调用Thread类的run()方法

  • 2.实现java.lang.Runnable接口,在run()方法中实现运行在线程上的代码

 

class test  
{
	public static void main (String[] args) 
	{
	   myThread myThread=new myThread();//创建线程myThread的线程对象
	   Thread thread=new Thread(myThread);//创建线程对象
	   thread.start();//开启线程,执行线程中的run方法
	   while(true){//通过死循环打印输出
	       System.out.println("main方法在执行");
	   }
	}
}
class myThread implements Runnable{
    public void run(){
        while(true){//通过死循环打印输出
             System.out.println("myThread方法在执行");
        }
    }
}

 

 

单线程程序:;

class test 

{

  public static void main (String[] args)

  {

      myThread myThread =new myThread();//创建myThread实例对象

      myThread.run();//调用myThread类的run方法

      while(true){

          System.out.println("main方法在运行");

      }

  }

}

class myThread{

    public void run(){

        while(true){

           System.out.println("myThread类的run方法在运行");

        }

    }

}

两种线程在实际中的应用案例:

假设售票厅有四个窗口可发售某日某次列车的100张车票。

使用继承Thread实现多线程,代码如下:

class test  
{
	public static void main (String[] args) 
	{
	  new TicketWindows().start();//创建一个TicketWindows并开启
	  new TicketWindows().start();//创建一个TicketWindows并开启
	  new TicketWindows().start();//创建一个TicketWindows并开启
	  new TicketWindows().start();//创建一个TicketWindows并开启
	}
}
class TicketWindows extends Thread{
    private int tickets=100;
    public void run(){
        while(true){
            if(tickets>0){
                    Thread th=Thread.currentThread();//获取当前线程
                    String th_name=th.getName();//获取当前线程的名字
                    System.out.println(th_name+"正在发售第"+tickets--+"张票");
             }
        }
    }
}

使用构造方法Thread(Runnable target,String name)创建线程对象的同时指定线程名字

class test  
{
	public static void main (String[] args) 
	{
	    TicketWindows tw=new TicketWindows();//创建一个TicketWindows实例对象tw
	     new Thread(tw,"窗口1");//创建线程对象并命名为窗口1,开启线程
	     new Thread(tw,"窗口2");//创建线程对象并命名为窗口2,开启线程
	     new Thread(tw,"窗口3");//创建线程对象并命名为窗口3,开启线程
	     new Thread(tw,"窗口4");//创建线程对象并命名为窗口4,开启线程
	 
	}
}
class TicketWindows implements Runnable{
    private int tickets=100;
    public void run(){
        while(true){
            if(tickets>0){
                    Thread th=Thread.currentThread();//获取当前线程
                    String th_name=th.getName();//获取当前线程的名字
                    System.out.println(th_name+"正在发售第"+tickets--+"张票");
             }
        }
    }
}

Runnable接口相对于继承Thread类来说,有如下显著好处:

1.适合多个相同程序代码的线程去处理同一个资源的情况,把线程同程序代码、数据有效的分离,很好的体现了面向对象的设计思想

2.可以避免由于Java单继承带来的局限性。

事实上,大部分应用程序都会采用第二种方式来创建多线程,使用Runnable接口

后台线程

class test  
{
    class DamonThread implements Runnable{//创建DamonThread类,实现runnable接口
    public void run(){//实现接口中的run方法
        while(true){
            System.out.println(Thread.currentThread().getName()+"---is running");
        }
    }
	public static void main (String[] args) 
	{
	    System.out.println(Thread.currentThread().isDamon());
	    DamonThread dt=new DamonThread();//创建一个DamonThread对象dt
	    Thread t=new Thread(dt,"后台线程");//创建线程t共享dt资源
	    System.out.println(t.isDamon());//判断是否为后台线程
	    t.setDaemon(true);//将线程t设置为后台线程
	    t.start();//开启线程
	    for(int i=0;i<10;ix++){
	        System.out.println(i);
	    }
	}
}
}

 

三、线程的生命周期及状态转换

线程整个生命周期分为五个阶段:

新建状态New

   创建一个线程对象后,该线程对象就处于新建状态,此时他不能运行,和其他Java对象一样,仅仅由Java虚拟机为其分配内存,没有表现出任何线程的动态特征

就绪状态Runnable

   当线程对象调用了start()方法后,该线程就进入就绪状态,处于就绪状态的线程位于可运行池中,此时他只是具备了运行的条件,能否获得CPU的试用权开始运行,还需要等待系统的调度

运行状态Running

   如果处于就绪状态的线程获得去了CPU的使用权,开始执行run()方法中的线程执行体,则该线程处于运行状态,当一个线程启动后,他不可能一直处于运行状态(除非他的线程执行体足够短,瞬间就结束了)当使用完系统分配的时间后,系统就会剥夺该线程占用的CPU资源,让其他线程获得执行的机会。

   注意:只有处于就绪状态的线程才可能转成运行状态

阻塞状态Blocked

   列举线程由运行状态转换成阻塞状态的原因,以及如何从阻塞状态转成就绪状态:

  1. 当线程师徒获取某个对象的同步锁时,如果该锁被其他线程所持有,则当前线程会进入阻塞状态,如果想从阻塞状态进去就绪状态必须得获取到其他线程所持有的锁
  2. 当线程调用了一个阻塞式的IO方法时,该线程就会进入阻塞状态,如果新进入就绪状态就必须要等到这个阻塞的IO方法返回
  3. 当线程调用了某个对象的wait()方法时,也会使线程进入阻塞状态,如果想进入就绪状态就需要使用notify()方法唤醒该线程
  4. 当线程调用了Thread的sleep(long millis)方法,也会使该线程进入阻塞状态,在这种情况下,只需等到线程睡眠的时间到了以后,线程就会自动进入就绪状态
  5. 当在一个线程中调用了另一个线程的join()方法时,会使当前线程进入阻塞状态,在这种情况下,需要等到新加入的线程运行结束后才会结束阻塞状态,进入就绪状态

注意:线程从阻塞状态只能进入就绪状态,而不能直接进入运行状态,也就是说结束阻塞的线程需要重新进入可运行池中,等到系统的调度

死亡状态Terminated

     线程的run()方法正常执行完毕或者线程抛出一个未捕获的一场,错误,线程就进入死亡状态,一旦进入死亡状态,线程就不在拥有运行的资格,也不能在转换到其他状态

 

四、线程的调度

线程调用有两种模型:

1.分时调度模型:让所有的线程轮流获得CPU的使用权,并且平均分配每个线程占用CPU的时间片

2.抢占式调度模型:让可运行池中优先级高的线程优先占用CPU,而对于优先级相同的线程,随机选择一个线程使其占用CPU,当他失去CPU的使用权后,咋随机选择其他线程获取CPU的使用权

 

Java虚拟机默认采用抢占式调度模型,大多数情况下,程序员不需要关心它,但在某些特定的需求下需要改变这种模式,由程序逐级来控制CPU调度

 

线程的优先级用1-10直接的证书来表示,数字越大优先级越高。

还可以使用Thread类中提供的三个静态常量表示线程的优先级

 

Static int MAX_PRIORITY   线程最高优先级,相当于值10

Static int MIN_PRIORITY线程最低优先级,相当于值1

Static int NORM_PRIORITY线程普通优先级,相当于值5

 

程序在运行期间,处于就绪状态的每个线程都有自己的优先级,然而线程优先级不是固定不变的,可以通过Thread类的setPriority(int newPriority)方法对其进行设置。

五、线程的安全和同步

六、多线程通信

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值