JAVA学习之路(5):多线程01

Thread类实现多线程

一、继承Thread类实现多继承

需要覆写Thread类中提供的一个run()方法,这个方法就属于线程的主方法

class MyThread extends Thread { // 线程的主体类
    private String title;
    public MyThread(String title) {
        this.title = title;
    }
    @Override
    public void run() {
        for (int x = 0; x < 10 ; x ++) {
            System.out.println(this.title + "运行,x = " + x);
        }
    }
}

使用该类中的方法,需要实例化MyThread对象,而run()方法不能够直接被调用,因为这里牵扯到操作系统的资源调度问题,所以,想要启动多线程,需要使用start()方法完成

public class Demo1 {
    public static void main(String[] args) {
        new MyThread("线程A").start();
        new MyThread("线程B").start();
        new MyThread("线程C").start();
    }
}

虽然调用了线程的start()方法,但是最终执行的是覆写的run()方法,并且线程对象是交替执行的

每个线程对象只允许启动一次,如果重复启动,就会抛出异常illegalThreadStateException

Runnable接口实现多线程

通过Thread类可以实现多线程的定义,但是单继承是具有局限性的,所以,在java中提供了第二种多线程的主体定义结构,实现java.lang.Runnable接口

@FunctionalInterface
public interface Runnable {
    public void run();
}

由于上文MyThread不在继承Thread父类,那么将不再支持start()这个继承方法,所以线程将无法启动,查看api文档发现,Thread类所提供的构造方法中,提供了public Thread(Runnable target)方法

public static void main(String[] args) {
    Thread threadA = new Thread(new MyThread("线程对象A"));
    threadA.start();
}

Runnable接口使用了函数式接口定义,所以也可以使用lambda表达式进行线程类实现

public class ThreadDemo {
    public static void main(String[] args) {
        for (x = 0; x < 3; x ++) {
            String title = "线程对象-" + x;
            new Thread(() -> {
                for (int y = 0; y < 10; y ++) {
                    System.out.println(title + "运行,y = " + y);
                }
            }).start();
        }
    }
}

所以,在实际开发中,多线程的实现优先考虑Runnable接口,并通过Thread类对象启动多线程

Thread与Runnable关系

Thread类继承了Runnable接口,之前继承Thread类,实际是覆写了Runnable接口中的run()方法

在多线程的设计中,使用了代理设计模式,用户自定义的MyThread类,是负责核心业务功能的实现,而真正与系统进行资源的抢占是通过Thread类来代理实现的

通过Thread类的构造方法传递了Runnable接口对象的时候,该接口对象被Thread类中的target属性保存,在start()方法执行的时候,会调用Thread类中的run()方法,这个run()方法会调用Runnable子类覆写的run()方法。

Callable实现多线程

使用Runnable接口实现多线程,当线程执行完毕后,无法获取一个返回值,此时使用java.util.concurrent.Collable接口

// 接口定义
@FunctionalInterface
public interface callable {
    public V call() throws Exception;
}

在定义Callable接口时,可以设置一个泛型,也就是数据的返回类型,这样可以避免向下转型带来的安全隐患

  • java.util.concurrent.Callable接口中实现了一个call()方法,该方法可以设置一个泛型
  • 同样,在java.util.concurrent包中,有一个FutureTask类,这个类继承了RunnableFuture接口
  • 而RunnableFuture接口继承了Future接口,该接口同样设置了一个泛型,并且该接口定义了一个get()方法,方法如下
public V get() throws InterruptedException,ExecutionException
  • 由此,通过get()方法,可以取得call()方法的返回值
  • 应为RunnableFuture是java.lang.Runnable的子类,由此,可以使用Thread的start()方法来启动线程
// 例子
class MyThread implements callable {
    @Override
    public String call() throws Exception {
        for (int x = 0; x < 10; x ++) {
            System.out.println("*****线程执行,x = " + x);
        }
        return "线程执行完毕";
    }
}

public class ThreadDemo {
    publc static void main(String[] args) throws Exception {
        // new MyThread()作为callable接口子类的返回值对象,传递给FutureTask类的构造方法
        FutureTask task = new FutureTask<>(new MyThread());
        new Thread(task).start();
        System.out.println("线程返回数据:" + task.get());
    }
}

Runnable与Callable的区别:

  1. Runnable是在JDK1.0的时候提出的多线程的实现接口,而Callable是在JDK1.5之后提出的
  2. java.lang.Runnable接口中提供一个run()方法,没有返回值
  3. java.util.concurrent.Callable接口提供一个call()方法,有返回值

任何一个线程都使用Thread类来封装,启动使用的是start(),但是启动后进入的是一种就绪状态,并没有执行

等待资源调度,调度成功后,进入运行状态run()方法,但是所有的线程不可能一直持续执行下去,中间需要产生一些暂停状态,

最后,当run()方法执行完毕后。该线程的主要任务就结束了,进入到停止状态

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值