【Java多线程】(一)多线程实现

来源:互联网 发布:关于程序员的小说 编辑:程序博客网 时间:2024/06/05 08:53
最近要忙着招工作了,发现现在招Java开发的公司特别喜欢问多线程的问题,因此就在这自己小总结一下啦~

一:进程线程的区别:

这个问题大家基本都会问。首先,进程、线程定义什么的就不多说了,这个东西百度就可以了,而且也是最基本的东西。稍微说一下区别吧。麻老师曾经这么总结过区别:线程是CPU任务调度的最小单位,进程是操作系统分配资源的最小单位。很精辟,但是也很懵。结合自己看过的资料,大约懂了一点:一段正在执行的程序就是一个进程,比如刚入门写的"Hello World"等;而线程是程序执行的最小单元,即一个进程可以拥有多个线程,各个线程之间共享程序的内存空间等等。举个例子,比如一个公司(操作系统)会有各种分厂(进程),工厂会有n条生产流水线(线程)。各个工厂之间是独立的,大家有各自的财务、厂长、生产资料等等,但是同一个工厂中各个流水线却共享工厂的财务、厂长、生产资料等等。然后董事会分配资源、制定计划是以工厂为单位。(例子貌似不是很恰当...水平有限,想不到好点的例子)。

二,如何实现多线程

java中实现多线程有三种方式,但是一般前两种用的比较多。
1.继承Thread类,重写run()方法

Thread本质是一个实现了Runnable接口的实例(Runnable接口后面会讲),代表的就是一个线程的实例。启动线程的的流程:首先通过Thread类的start()方法启动线程,这也是唯一的方法。start()方法将会启动一个新的线程,并执行run()方法,执行run方法中的代码。所以,我们一般会在run()方法中写入自己想要执行的代码。

示例代码:

public class Main {    public static void main(String[] args) {        MyThread myThread = new MyThread();        myThread.start();    }}class MyThread extends Thread{  //创建一个线程    @Override    public void run() {        System.out.println("hello");    }}

2.实现Runnable接口,并实现run()方法

主要步骤:

1)自定义类实现Runnable接口,实现run()方法

2)创建Thread对象,把实现Runnable接口的对象作为参数传入。

3)调用Thread的start()方法。

代码:

public class Main {    public static void main(String[] args) {        MyThread myThread = new MyThread();        Thread thread = new Thread(myThread);        thread.start();    }}class MyThread implements Runnable{  //创建一个线程    @Override    public void run() {        System.out.println("hello");    }}
 

3.实现Callable接口,重写call()方法

Callable接口是Executor框架中的实现类。类似于Runnable接口,但是更加强大:

1)Callable接口在任务结束之后,可以返回一个返回值,Runnable则不可以。

2)Callable接口的call()方法可以跑出异常,但是Runnable接口的run()方法则不能抛出异常。

3)运行Callable接口可以拿到一个Future对象,Future对象表示异步计算的结果,提供了方法可以计算是否完成。因为多线程属于异步计算,前面的两种方法无法从别的线程得知返回情况。在这种方法们可以使用Future监视目标线程调用call()方法的情况,调用Future的get()方法以获取结果时,当前线程会被阻塞至call()方法结束返回结果。

代码:

import java.util.concurrent.*;public class Main {    public static void main(String[] args) {        ExecutorService threadPool = Executors.newSingleThreadExecutor();        //启动线程        Future<String> future = threadPool.submit(new CallableTest());        try{            System.out.println("wait...");            System.out.println(future.get());            System.out.println("finish...");        }catch(Exception e){            e.printStackTrace();        }    }}class CallableTest implements Callable<String>{    @Override    public String call() throws Exception {        return "hello";    }}


总结:前两种方法,我们一般选择哪种方法?

第二种。看书中的介绍,主要是有两种原因:

:(两种方法实现功能类似)Thread类定义多个方法可以被重写,但是只有run()方法必须被重写。Runnable接口也可以做到。

二:(默认的约定)一般只有一个类被加强、修改的时候,才会被继承。如果没有必要重写Thread类的其他方法,则可以通过实现Runnable接口就可以实现。

因此一般选择第二种。

三:run()与start()区别:

通常调用start()方法,可以实现启动一个线程,并成为就绪态,但是并不是运行态。只是可以被JVM来调度。调度时,JVM调用run方法来完成实际的操作。因此,如果直接调用线程类的run方法,则变成一个普通的函数调用,依然是一个线程执行。如果调用start方法才能实现多线程。


原创粉丝点击