线程与进程
进程是程序的一次动态执行过程,它经历了从代码加载、执行到执行完毕的一个完整过程,这个过程也是进程本身从产生、发展到最终消亡的过程。
线程是比进程更小的执行单位,线程是在进程的基础上进行的进一步划分,一个进程可能包含多个同时执行的线程。
注意:所有的线程一定要依附于进程才能够存在,进程一旦消失,线程也一定会消失。 而Java是为数不多的支持多线程的开发语言之一。
多线程实现
在Java中,如果要想实现多线程的程序,就必须依靠一个线程的主类体(表示的是一个线程的主类)。但是这个线程的主类体在定义时也需要有一些特殊要求,即此类需要继承Thread类或实现Runnable(Callable)接口来完成定义。
继承Thread类
java.lang.Thread是一个负责线程操作的类,任何类只需要继承Thread类就可以成为一个线程的主类。线程主类的定义格式如下:
class 类名称 extends Thread{ //继承Thread类
属性...; //类中定义属性
方法...; //类中定义方法
public void run(){ //覆写Thread类中的run()方法,此方法是线程的主体
线程主体方法;
}
}
例:启动多线程
package Project.Study.Multithreading;
import java.lang.Thread;
class MyThread extends Thread{ //这就是一个多线程的操作类
private String name; //定义类中的属性
public MyThread(String name){ //定义构造方法
this.name=name;
}
@Override
public void run(){ //覆写run()方法,作为线程的主操作方法
for (int x=0;x<200;x++){
System.out.println(this.name+"-->"+x);
}
}
}
public class Test1 {
public static void main(String[] args){
MyThread mt1=new MyThread("线程A"); //实例化多线程对象
MyThread mt2=new MyThread("线程B");
MyThread mt3=new MyThread("线程C");
mt1.start(); //启动多线程
mt2.start();
mt3.start();
}
}
//结果
//线程B-->0
//线程C-->0
//线程A-->0
//线程C-->1
//线程B-->1
//线程C-->2
//后面省略...
注意:直接调用run()方法,并不能启动多线程,多线程启动的唯一方法就是Thread类中的start()方法:public void start()(调用此方法执行的方法体是run()方法定义的代码)
实现Runnable接口
Thread类实现多线程最大的缺点就是单继承的问题,因此Java中也可以利用Runnable接口来实现多线程。这个接口定义如下:
@FunctionalInterface //函数式接口
public interface Runnable{
public void run();
}
例:使用Runnable实现多线程
package Project.Study.Multithreading;
class MyTread2 implements Runnable{ //定义线程主体类
private String name; //定义类中的属性
public MyTread2(String name){ //定义构造方法
this.name=name;
}
@Override
public void run(){ //覆写run()方法
for(int x=0;x<200;x++){
System.out.println(this.name+"-->"+x);
}
}
}
public class Test2 {
public static void main(String []args){
MyTread2 mt1=new MyTread2("线程A"); //实例化多线程类对象
MyTread2 mt2=new MyTread2("线程B");
MyTread2 mt3=new MyTread2("线程C");
new Thread(mt1).start(); //使用Thread启动多线程
new Thread(mt2).start();
new Thread(mt3).start();
}
}
//结果:
//线程C-->0
//线程A-->0
//线程B-->0
//线程A-->1
//线程C-->1
//线程C-->2
//后面省略...
例:使用Lambda表达式操作
package Project.Study.Multithreading;
public class Test2 {
public static void main(String []args){
String name="线程对象";
new Thread(()->{
for(int x=0;x<200;x++){
System.out.println(name+"-->"+x);
}
}).start();
}
}
//结果:
//线程对象-->0
//线程对象-->1
//线程对象-->2
//线程对象-->3
//线程对象-->4
//线程对象-->5
//后面省略...
利用Callable接口实现多线程
Callable接口解决了Runnable接口里的run()方法不能返回操作结果的问题。
该接口的定义如下:
@FunctionalInterface
public interface Callable{
public V call() throws Exception;
}
定义一个线程主体类
import java.util.concurrent.Callable;
class MyThread3 implements Callable<String>{ //多线程主体类
private int ticket=10; //卖票
@Override
public String call() throws Exception{
for(int x=0;x<100;x++){
if(this.ticket>0){ //还有票可以出售
System.out.println("卖票,ticket="+this.ticket--);
}
}
return "票已卖光!"; //返回结果
}
}
当多线程的主体类定义完成后,要利用Thread类启动多线程,但是在Thread类中并没有定义任何构造方法可以直接接收Callable接口对象实例,并且由于需要接收call()方法返回值的问题,Java提供了一个java.util.concurrent.FutureTask< V >类,定义如下:
public class FutureTask<V>
extends Object
implements RunnableFuture<V>
例:启动多线程
package Project.Study.Multithreading;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
class MyThread3 implements Callable<String>{
private int ticket=10;
@Override
public String call() throws Exception{
for(int x=0;x<100;x++){
if(this.ticket>0){
System.out.println("卖票,ticket="+this.ticket--);
}
}
return "票已卖光!";
}
}
public class Test3 {
public static void main(String []args)throws Exception{
MyThread3 mt1=new MyThread3(); //实例化多线程对象
MyThread3 mt2=new MyThread3();
FutureTask<String>task1=new FutureTask<>(mt1);
FutureTask<String>task2=new FutureTask<>(mt2);
//FutureTask是Runnable接口子类,所以可以使用Thread类的构造来接收task对象
new Thread(task1).start(); //启动第一个多线程
new Thread(task2).start(); //启动第二个多线程
//多线程执行完毕后可以取得内容,依靠FutureTask的父接口Future中的get()方法实现
System.out.println("A线程返回的结果:"+task1.get());
System.out.println("B线程返回的结果:"+task2.get());
}
}
//结果:
//卖票,ticket=10
//卖票,ticket=9
//卖票,ticket=10
//卖票,ticket=9
//卖票,ticket=8
//卖票,ticket=8
//卖票,ticket=7
//卖票,ticket=7
//卖票,ticket=6
//卖票,ticket=6
//卖票,ticket=5
//卖票,ticket=5
//卖票,ticket=4
//卖票,ticket=4
//卖票,ticket=3
//卖票,ticket=2
//卖票,ticket=1
//卖票,ticket=3
//卖票,ticket=2
//卖票,ticket=1
//A线程返回的结果:票已卖光!
//B线程返回的结果:票已卖光!
本程序利用FutureTask类实现Callable接口的子类包装,由于FutureTask是Runnable接口的子类,所以可以利用Tread类的start()方法启动多线程,当线程执行完毕后,可以利用Future接口中的get()方法返回线程的执行结果。
线程的操作状态
任何线程一般都具有5个状态,即:创建、就绪、运行、堵塞和终止。
1.创建状态
在程序中用构造方法创建一个线程对象后,新的线程对象便处于新建状态,此时,它具有相应的内存空间和其他资源,但还处于不可运行的状态。
2.就绪状态
新建线程对象后,调用该线程的start()方法就可以启动线程。当线程启动,线程就进入就绪状态。此时,线程将进入线程队列排队,等待CPU服务,这表明它已经具备了运行条件。
3.运行状态
当就绪状态的线程被调用并获得处理器资源时,线程就进入了运行状态。此时,自动调用该线程对象的run()方法。
4.堵塞状态
一个正在执行的线程在某些特殊情况下,如被人为挂起或需要执行耗时的输入输出操作时,将让出CPU并暂时中止自己的执行,进入堵塞状态。在可执行状态下,如果调用sleep()、suspend()、wait()等方法,线程都将进入堵塞状态。堵塞时,线程不能进入排队队列,只有引起堵塞的原因被消除后,线程才可以转入就绪状态。
5.终止状态
线程调用stop()方法或run()方法执行结束后,就处于终止状态。处于终止状态的线程不具有继续运行的能力。