关于线程(一)线程的基础

来源:互联网 发布:windows store app 编辑:程序博客网 时间:2024/05/17 22:56

今天闲来无事,整理了一下Java的线程,如有不足还望各位看官补充指导!

一、为什么需要多线程

       多线程编程可以使程序具有有两条或两条以上的并发执行线索,就像日常工作中由多人同时合作完成一个任务。这在很多情况下可以改善程序的响应性能,提高资源的利用效率,在多核CPU年代,这显得尤为重要。然而滥用多线程也有可能给程序带来意想不到的错误,降低程序执行的效率。

       例如,开发一个网上购物时自动邮件通知的程序,在用户点击提交按钮确认定单时,一方面要显示信息提示用户定单已确认,一方面应该自动给用户发送一份电子邮件。

       如果是单线程模式,则需要等待邮件发送完成之后,再显示提示信息,由于邮件发送的过程相对较慢,用户可能要经过漫长的等待才看到确认信息,界面响应性能不是很好。

       而显示定单确认信息与发送通知邮件这两个工作是没有相互约束关系的,完全可以一边显示提示信息,一边发送通知邮件,这样界面的响应性能会提高很多,用户不用进行漫长的等待。

      然而,不能并发执行的任务是不应该使用多线程来实现的,否则有可能会得到错误的结果。例如进行某些计算的程序,后面的计算依赖与前面的结果,这就不能把这些具有前后依赖关系的计算任务并发处理。

二、线程,你来自何方

       Java中的线程有两方面的含义,一是一条独立的执行线索,二是java.lang.Thread类或其子类的对象。在Java中开发自己的线程有两中方式,包括继承Thread类与实现Runnable接口,这两种方式在不同的场合各有优缺点,要知区别何在,且听我细细道来。

       1、继承Thread类

      若一个类直接或间接继承自Thread类,则该类对象便具有了线程的能力。这是最简单的开发自己线程的方式,采用此方式最重要的是重写继承的run方法。其实,run方法中的代码就是线程所要执行任务的描述。

      

class MyThread extends Thread{//重写run方法public void run(){System.out.println("这里是一个新的线程开始的地方");}}


      2、实现Runnable接口

      由与Java中采用的是单亲继承,一个类只能唯一的继承另一个类。如果只能通过继承Thread类来定义自己的线程,在开发中有很多限制。因此,Java中提供了一个名称为Runnable(java.lang.Runnable)的接口,此接口中有一个如下签名的抽象方法:

public abstract void run();


      这样,实现了Runnable接口的类中同样也就具有了描述线程任务的run方法,此run方法也可以在一定的条件下成为一条独立的执行线索。


//实现了Runnable接口class MyRunnable implements Runnable{        //重写run方法public void run(){System.out.println("这里是一个新的线程开始的地方");}}

       3、两种方式的比较

      无论使用哪种方式,都可以通过一定的操作得到一条独立的执行线索,然而二者之间不是完全相同的。

      继承Thread类的方式虽然最简单(在后面的具体案例中会体会到),但继承了该类就不能继承别的类,这在有些情况下严重影响开发。其实,很多情况下只是希望自己的类具有线程的能力,扮演线程的角色,而自己的类还需要继承其他类。

      实现Runnable接口既不影响继承其他类,也不影响实现其他接口,只是实现Runnable接口的类多了扮演了一种角色,多了一种能力而已,灵活性更好。

      4.创建线程对象

      对于继承Thread的类来说,在创建线程对象时非常简单。其继承了Thread类,因此其自身的对象便就是线程对象,在创建线程对象时只需创建自身的对象即可。

      对于实现Runnable接口的类,其自身的对象并不是一个线程,只是在该类中通过实现的run方法指出了线程需要完成的任务。然而,若想得到一个线程,必须创建Thread类或其子类的对象,这时就需要使用Thread类的特定构造器来完成这个工作。

      从Thread类的API列表中可以看出,当创建线程对象时,只需首先创建实现Runnable接口的类的对象,然后将此对象的引用传递给Thread类构造器即可,这种方式实际上是告诉线程对象要执行的任务(run方法)在哪里。

       若要启动一个线程调用的是对象的start()方法哦,当然run()方法也可以作为一个普通方法调用,只是调用后很遗憾它并不代表了新建了执行线程,它仅仅在调用的线程中执行哦。

      特别注意的是,线程在其生命周期中只能被启动一次,如果线程被启动后再次调用其start方法进行启动则会抛出IllegalThreadStateException异常。

      下面贴两段代码仅供参考,因为第二段会报错得意

//定义实现Runnable接口的类class MyRunnable implements Runnable{//实现run方法,指定线程执行的任务public void run(){System.out.println("恭喜你,线程被启动了,执行了run方法中的代码!!!");}}//主类public class Sample{public static void main(String[] args){//创建实现Runnable接口的类的对象MyRunnable mr=new MyRunnable();//创建Thread对象,将第一步创建对象的引用作为构造器参数//传递,指定线程要执行的任务Thread t=new Thread(mr);//启动线程t.start();}}

public static void main(String[] args){//创建Runnable实现类的对象MyRunnable mr=new MyRunnable();//创建Thread对象,将第一步创建对象的引用作为构造器参数//传递,指定线程要执行的任务Thread t=new Thread(mr);//启动线程t.start();//再次启动线程t.start();}

       5.初尝多线程

       两条执行线索并发运行时,结果可能是不惟一的

      这是因为,如果有多个没有同步约束关系的线程需要执行,调度线程将不能保证哪个线程先执行及其持续的时间,在不同的平台上,或在同一平台上多次运行可能会得到不同的结果。

      Java中对于线程启动后唯一能够保障的就是:“每个线程都将启动,每个线程都会执行结束”。但谁会先执行谁会后执行,将没有保障,也就是说,就算一个线程在另一个线程之前启动,也无法保障该线程一定在另一个线程之前执行完毕。线程之间具体的执行顺序受到硬件、操作系统等因素的影响,而Java又是一个跨平台的开发语言,因此不可能对没有约束关系的线程执行顺序提供保障。看段代码运行一下就了然了。

//定义Runnable实现接口的类class MyRunnable1 implements Runnable{//重写run方法,指定该线程执行的代码public void run(){for(int i=0;i<50;i++){//打印线程运行过程的情况System.out.print("["+i+"] ");}}}//定义另外一个实现Runnable接口的类class MyRunnable2 implements Runnable{//重写run方法,指定该线程执行的代码public void run(){for(int i=0;i<50;i++){//打印线程运行过程的情况System.out.print("<"+i+"> ");}}}//主类public class Sample16_2{public static void main(String[] args){//创建实现Runnable接口的类的对象MyRunnable1 mr1=new MyRunnable1();MyRunnable2 mr2=new MyRunnable2();//创建线程Thread对象,并指定targetThread t1=new Thread(mr1);Thread t2=new Thread(mr2);//启动线程t1.start();t2.start();}}

      上述代码中定义了两个实现Runnable接口的类,两个类中的run方法均为打印0到49之间的数字。对于MyRunnable1类,其用方括号将数字括起来,而MyRunnable2使用尖括号将数字括起来,这样可以区分是哪个线程打印的数字。

      主方法中,首先分别创建了两个实现Runnable接口的类的对象,同时创建了两个Thread对象。两个Thread对象的target分别为前面创建的两个实现Runnable接口的类的对象,最后分别启动了两个线程。

      基本的就先墨迹这么多吧...

0 0
原创粉丝点击