多线程:
多线程比单线程快,前面简单介绍过:集合如果是不安全的,那么它就是多线程的,了解多线程之前,先了解什么是并发和并行。
并发:指两个或多个事件在同一个时间段内发生。
并行:指两个或多个事件在同一时刻发生,这里强调同一时刻,并行的速度快于并发。
线程与进程:
进程:指一个内存中运行的应用程序,每个进程都有一个独立的空间,一个应用程序可以同时运行多个进程,进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个程序从创建到运行再到消亡的过程。
线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程,一个进程中是可以有多个线程的,这个应用程序可以称为多线程程序。简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程。
CPU:计算机中央处理器,可以对数据进行计算,指挥计算机中软件和硬件工作。
线程调度:
分时调度:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间。
抢占调度:优选让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个线程执行,java使用的是抢占式调度。
创建线程类:
主线程:执行主方法(main)的线程被称作主线程。
单线程实现一个业务:
// 主线程:执行主方法(mian)的线程
// java默认是单线程的,从mian方法中开始,从上到下依次执行。
public class MainClass {
public static void main(String[] args) {
// 创建第一个对象:
Person p1 = new Person("小明");
p1.consoleHandle();
// 创建第二个对象:
Person p2 = new Person("小红");
p2.consoleHandle();
// 执行此程序,可以看到到上面部分从上到下依次执行:
// 小明--0
// 小明--1
// 小红--0
// 小红--1
System.out.println(1 / 0); // 执行完上面的代码后,从这里开始报错Exception in thread "main"单线程程序中,抛出异常后,后面的代码将不能在执行,多线程的话可以将多个业务放到不同的线程中去,这样即使一个线程中抛出了异常,另一个线程也不会被影响到。
System.out.println(5);
}
}
创建一个多线程程序 :
在lang包下有一个Thread类,代表线程,用来描述多个线程。java中创建线程有两种方式。
方式一:将类声明为Thread的子类,该子类应该重写Thread的run方法,,如:
定义一个多线程的类:
// 创建线程的第一种方式:创建Thread子类:
// 想要实现多线程,就要继承Thread类,其步骤:1.创建一个Thread子类、2.在该子类中重写run方法,设置线程任务(该线程要做什么)3.创建该类的实例对象 4.调用该实例的start方法开启新的线程(调用start方法会开辟新的线程,虚拟调用该线程的run方法)
// run执行的结果是两个线程并发的运行,当前线程(main线程,从调用返回给start方法)和另一个线程(创建的新线程,执行其run方法)
// start方法,一个线程只能调用一次,多次启动一个线程是非法的,特别是当线程已经结束执行后,不能再重新启动。
// 1.创建一个Thread的子类:
public class OneThreadClass extends Thread {
// 2.重写run方法
@Override
public void run () {
// run方法里面用来设置任务,这里还是以循环为例:
for (int i = 0; i < 2; i++) {
System.out.println("run1--" + i);
}
};
}
实例化一个多线的类:
public class TestOneThreadClass {
public static void main(String[] args) {
// 3.创建一个多线程类的实例:
OneThreadClass ot = new OneThreadClass();
// 4.调用该实例的start方法(start方法继承于Thread类)让其调用run方法来执行任务:
ot.start();
// 主线程也执行for
for (int i = 0; i < 2; i++) {
System.out.println("main--" + i);
}
// 打印到控制台的结果如下:可以看到此时程序不是从上到下依次执行了,原因是这里是个多线程的,多个线程抢占cpu执行,同一优先级随机选择一个线程执行
// main--0
// run1--0
// main--1
// run1--1
}
}
程序执行图:
内存图:
Thread类的常用方法:
创建一个继承Thread的类:
public class MyThread extends Thread {
public MyThread() {
}
// 4.修改线程名称还有一种方式就是通过构造方法调用super将名称传给super,注意重载格式是(String name)这个,如:
public MyThread(String name) {
super(name);
}
@Override
public void run () {
// 1.Thread类的getName可以获取到线程的名称:
String nm = getName();
System.out.println(nm); // Thread-0, 每创建一个线程,并执行start方法后,会新增一个后面索引增加1的新线程
// 2.Thread类的currentThread返回当前正在执行的线程对象的索引:
System.out.println(Thread.currentThread()); // Thread[Thread-1,5,main] 和 Thread[Thread-0,5,main] Thread[线程001,5,main] Thread[线程003,5,main]
System.out.println(Thread.currentThread().getName()); // Thread-0 和 Thread-1 线程001 线程003
};
}
使用继承了Thread类的类:
public class UseMyThread {
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();
new MyThread().start();
// 3.Thread类的setName(String name)设置线程的名字:
MyThread mt2 = new MyThread();
mt2.setName("线程001");
mt2.start();
// 4-2:对构造方法直接修改名称的测试:
MyThread mt3= new MyThread("线程003");
mt3.start();
// 5.Thread的sleep(mills)方法,让线程休眠n毫秒后继续执行,如:正常一个循环会很快的执行完,此时用sleep给它休眠5000毫秒执行,就可以看到每各5秒钟打印一次:
for (int i = 0; i < 10; i++) {
System.out.println(i);
try {
// 本身会报异常,使用try/catch处理下即可
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
创建多线程的第二种方式(推荐方式):
继承Runnable接口实现类:
// 1.创建一个Runnable接口的实现类
public class RunnableTmplClass implements Runnable {
// 2.在实现类中重写Runnbale接口的run方法,设置线程任务
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "--" + i);
}
}
}
测试第二种方式实现多线程:
// 创建多线程的第二种方式:实现Runnable接口:
// Runnable接口应该由那些打算通过某一线程执行某实例的类来实现,类必须定义一个称为run的无参数方法。
// 实现步骤: 1.创建一个Runnable接口的实现类 2.在实现类中重写Runnbale接口的run方法,设置线程任务 3.创建一个Runnable接口的实现类对象 4.创建Thread类对象,构造方法中传递Runnable接口实现类对象 5.调用Thread类中的start方法,开启新的线程执行run方法
public class TwoThreadClass {
public static void main(String[] args) {
// 3.创建一个Runnable接口的实现类对象
RunnableTmplClass rt = new RunnableTmplClass();
// 4.创建Thread类对象,构造方法中传递Runnable接口实现类对象
Thread td = new Thread(rt);
// 5.调用Thread类中的start方法,开启新的线程执行run方法
td.start();
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "--" + i);
}
}
}
前两种创建线程方式之间的区别:
1.实现Runnable接口创建多线程:
避免了单继承的局限性(一个类只能继承一个父类,如果类继承了Thread类后就无法再继承其他类了),但是实现了Runnable接口后还可以继承其他类,实现其他的接口。
2.增强了程序的扩展性,降低了程序的耦合性(解耦,实现Runnable接口的方式,把设置线程任务和开启新线程进行了分离,可以实现多个Runnable接口实现类并设置run方法任务,给Thread类创建对象使用)。
匿名内部类方法实现线程的创建:
// 匿名内部类方式实现线程的创建
// 匿名内部类的作用:简化代码,把子类继承父类,重写父类的方法,创建子类对象合在一起完成,把实现类实现接口,重写接口中的方法,创建实现类对象合成一步完成
// 匿名内部类的最终产物:子类、实现类对象,这个类没有名字
// 格式:new 父类/接口(){重复父类/接口中的方法}
public class ThreeThreadClass {
public static void main(String[] args) {
// 线程的父类是Thread
new Thread(){
@Override
public void run(){
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "----" + i);
}
};
}.start();
}
}
提示:本文图片等素材来源于网络,若有侵权,请发邮件至邮箱:810665436@qq.com联系笔者删除。
笔者:苦海