Java多线程(一)

来源:互联网 发布:女生用电动牙刷知乎 编辑:程序博客网 时间:2024/05/25 20:01

Java线程被称为是轻量级的进程,一个进程可以有多个线程,一个线程必须要有一个父进程。线程是独立运行的。

Java使用Thread类代表线程,所有线程对象都必须是Thread类或者子类的实例。

同一个进程可以由多个线程并发进行。多线程之间内存共享容易,实现多任务并发效率高,多线程是非常有用的。


创建多线程

创建线程有三种方式:继承Thread类,实现Runnable接口,使用Callable和Future创建

一、继承Thread类创建

1.定义子类,重写run()方法,run()方法是线程执行体

2.创建子类实例,即创建了线程对象

3.调用线程对象start()方法启动线程

public class ChildThread extends Thread{    public void run(){        //这里写要执行的语句执行体    }    public static void main(String[] args){        Thread thread_1 = new ChildThread();        thread_1.start();    }}

使用继承Thread类的方法创建线程类时,多线程间不能共享线程类实例变量。


二、实现Runnable接口创建

1.定义Runnable接口的实现类,重写run()方法,依然是线程的执行体

2.创建实例,以此实例为Thread的target创建Thread对象,该Thread对象是真正的线程对象

public class RunThread implements Runnable{    public void run(){        //这里写要执行的语句执行体    }    public static void main(String[] args){        RunThread runThread = new RunThread();<span style="white-space:pre"></span>//创建Runnable对象        Thread trueThread = new Thread(runThread);<span style="white-space:pre"></span>//用Runnable对象创建线程对象        trueThread.start();    }}
和继承Thread相比,实现Runnable接口的方法不能直接创建线程对象,必须先创建Runnable对象在进一步创建线程对象。

Runnable接口是函数式接口(只有一个抽象方法的接口,可以包含多个默认方法和类方法),可用Lambda表达式创建Runnable对象。


三、使用Callable和Future创建线程

Callable提供了一个call()方法作为线程执行体,call()有返回值,并且可以声明抛出异常

Future接口代表call()方法的返回值,有一个FutureTask实现类,实现了Future和Runnable接口,可以作为Thread类的target

Callable接口同样是函数式接口,可用Lambda表达式创建Callable对象。

线程启动步骤:

1.创建Callable接口实现类,实现call()方法,call()方法作为线程执行体,有返回值,再创建实现类实例

2.使用FutureTask包装Callable对象,该Futuretask对象封装了返回值

3.使用FutureTask对象创建启动线程

4.调用FutureTask对象的get()方法获得子线程结束的返回值

public class FirstCall implements Callable{    public void call throws Exception(){        //这里写要执行的语句执行体        return ;//可以有返回值    }    public static void main(String[] args){        Callable callable = new FirstCall();//创建Callable对象        FutureTask firstTask = new FutureTask(callable);//创建FutureTask对象        Thread trueThread = new Thread(firstTask);//用FutureTask对象创建线程对象        trueThread.start();    }}
也可以使用Lambda表达式直接创建Callable对象,这样就无需创建Callable实现类和Callable对象了。

public class CallThread{    public static void main(String[] args){    CallThread callhread = new CallThread();//创建Callable方法    FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>)()->{    //这里直接写call方法里面执行体,不用写call()    });        new Thread(task).start();   }}

三种实现多线程的方式都可以实现,实现Runnable接口和实现Callable接口方式基本相同,可以归为一种方式。这种方式和继承Thread差别在于:

采用Runnable或Callable接口的方式优缺点:

1.线程类只实现了Runnable或Callable接口,可以继承其他类

2.多个线程可以共享一个target,适合多个相同线程处理同一份资源。

3.缺点:编程复杂,想访问当前线程,必须用Thread.currentThread()方法

采用继承Thread创建方式的优缺点:

1.编写简单,直接用this即可获得当前线程

2.缺点:已经继承了Thread类,不能继承其他父类


因此通常采用实现Runnable接口或Callable接口创建多线程


线程生命周期

线程生命周期包括新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)、死亡(Dead) 5种

New新建一个线程,当线程对象调用start()线程处于就绪状态,表示可以运行了,只能对处于新建状态的线程调用start()方法,否则会引发异常。

线程一下情况会进入阻塞状态:

1.调用sleep()主动放弃

2.调用了阻塞IO的方法,该方法返回之前,线程被阻塞

3.试图获得一个同步监视器,但该同步监视器正被其他线程持有

4.等待某个通知(notify)

5.调用了suspend()方法将线程挂起

针对这些情况,发生如下情况可解除阻塞:

1.调用sleep()方法的线程经过了指定时间

2.调用的阻塞IO方法已经返回

3.成功获得同步监视器

4.在等待某个通知时,其他线程发出通知

5.调用resume()恢复

测试线程是否死亡可调用线程对象的isAlive()方法,不要对已经死亡的线程调用start()方法,否则会引发异常


0 0