Java多线程编程

1、进程与线程

Java语言的特点之一即是支持多线程开发。在传统的DOS系统时代,如果电脑出现病毒,那么所有的程序将无法执行,因为传统的DOS采用的是单进程处理,即在同一时间段上只允许一个程序在执行。而多进程就表示在一个时间段可以同时运行多个程序,这些程序将进行资源的轮流抢占。所以在同一个时间段上会有多个程序依次执行,但是在同一个时间点上只会有一个进程执行。到了多核CPU,即可以处理的CPU增加,所以处理速度增快。

线程是在进程基础上划分的更小的程序单元,也是在进程的基础上创建并使用的,所以线程依赖于进程的支持。线程的启动速度要比进程快得多,当时用多线程进行并发处理时,其执行性能高于进程。Java是多线程的编程语言,所以Java在进行并发访问处理的时候可以得到更高处理性能。

2、Thread类实现多线程

在Java中实现多线程定义需要有专门的线程主体类,这个线程主体类必须实现特定的接口或者继承特定的父类。

①继承Thread类实现多线程:(在java.lang.Thread包中)

继承Thread类的线程主体类需要覆写Thread类中提供的一个public void run()方法,这个方法属于线程的主方法。多线程要执行的功能都应该在run()方法中进行定义。因为run()方法与操作系统的资源调度有关,所以run()方法不能被直接调用,要想启动多线程必须使用public void start()方法。(此时虽然调用的是start()方法,但是最终执行的是run()方法,并且所有的线程对象交替执行,其执行顺序不可控)

分析start()方法:

public synchronized void start() {
        if (threadStatus != 0) //判断线程的状态
            throw new IllegalThreadStateException(); //抛出了一个异常,但是整个程序并没有使用throws或者明确的try...catch处理,因为该异常一定是RuntimeException的子类。每一个线程类对象只允许启动一次,如果重复启动则会抛出此异常
        group.add(this);

        boolean started = false;
        try {
            start0(); //调用方法
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
            }
        }
    }

    private native void start0(); //该方法无方法体。

在Java程序执行的过程中考虑到不同层次开发者的需求,所以其支持有本地的操作系统函数调用,这被称为JNI技术,即Java本地接口(Java Native Interface),利用这项技术可以使用操作系统提供的底层函数进行一些特殊处理,在Thread类中提供的start0()就表示需要将此方法依赖于不同的操作系统实现。但是Java开发过程中不推荐使用。

(提供不同版本的JVM是为了匹配不同版本的操作系统。)

②基于Runnable接口实现多线程

由于单继承的局限,所以Java提供了第二种多线程的主体定义结构形式:实现java.lang.Runnable接口,接口定义如下:

@FunctionalInterface //从JDK1.8引入了Lambda表达式之后就变为了函数式接口
public interface Runnable {
    public abstract void run();
}

Thread类中提供有构造方法:public Thread(Runnable target);

此时,启动多线程的方法:
class MyThread implements Runnable {
    private String title;
    public MyThread(String title) {
        this.title= title ;
    }
    public void run() { //线程的主体方法
        for(int x = 0; x < 10; x ++) {
            System.out.println(this.title + "运行,x =" + x);
        }
    }
}

public class Demo {
    public static void main(String[] args) {
        Thread threadA = new Thread(new MyThread("线程对象A"));
        Thread threadB = new Thread(new MyThread("线程对象B"));
        Thread threadC = new Thread(new MyThread("线程对象C"));
        threadA.start();
        threadB.start();
        threadC.start();
    }
}
此时不受到单继承的局限所以这样的设计才是一个标准型的设计。由于Runnable接口使用了函数式接口定义,所以可以直接利用Lambda表达式实现多线程定义:

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

运行结果(部分):

在开发之中,优先考虑通过Runnable接口实现多线程,并且是通过Thread类对象启动多线程。

3、Thread与Runnable关系

Thread类定义:public class Thread extends Object implements Runnable {}

通过分析上述实现多线程的程序,可以了解到,Thread类和MyThread类都实现了Runnable接口。不同在于,Thread类属于代理类,负责线程相关的资源调度处理,MyThread类属于业务处理类,负责核心业务。

在进行Thread启动多线程的时候调用的是start()方法,而后找到的是run()方法。具体过程:通过Thread类的构造方法传递了一个Runnable接口对象的时候,该接口对象被Thread类中的target属性保存(其中有定义:private Runnabe target;)。而后run()方法执行的是target.run()方法,即在start()方法执行的时候会调用Thread类中的run()方法,而这个run()方法去调用Runnable接口子类被覆写过的方法。

多线程开发的实质是多个线程可以进行统一资源的抢占。Thread主要描述的是线程,而资源的描述通过Runnable类(子类)完成的,(线程对象访问资源)。

4、Callable实现多线程

依靠Runnable实现多线程的时候,当线程执行完毕后无法获取一个返回值,所以从JDK1.5之后提出了一个新的线程实现接口:java.util.concurrent.Callable接口。

其中,Future是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果,可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。

RunnableFuture继承了Runnable接口和Future接口,而FutureTask实现了RunnableFuture接口。所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。

事实上,FutureTask是Future接口的一个唯一实现类。

Callable实现多线程示例:

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

class MyThread implements Callable<String> {
    public String call() throws Exception {
        for(int x = 0 ; x < 100 ; x ++) {
            System.out.println("线程执行:x = " + x);
        }
        return "线程执行完毕";
    }
}
public class Pool {
    public static void main(String[] args) throws Exception {
        FutureTask<String> 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()方法,可以有返回值;

5、多线程运行状态

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值