欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java多线程-多线程的创建、基本操作

程序员文章站 2022-05-05 18:00:25
...

  如果一次只完成一件事情,会很容易实现,但现实生活中很多事情都是同时进行的,所以在Java中为了模拟这种状态,引入了线程机制。简单的说,当程序同时完成多件事情时,就是所谓的多线程程序。
  多线程在Windows中的运行方式:Windows操作系统是多任务操作系统,以进程为单位。一个进程是一个包含有自身地址的程序,每个独立执行的程序都称为进程,也就是正在执行的程序。系统可以分配给每个进程一段有限使用CPU的时间(也可以称为CPU时间片),CPU在这段时间中执行某个进程,然后下一个时间片又跳至另一个进程中执行。由于CPU转换较快,所以使得每个进程好像是同时执行一样。

1.多线程的两种实现方式

  Java中主要提供两种方式实现线程,分别为继承java.lang.Thread类与直线java.lang.Runable接口。

1.1继承Thread类

  位置:java.lang
  构造方法:
    public Thread():创建一个新的线程对象。
    public Thread(String threadName):创建一个名称为threadName的线程对象。

//继承Thread类
public class ThreadTest extends Thread{
    //重写Thread类中的run()方法
    public void run(){
        //实现该线程功能的代码
    }
}

public static void main(String[] aegs){
    //启动线程
    new ThreadTest().start();
}

  当一个类继承Thread类后,就可以在该类中覆盖run()方法,将实现该线程功能的代码写入run()方法中,然后同时调用Thread类中的start()方法执行线程,也就是调用run()方法。
  当执行一个线程程序时,就会自动产生一个线程,主方法正式在这个线程上运行的。当不再启动其他线程时,该程序就为单线程程序。主方法线程启动由JVM负责,程序员负责启动自己的线程。

1.2实现Runnable接口

  如果程序员需要继承其他类,而当前类还需要实现多线程的功能,此时就可以使用Runnable接口来实现。实质想Thread类也是实现了Runnable接口,其中的run()方法正式对Runnable接口中的run()方法的具体实现。
  实现Runnable接口的程序会创建一个Thread对象,并将Runnable对象与Thread对象相关联。Thread类中由以下两个构造方法:
  public Thread(Runnable target);
  public Thread(Runnable target,String name);
  这两个构造方法的参数中都存在Runnable实例,使用以上构造方法就可以将Runnable实例与Thread实例相关联。
  使用Runnable接口启动新的线程步骤图解如下:


Java多线程-多线程的创建、基本操作
接口方式实现多线程

2.线程的生命周期

  线程具有生命周期,其中包含7中状态,分别为出生、就绪、运行、等待、休眠、阻塞和死亡。如图:


Java多线程-多线程的创建、基本操作
线程的声明周期状态图

3.线程的操作方法

  操作线程有很多方法,这些方法可以使线程从某一种状态过渡到另一种状态。

3.1线程的休眠

  一种能控制线程行为的方法时调用sleep()方法,sleep()方法需要一个参数用于指定该线程休眠的时间,该时间以毫秒为单位。

try{
    Thread.sleep(2000);//使线程休眠2秒
} catch(InterruptedException e){
    e.printStackTrace();
}

  使用了sleep()方法的线程在一段时间内会醒来,但是并不能保证它醒来后进入运行状态,只能保证它进入就绪状态。

3.2线程的加入

  如果当前某程序为多线程程序,假如存在一个线程A,现在需要插入线程B,并要求线程B先执行完毕,然后再继续执行线程A,此时可以使用Thread类中的join()方法来完成。
  当某个简称使用join()方法加入到另外一个线程时,另一个线程会等待该线程执行完毕后再继续执行。
  一个简单的join()方法的应用实例,将一个并发执行的程序修改为顺序执行:

public static void main(String[] args){
    System.out.println("main start");
    //创建两个多线程t1,t2
    Thread t1 = new Thread(new Worker("thread-1"));
    Thread t2 = new Thread(new Worker("thread-2"));
    t1.start();
    t1.join();//等待t1结束,这时候t2线程并未启动

    t2.start();//t1结束后,启动t2线程
    t2.join();//等待t2结束

    System.out.println("main end");
}

//实现多线程的Worker类
class Worker implements Runnable{
    private String name;
    public Worker(String name){
        this.name = name;
    }
    @Override
    public void run(){
        for (int i = 0; i < 10; i++){
            try{
                Thread.sleep(100);//线程休眠0.1秒
            }
            catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(name);
        }
    }

}
3.3线程的中断

  早期的Java多线程程序使用stop()方法来停止线程,但当前版本的JDK已经废除了这种不安全的方法,并提倡在run()方法中使用无限循环的形式,然后使用一个布尔型标记控制循环的停止。

public class InterruptedTest implements Runnable{
    private boolean isContinue = false;//设置一个标记变量,默认值为false
    public void run(){
        while(true){
            //当前线程代码
            if(isContinue){
                break;//当isContinue变量为true时,停止线程
            }
        }
    }
    public void setContinue(){//定义设置isContinue变量为true的方法
        this.isContinue = true;
    }
}

  如果线程时因为使用了sleep()或wait()方法进入了就绪状态,可以使用Thread类中的interrupt方法使线程离开run()方法,同时结束线程,但程序会抛出InterruptedException异常,用户可以在处理该异常时完成线程的中断业务处理,如终止while循环。在项目中,此方法常用于执行关闭数据库连接和关闭Socket连接等操作。

3.4线程的礼让

  Thread类中提供了一种礼让方法,使用yield()方法表示,它只是给当前正处于运行状态的线程一个提醒,告知它可以将资源礼让给其他线程,但这仅仅是一种暗示,没有任何一种机制保证当前线程会将资源礼让。
  yield()方法使具有同样优先级的线程有进入可执行状态的机会,当当前线程放弃执行权是会再度会到就绪状态。对于支持多任务的操作系统来说,不需要调用yield()方法,因为操作系统会为线程自动分配CPU时间片来执行。

4.线程的优先级

  每个线程都具有各自的优先级,线程的优先级可以表明在程序中该线程的重要性,如果有很多线程处于就绪状态,系统会根据优先级来决定首先使哪个线程进入运行状态。但这并不意味着低优先级的线程得不到运行,而只是它运行的几率比较小,如辣鸡回收线程的优先级就较低。
  Thread类中包含的成员变量代表了线程的某些优先级。
  Thread.MIN_PRIORITY(常数1);
  Thread.MAX_PRIORITY(常数10);
  Thread.NORM_PRIORITY(常数5);
  其中每个线程的优先级都在Thread.MIN_PRIORITY–Thread.MAX_PRIORITY之间。每个新产生的线程都继承了父线程的优先级。
  在多任务操作系统中,每个线程都会得到一小段CPU时间片运行,在时间结束是,将轮换另一个线程进入运行状态,这是系统会选择与当前线程优先级相同的线程予以运行。系统始终选择就绪状态下优先级较高的线程进入运行状态。