一、线程相关概念
- 程序:完成特定任务,用某种语言编写的一组指令的集合。
- 进程:运行起来的程序就是进程。进程运行时,操作系统需要为该进程分配内存空间。进程是一个动态过程,有产生、存在和消亡的过程。
- 线程:线程是由进程创建的,是进程的一个实体,一个进程可以拥有多个线程。
- 单线程:同一时刻只允许执行一个线程。
- 多线程:同一个时刻,可以执行多个线程,比如:一个QQ进程,可以同时打开多个聊天窗口,一个迅雷进程,可以同时下载多个文件。
- 并发:单核CPU实现的多任务就是并发
- 并行:同一个时刻,多个任务同时执行,多核CPU可以实现并行。
通过下面这段代码可以显示您当前电脑的处理器CPU的个数。
public class CpuNum {
public static void main(String[] args) {
Runtime runtime = Runtime.getRuntime();
int cpuNums = runtime.availableProcessors();
System.out.println("当前电脑有cpu个数:" + cpuNums);
}
}
二、线程的基本使用
创建线程有两种方法:1.继承Thread类;2.实现Runnable接口。
2.1 继承Thread类创建线程
继承Thread类之后需要重写run方法,写上自己的代码逻辑。继承了Thread类之后,可以通过创建该对象,调用其的start()方法来启动线程,启动之后会自动执行run方法中的代码。这里举个例子:创建一个猫咪线程,每秒打印一次“喵喵,我是小喵咪 x”。
public class ThreadTest1 {
public static void main(String[] args) throws InterruptedException {
new Cat().start();//启动猫咪线程
//当main线程启动一个子线程 Thread-0,主线程不会阻塞,会继续执行
System.out.println("主线程继续执行 " + Thread.currentThread().getName());
for (int i = 0; i < 10; i++) {
System.out.println("主线程" + i);
Thread.sleep(1000);
}
}
}
//1. 当一个类继承了Thread类,该类就可以当做线程使用
//2. 我们会重写run方法,写上自己业务代码
//3. Thread类里面的run方法是继承Runnable接口的run方法
class Cat extends Thread{
int times = 0;
@Override
public void run() {//重写run方法,写上自己的业务
while (true) {
System.out.println("喵喵, 我是小猫咪" + times
+ "线程名 = " + Thread.currentThread().getName());
++times;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(times == 8){
break;//退出while循环,结束线程
}
}
}
}
主线程可以创建很多子线程,只有当所有子线程都结束时,进程才会结束。
思考:为何线程启动时是使用start()方法,而不是直接调用run()方法。
直接调用run方法,相当于主线程main直接调用run这个方法,虽然执行的代码逻辑是一样的,但是主线程里面的代码是顺序执行的,不是另外开了一个子线程并行处理。
start()源码:start()方法内部调用的是start0()方法,是JVM调用,底层是C/C++实现。真正实现多线程效果的是start0()方法,而不是run()。start()方法是将该线程加入一个group集合中,只是将线程设置为可以运行的状态,有JVM来决定何时启动该线程。
group.add(this);//加入待启动集合
boolean started = false;
try {
start0();//native方法,由JVM来进行调用
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
2.2 实现Runnable接口创建线程
该方法实现创建线程类使用到了静态代理这种设计模式,这里描述一下本人对该模式的理解。其中实现该模式需要有两个对象和1个接口的参与。其中为:代理类即本代码的Thread类,被代理类即Dog类以及我们的Runnable接口。我们的目标是让被代理类(Dog)作为主体来创建一个线程,而创建线程就需要调用start()方法。但是Dog类并没有start()方法。此时静态代理模式的几个步骤为:
- 让被代理的类和代理类都实现Runnable接口。
- 代理类的构造器需要将代理类作为参数传入。
- 代理类的run方法的方法体就是调用代理类的run方法,即代理执行。
- 通过调用代理类的start方法来间接让代理类作为主体创建一个新的线程。
这个是我所理解的通过静态代理的设计模式来实现创建线程的方法和步骤,如果有错误的地方,还请海涵。
public class TreadTest2 {
public static void main(String[] args) {
// Dog dog = new Dog();
// //无法直接使用dog.start();启动线程
// Thread thread = new Thread(dog);
// thread.start();
}
}
class Dog implements Runnable{//通过Runnable接口实现线程类
int count = 0;
public void run() {
while (true){
System.out.println("小狗汪汪叫... hi "
+ (++count) + Thread.currentThread().getName());
//休眠1秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(count == 10){
break;//退出线程
}
}
}
}