持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第14天,点击查看活动详情
1 多线程程序的含义和作用的提出
现在先来将上面复杂的知识简单化一下(不严谨,只管用):一个应用程序站一个进程,那我们简单粗暴的将一个进程就看作是一个应用程序,把操作系统中的东西丢掉。那我们要对应用程序的某个共享变量进行操作。
就比如抢鞋:nike发布的2双鞋子就是下面五个用户的共享变量,那这个就可以理解成一个多线程程序。
1.1 创建线程的方法
这里旨在感受线程是如何抢占资源的
-
使用继承的方法
使用这个方法我们要写一个类继承
Thread类,然后实现其中的run方法,代码如下/** * 使用继承Thread创建的进程 */ public class ExtendThread extends Thread { @Override public void run() { System.out.println("ExtendThread"); } }在主函数中调用这个线程的方法是
public class Demo { public static void main(String[] args) { System.out.println("MainThread"); ExtendThread extendThread = new ExtendThread(); extendThread.run(); } }运行结果如下
上面这个挺废话的,就这玩意是多线程?这不是随便写个类就能有这个效果吗?继续往下看
我们现在来把上面的代码改写一下,来看看区别
/**
* 使用继承Thread创建的进程
*/
public class ExtendThread extends Thread {
@Override
public void run() {
while(true) {
System.out.println("ExtendThread");
}
}
}
public class Demo {
public static void main(String[] args) {
ExtendThread extendThread = new ExtendThread();
extendThread.start();
while(true) {
System.out.println("MainThread");
}
}
}
上述两个代码改写以后的运行结果如下
上述两个代码都是死循环,按照原来的代码逻辑,下面这个死循环是执行不了的,但是现在它们都在执行,而且在争抢的打印,这样就可以很直观的感受多线程了
为什么改写后,在main中使用的是start方法呢?如果使用run的话,就只有这一个线程在运行,而使用start的话就可以用时运行两个线程,jdk的注释大概是这么个意思
2 多线程的执行过程
在最开始的时候我们是回顾了一下原来我们的程序执行的过程,现在我们来看看多线程的程序执行过程,大概的样子如下图
这张图看上去是“同时在执行”,其实是在争抢资源,谁抢到了谁执行。和我们以前程序最大的区别就是,原来是碰到一个函数就执行那个函数,直到那个函数执行结束,再回到主函数,除非再次遇到这个函数,不然永远不会执行。
而上述的过程是三个线程都在争抢资源,只要这个线程不结束,就会一直和主函数争抢资源
3 Runnable 接口创线程
使用接口实现也是需要实现接口的run 方法,实现代码如下
/**
* 使用实现接口来创建线程
*/
public class ImplementThread implements Runnable {
@Override
public void run() {
while(true){
System.out.println("ImplementThread");
}
}
}
public class Demo {
public static void main(String[] args) {
ImplementThread implementThread = new ImplementThread();
//将我们创建的对象变成一个线程,原来只是一个单纯的类
Thread thread = new Thread(implementThread);
thread.start();
while(true) {
System.out.println("MainThread");
}
}
}
运行结果
这两者都有自己的局限性和应该使用的地方,这个就要根据实际场景去做出选择了
4 简化操作以及线程名
这个操作简化主要是对应于上一小节的,因为看起来代码好像有点多,可以简化成:
package com.study.thread;
public class Demo {
public static void main(String[] args) {
ImplementThread implementThread = new ImplementThread();
new Thread(implementThread).start();
while(true) {
System.out.println("MainThread");
}
}
}
//你甚至还可以这样写
package com.study.thread;
public class Demo {
public static void main(String[] args) {
new Thread(new ImplementThread()).start();
while(true) {
System.out.println("MainThread");
}
}
}
这个就是涉及到可读性问题了,大家自己选择那种更合适自己
我们从这里点到 new Thread里面去看看他的构造方法源码:
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
可以看到我们传给Thread的是一个Runnable ,我们的类是实现了这个接口的。再往下看可以看到name这么一个变量,这个变量是线程名,那我们可以传递一个线程名。
现在就需要给线程传递一个线程名,并且将它的名字打印出来:
- 先给线程传递名字
- 再在原来的类中添加一个
Thread.currentThread().getName()翻译过来就是获取当前线程的名字
public class Demo {
public static void main(String[] args) {
ImplementThread implementThread = new ImplementThread();
new Thread(implementThread,"implementThread-1").start();
while(true) {
System.out.println("MainThread");
}
}
}
/**
* 使用实现接口来创建线程
*/
public class ImplementThread implements Runnable {
@Override
public void run() {
while(true){
System.out.println(Thread.currentThread().getName());
}
}
}
运行结果