进程与线程
单进程:在同一个时间段内只允许一个程序执行。
多进程:在一个时间段内可以同时运行多个程序(对资源进行轮流抢占,程序依次执行),但在同一个时间点,只允许一个程序执行,然后多核的出现就使得可以在同一个时间段上允许执行多个程序。
线程:在进程的基础上划分的更小的程序单元,线程依赖于进程的支持,线程的启动速度比进程的快速多。
多线程的实现
如果要想在Java之中实现多线程的定义,那么就需要有一个专门的线程主体类进行线程的执行任务的定义,而这个主体类的定义是有要求的,必须实现特定的接口或者继承特定的父类才可以完成。
Thread类实现多线程
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);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
new MyThread("线程A").start() ;//run()方法不能直接调用,要用start()去启动
new MyThread("线程B").start() ;
new MyThread("线程C").start() ;
}
}
任何情况下,只要定义了多线程,多线程的启动永远只有一种方法:Thread类中的start()方法
Runnable接口实现多线程
用接口去实现多线程级就可以避免线程主体类的单继承局限,所以一般优先考虑用接口去实现多线程
class MyThread implements Runnable {
// 线程的主体类
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);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
//Thread提供有一个构造方法:public Thread(Runnable target);
Thread threadA = new Thread(new MyThread("线程A"));//调用Thread中的构造方法,将Runnable的子类对象传递过来,再调用Thread的start方法去启动
Thread threadB = new Thread(new MyThread("线程B"));
Thread threadC = new Thread(new MyThread("线程C"));
threadA.start();//启动多线程
threadB.start();//启动多线程
threadC.start();//启动多线程
}
}//run()方法不能直接调用,要用start()去启动
Runnable使用了函数式接口定义,所以也可以利用Lambda表达式实现
class MyThread implements Runnable {
// 线程的主体类
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);
}
}
}
public class ThreadDemo {
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.print1n(title + "运行,y ="+ y);
}
};
new Thread(run).start() ;
}
}
}
Callable接口实现多线程
Runnable接口实现多线程有一个缺点:当线程执行完毕后,无法获取一个返回值。所以引出一个Callable接口
@FunctionalInterface.
public interface Callable< V> {
public V call() throws Exception ;
}
提供有一个call()方法,就有返回值
代码实现:
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
class MyThread implements callable<String> {
@Override
public String call() throws Exception {
for (int x=0;x<10;x++) {
System. out. println("*********线程执行、x="+ x);
}
return "线程执行完毕。 ";
}
}
public class ThreadDemo {
public static void main(String[] args) throws Exception {
FutureTask<String> task = new FutureTask<>(new MyThread()) ;
new Thread(task). start();
System.out.print1n("【线程返回数据】" + task.get());
}
}
Thread和Runnable关系
Thread类的定义:
public class Thread extends Object implements Runnable { }
由thread的定义可知,Thread是Runnable的子类,所以在使用主体类继承Thread的时候覆写的run()方法还是Runnable的方法。
以第二个程序为例分析:
在进行Thread 启动多线程的时候调用的是start()方法, 而后找到的是run()方法,但通过Thread类的构造方法传递了一个Runnable接口对象的时候,那么该接口对象将被Thread类中的target 属性所保存,在start()方法执行的时候会调用Thread类中的run()方法,而这个run()方法去调用Runnable接口子类被覆写过的run(方法。
Thread主要描述的是线程,而资源的描述是通过Runnable完成的
以一个卖票的程序为例:
class MyThread implements Runnable {
// 线程的主体类
private int ticket = 5 ;//票数
@Override
public void run() {
//线程的主体方法
for(int x=0 ; x < 100 ; x++){
if(this.ticket > 0)
System.out.print1n("卖票,票号 ="+ this.ticket--);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
MyThread mt = new MyThread() ;
new Thread(mt).start();//第一个窗口卖票
new Thread(mt).start();//第二个窗口卖票
new Thread(mt).start();//第三个窗口卖票
}
}
多线程运行状态
- 任何一个线程的对象都应该使用Thread类进行封装,所以线程的启动使用的是start(),但是启动的时候实际上若干线程都将进入到一种就绪状态,现在并没有马上执行
- 进入到就绪状态之后就需要等待进行资源调度,当某一个线程调度成功之后则进入到运行状态(run()方法),但是所有的线程不可能一直持续执行下去,中间需要产生一些暂停的状态,例如:某个线程执行一段时间之后就需要让出资源进入到阻塞状态,随后重新回归到就绪状态;
- 当 run()方法执行完毕之后,实际上该线程的主要任务也就结束了,那么此时就可以直接进入到停止状态