Java多线程基础
1. 线程相关概念
1.1 程序(program)
- 是为完成特定任务、用某种语言编写的一组指令的集合。简单的说:就是我们写的代码
public class BallMove extends JFrame{
//窗口
MyPanel mp=null;
public static void main(String[] args){
BallMove ballMove=new BallMove();}
//构造器
public BallMove(){
mp=new MyPanel(); this.add(mp);
this.setSize(400,300);
//窗口JFrame 对象可以监听键盘事件,即可以监听到面板发生的键盘事件
this.addKeyListener(mp);
this.setDefaultCloseOperation(JFrame.EXITON_CLOSE);
this.setVisible(true);
}
}
1.2 进程
- 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存空间。当我们使用迅雷,又启动了一个进程,操作系统将为迅雷分配新的内存空间。
- 进程是程序的一次执行过程,或是正在运行的一个程序。是动态过程:有它自身的产生、存在和消亡的过程。
1.3 什么是线程
- 线程是由进程创建的,是进程的一个实体。
- 一个进程可以拥有多个线程,如下图
- 坦克大战进阶部分会加入多线程的应用。
1.4 其他相关概念
- 单线程:同一个时刻,只允许执行一个线程
- 多线程:同一个时刻,可以执行多个线程,比如:一个qq的进程,可以同时打开多个聊天窗口,一个迅雷进程,可以同时下载多个文件。
- 并发:同一个时刻,多个任务交替执行,造成一种"貌似同时"的错觉,简单地说,单核cpu实现的多任务就是并发。
- 并行:同一个时刻,多个任务同时执行。多核cpu可以实现并行。
- 下面简单的用代码看一下自己的电脑cpu是几何的
public class CpuNum {
public static void main(String[] args) {
Runtime runtime = Runtime.getRuntime();
//获取当前电脑的cup数量
int cpuNums = runtime.availableProcessors();
System.out.println("当前cpu数量 = "+cpuNums);
//当前cpu数量 = 12
//表示我的电脑cpu是12核的
}
}
2. 线程基本使用
2.1 创建线程的两种方式
在java中线程来使用有两种方法:
- 继承Thread类,重写run方法
- 实现Runnable接口,重写run方法
2.2 线程基本使用
2.2.1 案例一:继承Thread 类
- 请编写程序,开启一个线程,该线程每隔1秒。在控制台输出“喵喵,我是小猫咪”
- 对上题改进:当输出80次 喵喵,我是小猫咪,结束该线程
- 使用JConsole 监控线程执行情况,并画出程序示意图!
- 代码如下:
public class Thread01 {
//通过继承Thread类创建
public static void main(String[] args) {
//创建Cat对象,可以当做线程使用
Cat cat = new Cat();
//简单的源码阅读
/*
第一步,执行start()方法
public synchronized void start() {
start0();
}
第二步,执行start0方法
//start0()是本地方法,是JVM调用,底层是c/c++实现
//真正实现多线程的效果,是start0(),而不是 run方法
private native void start0();
*/
cat.start();//启动线程->最终会执行cat的run方法
//cat.run();//如果我们在主方法里面直接调用run方法,
// 那么并没有真正的启动一个线程,就会把run方法执行完毕后,才向下执行
//说明:
//当main线程启动一个子线程Thread-0,主线程不会阻塞,会继续执行
//这时,主线程和子线程是交替执行的
System.out.println("主线程继续执行" + Thread.currentThread().getName());
for (int i = 0; i < 60; i++) {
System.out.println("主线程i = " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
/*
1. 请编写程序,开启一个线程,该线程每隔1秒。在控制台输出“喵喵,我是小猫咪”
2. 对上题改进:当输出80次 喵喵,我是小猫咪,结束该线程
*/
//1. 当一个类继承了Thread类,该类就可以当成线程使用
//2. 我们会重写run方法,写上自己的业务逻辑
//3. run Thread 类实现了 Runnable 接口的 run方法
/*
@Override
public void run() {
if (target != null) {
target.run();
}
}
*/
class Cat extends Thread {
//重写run方法,写上自己的业务逻辑
int times = 1;
@Override
public void run() {
while (true) {
System.out.println("喵喵,我是小猫咪" + (times++) + "线程名=" + Thread.currentThread().getName());
//得到线程名=Thread-0
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (times > 80) {
break;
}
}
}
}
-
使用JConsole 监控线程执行情况
-
先运行程序
-
然后点击Terminal,输入jconsole打开JConsole
-
选择我们的java文件Thread01
-
可以看到正在运行主线程main和子线程Thread-0
-
最后我们可以看到当主线程main和子线程Thread-0都执行完过后,主线程已经消失,子线程并没有立刻消失
-
-
start()方法调用start0()方法后,该线程并不一定会立马执行,只是将线程变成了可运行状态。具体什么时候执行,取决于CPU,由CPU统一调度。
2.2.2 案例二:实现Runnable 接口
说明
- java是单继承的,在某些情况下一个类可能已经继承了某个父类,这时在用继承Thread类方法来创建线程显然不可能了。
- java设计者们提供了另外一个方式创建线程,就是通过实现Runnable接口来创建线程
案例
- 请编写程序,该程序可以每隔1秒。在控制台输出“hi!”,当输出10次后,自动退出。
- 请使用实现Runnable接口的方式实现。
- 这里底层使用了设计模式[代理模式]=>代码模拟 实现Runnable接口开发线程的机制
public class Thread02 {
//通过实现接口Runnable来开发线程
public static void main(String[] args) {
Person person = new Person();
//创建了 Thread 对象,把 person 对象(实现 Runnable),放入 Thread
Thread thread = new Thread(person);
thread.start();
}
}
/*
1. 请编写程序,该程序可以每隔1秒。在控制台输出“hi!”,当输出10次后,自动退出。
2. 请使用实现Runnable接口的方式实现。
*/
class Person implements Runnable {
int count = 0;
@Override
public void run() {
while (true) {
System.out.println("hi" + (++count) + "线程名" + Thread.currentThread().getName());
//休眠一秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (count >= 10) {
break;
}
}
}
}
- 用代码简单模拟一下静态代理模式
public class Thread03 {
public static void main(String[] args) {
B b = new B();//实现了Runnable
Thread thread = new Thread(b);
thread.start();
}
}
class A {
}
class B extends A implements Runnable {
@Override
public void run() {
System.out.println("最简易的代理模式");
}
}
//线程代理类 , 模拟了一个极简的 Thread 类
class ThreadProxy implements Runnable {
private Runnable target = null;//属性 Runnable
@Override
public void run() {
if (target != null) {
target.run();//动态绑定(运行类型 B)
}
}
public ThreadProxy(Runnable target) {
this.target = target;
}
public void start() {
start0();
}
public void start0() {
run();
}
}
2.2.3 案例三:多线程执行
- 请编写一个程序,创建两个线程,一个线程每隔1秒输出“helloworld”,输出10次,退出,
- 一个线程每隔1秒输出“hi”,输出 5次退出.
- 代码如下:
public class Thread04 {
//多线程执行
public static void main(String[] args) {
Hello hello = new Hello(