Java多线程理论和手把手代码教程
- 定义:一个基本的概念就是同时对多个任务加以控制。许多程序设计问题都要求程序能够停下手头的工作,改为处理其他一些问题,再返回主进程。可以通过多种途径达到这个目的。
- 多线程的作用:通俗的讲为了实现同一时间干多件事情。
- 注意点:多个线程之间是抢CPU的关系,线程之间有随机性。
关于程序、进程、线程、任务区的理解
- 程序:通俗的讲就是一个可执行的文件。
- 进程:一个正在运行的程序。也可以理解成在内存中开辟了一块儿空间。
- 线程:负责程序的运行,可以看做一条执行的通道或执行单元,所以我们通常将进程的工作理解成线程的工作。
- 任务区:我们将线程工作的地方称为任务区。每一个线程都有一个任务区,任务区通过对应的方法产生作用。
进程和线程的关系
- 列一个小问题给出关系:进程中可不可以没有线程?
- 答案: 必须有线程,至少有一个.当有一个线程的时候我们称为单线程(唯一的线程就是主线程)。当有一个以上的线程同时存在的时候我们称为多线程。
关于JVM的多线程问题
- JVM至少有两个线程:
- 主线程:任务区是main函数。
- 垃圾回收线程:任务区是finalize函数。
- 关于JVM的两个线程的例子
class Test{
/*
* finalize()方法应该由系统调用,为了模拟多线程的环境,我们将它进行了重写
* 正常情况下,当Test类的对象被释放的时候,finalize方法会被自动调用.
* @see java.lang.Object#finalize()
*/
protected void finalize() throws Throwable {
System.out.println("finalize()");
}
}
public class Demo9 {
public static void main(String[] args) {
new Test();
//手动执行gc方法,运行垃圾回收器,触发垃圾回收机制
/* 原理:当执行gc的时候,会触发垃圾回收机制,开启垃圾回收线程,调用finalize()方法.
*/
System.gc();
//当执行到这里的时候,正常情况下有两个线程:主线程和垃圾回收线程
System.out.println("main");
}//线程是随着任务的开始而开始结束而结束.只要任务没有结束,线程就不会结束.当线程还在工作的时候,进程没有办法结束.
}
多线程编程的两种创建模式
1. 继承Thread类的方式
a. 创建Thread的子类的方式
class MyThread extends Thread {
/*
* 重写run方法---作为任务区 ,完成我们自己的功能
* Thread.currentThread():获取的是当前线程
* .getName():获取名字;
*/
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.err.println(Thread.currentThread().getName() + ":" + i);
}
}
}
public class Demo2 {
public static void main(String[] args) {//这儿有两个线程,一个主线程、一个垃圾回收线程。一般先暂时忽略垃圾回收线程
Thread thread1 = new MyThread();
Thread thread2 = new MyThread();
thread1.start();
thread2.start();
//主线程的工作代码
for (int i = 0; i < 10; i++) {
System.err.println(Thread.currentThread().getName() + ":" + i);
}
}
}
- 注意点:我们如果想run作为任务区,必须通过执行start方法,让run自动执行,不能手动调用。如果手动调用run方法,他只是一个普通方法,不代表任务区,只有自动调用的时候才代表任务区。手动调用的时候在哪儿调用,run方法内部在哪儿就是哪个线程。
b.直接匿名内部用类方法
public class Demo2 {
public static void main(String[] args) {//这儿有两个线程,一个主线程、一个垃圾回收线程。一般先暂时忽略垃圾回收线程
//1.通过Thread类直接创建线程
new Thread() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}.start();
new Thread() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}.start();
}
}
- 通过匿名内部内直接new一个Thread类,然后重写run方法,最后再执行start()方法。
2. 实现Runnable接口的方法
- 理念:实现了线程与任务分离。
- 好处:哪个线程工作,就将哪个线程交给谁,操作方便。
- 实现
//创建任务类
class Ticket implements Runnable{
//因为Ticket对象被4个线程共享,所以num也被共享
int num = 40;
public void run() {
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+" i:"+i+" "+ --num);
}
}
}
public class Demo3{
public static void main(String[] args) {
//1.先创建一个任务类对象
Ticket ticket = new Ticket();
//2.创建线程并绑定任务
Thread thread1 = new Thread(ticket);
Thread thread2 = new Thread(ticket);
Thread thread3 = new Thread(ticket);
Thread thread4 = new Thread(ticket);
//3.开启线程
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
- 注意点: 如果我们自己创建了独立的任务类,线程会优先调用我们手动传入的任务类对象的run方法,不会再去调用Thread类默认的run方法。