针对线程这方面的学习,个人觉得有必要通过笔记的方式将之纪录下来,以便日后查看复习。
首先第一点就是要区分多进程 和 多线程的区别,两者名字相近但意义相差巨大。
多进程:在操作系统中能(同时)运行多个任务(程序);
多线程:在同一个应用程序中多个顺序流被同时执行;
可以看到,线程其实是进程之中的一个执行的顺序流。
接下来看一下线程的的执行过程:
由上面这张图片可以看到,当一个线程对象被创建时,它并不是直接就可以执行的。调用start()方法可以使得该线程进入【就绪状态】,这时候这个线程可以同其他的线程竞争CPU,当该线程抢到CPU的执行权时,就会进入【运行状态】开始执行线程体中的代码,执行期间如果CPU被其他线程抢走,该线程重新回到【就绪状态】,等待再次抢占CPU,如果在【运行状态】中,线程体内的部分代码出现问题(出现阻塞事件),比如在从网络接收数据时,某一时刻这个数据没有准备好,导致线程代码无法向下执行时,就会进入【阻塞状态】,此时该线程同样不占用CPU,当阻塞事件结束时,比如网络数据准备就绪,此时该线程会解除阻塞状态,返回到【就绪状态】。当线程体中的代码执行完的时候,该线程死亡。
最重要内容就是的线程的启动方法了,线程的启动有两种方法:
方法一:定义一个类继承Thread类,复写父类中的run()方法,该方法的函数体就是该线程的线程体。然后在主函数中生成该类的对象,调用该对象的start()方法来启动线程。
//定义MyThread类 继承自 Thread
public class MyTread extends Thread{
public void run(){
for(int i = 0;i < 10;i++){
System.out.println("MyThread-->"+i);
}
}
}
//在主函数中代码
public static void main(String[] args) {
MyTread mt = new MyTread();
mt.start();
for(int i = 0;i < 10;i++){
System.out.println("Main-->"+ i);
}
}
看看执行结果如下:
Main-->0
MyThread-->0
Main-->1
MyThread-->1
Main-->2
MyThread-->2
Main-->3
MyThread-->3
MyThread-->4
MyThread-->5
Main-->4
MyThread-->6
Main-->5
MyThread-->7
Main-->6
MyThread-->8
Main-->7
MyThread-->9
Main-->8
Main-->9
方法二:定义一个类,实现Runnable接口,复写该接口的run()方法,为线程体。在主函数中先生成该类的对象,再定义Thread的引用,将该对象作为参数传入Thread的构造函数中,调用Thread对象的start()方法启动线程
//定义Runnable的接口实现类RunnableImpl
public class RunnableImpl implements Runnable{
@Override
public void run() {
for(int i = 0;i < 10;i++){
System.out.println("Runnable"+i);
}
}
}
//主函数
public static void main(String[] args) {
//生成RunnableImpl的对象rl
RunnableImpl rl = new RunnableImpl();
//生成Thread的对象,rl作为构造函数的参数
Thread t = new Thread(rl);
t.start();
for(int i = 0;i < 10;i++){
System.out.println("Main-->"+ i);
}
}
Runnable0
Main-->0
Runnable1
Main-->1
Main-->2
Main-->3
Runnable2
Main-->4
Runnable3
Runnable4
Runnable5
Runnable6
Runnable7
Runnable8
Runnable9
Main-->5
Main-->6
Main-->7
Main-->8
Main-->9
在这两种线程的启动方法中,更加常用的是第二种方法,因为java中只支持单继承,所以在第一种方法中,MyThread继承Thread类,就不能再继承其他类,而第二种方法中是实现了Runnable接口,同时他还可以继承其他的类。并且第二种方法中线程体的代码是和线程执行是分开的。
未完待续~