Java多线程学习(2)线程的常用方法和创建

上一节我们介绍了线程和进程的区别,这一小节我们来具体介绍线程的基础知识。

线程的基本方法

编号方法说明
1public void start()使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
2public void run()如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
3public final void setName(String name)改变线程名称,使之与参数 name 相同。
4public final void setPriority(int priority)更改线程的优先级。
5public final void setDaemon(boolean on)将该线程标记为守护线程或用户线程。
6public final void join(long millisec)等待该线程终止的时间最长为 millis 毫秒。
7public void interrupt()中断线程。
8public final boolean isAlive()测试线程是否处于活动状态。
9public static void yield()暂停当前正在执行的线程对象,并执行其他线程。
10public static void sleep(long millisec)在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
11public static Thread currentThread()返回对当前正在执行的线程对象的引用。

这里写图片描述

线程的创建

线程的实现可以通过两种方式:

继承thread类
class MyThread extends Thread{
    private static int num = 0;

    public MyThread(){
        num++;
    }

    @Override
    public void run() {
        System.out.println("主动创建的第"+num+"个线程");
    }
}

在这个基础上写一个main方法来测试一下这个线程。

public class Test {
    public static void main(String[] args)  {
        MyThread thread = new MyThread();
        thread.start();
    }
}
class MyThread extends Thread{
    private static int num = 0;
    public MyThread(){
        num++;
    }
    @Override
    public void run() {
        System.out.println("主动创建的第"+num+"个线程");
    }
}
//输出
主动创建的第1个线程

注意我们的线程类名叫MyThread,而此时的class类名叫Test,使用public修饰,因为我们知道Java中在一个类中允许有多个类,但只能有一个使用Public修饰,这是因为:

每编写一个Java文件就是Java编译器编译Java源代码的一个编译单元,具体怎么编译呢?这个就需要去了解Java编译器的工作原理了。编译器每编译一个.java文件(编译单元),对应着.java文件中的每个类都会有一个输出文件,而该输出文件的名称与.java文件中每个类的名称相同,只是多了一个后缀名.class。因此,在编译少量.java文件之后,会得到大量的.class文件。在.java文件中,不是必须含有public类的。public类只是用来表示编译单元中存在公开接口。

但如果含有public类,最多只能有一个,必须名字和文件名一样。因为编译的时候Java编译器会判断如果存在public类,该类当作这个编译单元的对外接口,类加载器需要把该类加载。对于一个public类,它是可以被项目中任何一个类所引用的,只需在使用它前import一下它所对应的class文件即可,将类名与文件名一一对应就可以方便虚拟机在相应的路径(包名)中找到相应的类的信息。如果不这么做的话,就很难去找,而且开销也会很大。运行的时候则是调用main()函数运行的。

好,回到我们的线程类当中,启动线程是调用线程的start()方法,此时线程启动会执行对应线程在run()方法体中的代码。

注意,不是调用run()方法启动线程,run()方法中只是定义需要执行的任务,如果调用run()方法,即相当于在主线程中执行run()方法,跟普通的方法调用没有任何区别,此时并不会创建一个新的线程来执行定义的任务。

为了分清start()方法调用和run()方法调用的区别,请看下面一个例子:

public class Test {
    public static void main(String[] args)  {
        System.out.println("主线程ID:"+Thread.currentThread().getId());
        MyThread thread1 = new MyThread("thread1");
        thread1.start();
        MyThread thread2 = new MyThread("thread2");
        thread2.run();
    }
}

class MyThread extends Thread{
    private String name;

    public MyThread(String name){
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println("name:"+name+" 子线程ID:"+Thread.currentThread().getId());
    }
}
//输出结果
主线程ID:1
name:thread2 子线程ID:1
name:thread1 子线程ID:11

从输出结果可以得出以下结论:

1)thread1和thread2的线程ID不同,thread2和主线程ID相同,说明通过run()方法调用并不会创建新的线程,而是在主线程中直接运行run()方法,跟普通的方法调用没有任何区别;

2)虽然thread1的start()方法调用在thread2的run()方法前面调用,但是先输出的是thread2的run()方法调用的相关信息,说明新线程创建的过程不会阻塞主线程的后续执行。

实现Runnable接口
public class TestThread2 {
    public static void main(String[] args)  {
        System.out.println("主线程ID:"+Thread.currentThread().getId());
        MyRunnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start();
    }
}
class MyRunnable implements Runnable{
    public MyRunnable() {
    }

    @Override
    public void run() {
        System.out.println("子线程ID:"+Thread.currentThread().getId());
    }
}
//结果
主线程ID:1
子线程ID:11
Thread和Runnable的区别

继承Thread 类和实现Runnable 接口都可以实现多线程,但是有经验的程序员都会选择实现Runnable接口 ,其主要原因有以下两点:

首先,java只能单继承,因此如果是采用继承Thread的方法,那么在以后进行代码重构的时候可能会遇到问题,因为你无法继承别的类了。

其次,如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。

例如下面的例子

package testThread;

class runnable implements Runnable{

    private int ticket = 10 ;

    public void run() {
        for (int i = 0; i < 10 && ticket >0; i++) {
            System.out.println("Runnable ticket count : " + ticket--);
        }

    }
}

class thread extends Thread{
    private int ticket = 10 ;

    @Override
    public void run() {
        for (int i = 0; i < 10 && ticket >0; i++) {
            System.out.println("Thread ticket count : " + ticket--);
        }
    }
}

public class ShareDataDemo {
    public static void main(String[] args) {
        runnable runnable1 = new runnable();
        new Thread(runnable1).start();
        new Thread(runnable1).start();
        new Thread(runnable1).start();

        new thread().start();
        new thread().start();
        new thread().start();

    }
}
//其中一次的结果
Runnable ticket count : 9
Runnable ticket count : 10
Runnable ticket count : 8
Runnable ticket count : 7
Runnable ticket count : 6
Runnable ticket count : 5
Runnable ticket count : 4
Runnable ticket count : 3
Runnable ticket count : 2
Runnable ticket count : 1
Thread ticket count : 10
Thread ticket count : 9
Thread ticket count : 8
Thread ticket count : 7
Thread ticket count : 6
Thread ticket count : 5
Thread ticket count : 4
Thread ticket count : 3
Thread ticket count : 2
Thread ticket count : 1
Thread ticket count : 10
Thread ticket count : 9
Thread ticket count : 8
Thread ticket count : 7
Thread ticket count : 6
Thread ticket count : 5
Thread ticket count : 4
Thread ticket count : 3
Thread ticket count : 2
Thread ticket count : 1
Thread ticket count : 10
Thread ticket count : 9
Thread ticket count : 8
Thread ticket count : 7
Thread ticket count : 6
Thread ticket count : 5
Thread ticket count : 4
Thread ticket count : 3
Thread ticket count : 2
Thread ticket count : 1

从上面的例子中不难看出,实现Runnable接口创建的多个线程中,他们的ticket数据是共享的,所以该循环只执行了10次即结束了。而通过继承Thread类创建的线程,他们所拥有的ticket数据是不共享的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值