JAVA中线程的三种创建方法

本文详细解析了Java中线程、进程与程序的区别,并介绍了三种创建线程的方法:继承Thread类、实现Runnable接口和实现Callable接口,附带代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

程序、进程、线程的区别是什么,在Java中又该如何创建线程。

程序,进程,线程的区别

  1. 运行在操作系统上的软件我们一般称之为程序,其实程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。

  2. 进程则是执行程序的一次执行过程,它是一个动态的概念。是系统资源分配的单位。

    在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能认为的干预的。

  3. 线程是CPU调度和执行的的单位。可以理解为在一个进程中包含有多个线程,是进程的一个执行单位。线程的执行是根据CPU的调度来的,而CPU是随机进行调用线程的
    注意:很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器。如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,因为切换的很快,所以就有同时执行的错局。

线程的创建

线程就是独立的执行路径,在Java程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程(main()方法),gc线程(垃圾回收器);

1. 继承Thread类

  1. 自定义一个线程类来继承Tread类
  2. 重写run()方法,并编写线程执行体
  3. 创建线程对象,调用start()方法启动线程

程序例子:

public class ThreadTest{
	public static void main(String[] args){
		//创建线程对象
	    MyStread myStread=new MyStread();
	    myStread.start();
	}
}
class MyStread extends Thread{
    //线程入口点
    @Override
    public void run() {
        //线程执行体
        for (int i = 0; i < 5; i++) {
            System.out.println("这是一个Thread线程...");
        }
    }
}

运行出来的结果就是打印了五遍的"这是一个Thread线程…"。
接下来看一下使用Thread方式创建多个线程…

public class ThreadTest{
	public static void main(String[] args){
		//创建线程对象
	    MyStread myStread1=new MyStread();
	    MyStread myStread2=new MyStread();
	    MyStread myStread3=new MyStread();
	    myStread1.start();
	    myStread2.start();
	    myStread3.start();
	}
}
class MyStread extends Thread{
    //线程入口点
    @Override
    public void run() {
        //线程执行体
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+"这是一个Thread线程...");
            //Thread.currentThread().getName()意思是:得到当前这个线程的对象名称
        }
    }
}
Thread-0这是一个线程...
Thread-1这是一个线程...
Thread-0这是一个线程...
Thread-1这是一个线程...
Thread-0这是一个线程...
Thread-1这是一个线程...
Thread-0这是一个线程...
Thread-1这是一个线程...
Thread-0这是一个线程...
Thread-1这是一个线程...
Thread-2这是一个线程...
Thread-2这是一个线程...
Thread-2这是一个线程...
Thread-2这是一个线程...
Thread-2这是一个线程...

运行结果:可以看到三个线程相互抢占资源执行,这也是CPU调度的结果…
学过java基础的我们不难发现,这是继承了Thread类,而Java的特点是单继承和多实现,所以这种方法其实存在着改进的可能性。
前面说到线程的调用是根据CPU随机调用来进行运行的,其实可以通过setPriority()方法来进行相应的优先变化,优先级分为1-10,1优先级最低,10优先级最高,而默认创建的线程优先级为5。

2. 实现Runnable接口

既然上面提到了单继承和多实现,线程可以进一步的改进,所以Runnable接口出现了。

  1. 定义MyRunnable类实现Runnable接口
  2. 实现run()方法,编写线程执行体
  3. 创建线程对象,调用start()方法启动线程

看上去和继承Thread类的方式大同小异,但从代码来看,还是会有一些不同的。

public class RunnableTest{
	public static void main(String[] args) {
	        //创建线程对象
	        MyRunnanble myRunnanble=new MyRunnanble();
	        new Thread(myRunnanble).start();
	    }
}
class MyRunnanble implements Runnable{
    //线程入口点
    @Override
    public void run() {
        //线程执行体
        for (int i = 0; i < 5; i++) {
            System.out.println("这是一个Runnable线程...");
        }
    }
}

**注意不同:**在创建了Runnable接口实现类的对象后,需要将对象放入Thread中进行实现,这其实是在Thread类中有实现Runnable这个接口,因此才可以将Runnable接口实现对象放入Thread中调用start()来启动线程。
运行出来的结果就是打印了五遍的"这是一个Runnable线程…"。
例2:

public class ThreadTest{
	public static void main(String[] args) {
	        //创建线程对象
	        MyRunnanble myRunnanble=new MyRunnanble();
	        new Thread(myRunnanble1,"小明").start();
	        new Thread(myRunnanble1,"小红").start();
	        new Thread(myRunnanble1,"小华").start();
	    }
}
class MyRunnanble implements Runnable{
    //线程入口点
    @Override
    public void run() {
        //线程执行体
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+"这是一个Runnable线程...");
            //Thread.currentThread().getName()意思是:得到当前这个线程的对象名称
        }
    }
}

可以看到Runnable接口的同一个实现对象被多个线程使用,运行就会出现三个线程相互抢占资源的情况
现给出一种运行的情况我们来分析一下

小明这是一个Runnable线程...
小明这是一个Runnable线程...
小明这是一个Runnable线程...
小红这是一个Runnable线程...
小明这是一个Runnable线程...
小红这是一个Runnable线程...
小明这是一个Runnable线程...
小红这是一个Runnable线程...
小红这是一个Runnable线程...
小红这是一个Runnable线程...
小华这是一个Runnable线程...
小华这是一个Runnable线程...
小华这是一个Runnable线程...
小华这是一个Runnable线程...
小华这是一个Runnable线程...

可以看到,这次的情况是**小明这个线程先启动了,但是运行for循环三次后,小红线程得到了调度,因此小红线程运行,而运行一次后,小明线程又得到了调度,一次后又是小红…小华最后才得到cpu的调度。**下一次运行可能会变成另外的情况。
由此也证明了我们前面的结论.线程的调度是由cpu随机调用的,当然可以做到先让小红线程走完,再让小明走,再让小华走,这我们之后会给出解决的办法。
这就是普通常用的两种线程的创建方式,因为Java单继承的局限性,推荐使用实现Runnable接口的方式来创建线程。
下一篇博客有更为详细的讲到Runnable接口是如何实现的以及如何更为简化代码的写法,链接为:JAVA中静态代理和Lamda表达式

3.实现Callable接口

Callable接口是新出现一种实现方式,其使用稍微有点复杂,但是也相比于Runnable接口的方法有了进步。
实现方式如下:

  1. 实现Callable接口,需要返回值类型
  2. 重写call方法,需要抛出异常
  3. 创建目标对象
  4. 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
  5. 提交执行:Future result1 = ser.submit(Callable接口实现对象);
  6. 获取结果:boolean r1 = result1.get()
  7. 关闭服务:ser.shutdownNow();

现在给出一种返回值为Boolean类型的例子:

public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建MyCallable对象
        MyCallable myCallable=new MyCallable();
        //创建执行服务:
        ExecutorService ser = Executors.newFixedThreadPool(1);
        	//这里使用到了线程池,之后会说到
        //提交执行:
        Future<Boolean> result=ser.submit(myCallable);
        //获取结果
        boolean flag = result.get();
        //输出
        System.out.println(flag);
        //关闭服务
        ser.shutdown();
    }

}
class MyCallable implements Callable{
    @Override
    public Boolean call() throws Exception {
        return true;
    }
}

解释:这个例子,只是简单的了解Callable接口的使用,这里是创建了一个线程,且,在重写call()中直接返回了一个true,在执行体中没有过多的加入复杂代码…
可以看到,Callable接口的方式需要开启服务,并且用到了线程池,之后还要关闭服务,相比于之前提到的两种方式略显繁琐,但Callable接口方式会返回一个对象,而且call()方法会抛出异常,这使得相比于前两种方式有所进步和改善。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值