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的区别:
- Runnable是在JDK1.0的时候提出的多线程的实现接口,而Callable是在JDK1.5之后提出的
- java.lang.Runnable接口中提供一个run()方法,没有返回值
- java.util.concurrent.Callable接口提供一个call()方法,有返回值
任何一个线程都使用Thread类来封装,启动使用的是start(),但是启动后进入的是一种就绪状态,并没有执行
等待资源调度,调度成功后,进入运行状态run()方法,但是所有的线程不可能一直持续执行下去,中间需要产生一些暂停状态,
最后,当run()方法执行完毕后。该线程的主要任务就结束了,进入到停止状态