java的重要功能之一就是内部支持多线程,这一系列文章将详细剖析java多线程的基础知识
多线程概述
多线程引入
- 程序只有一个执行流程,所以这样的程序就是单线程程序。
- 假如一个程序有多条执行流程,那么,该程序就是多线程程序。
进程:正在运行的程序,是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间和系统资源。
线程:是进程中的单个顺序控制流,是一条执行路径。一个进程如果只有一条执行路径,则称为单线程程序。
一个进程如果有多条执行路径,则称为多线程程序。
Java程序运行原理
java 命令会启动 java 虚拟机,启动 JVM,等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法。所以 main方法运行在主线程中。在此之前的所有程序都是单线程的。
java虚拟机是多线程的,因为除了主线程外,还有垃圾回收线程
多线程实现方案
方式1:继承Thread类
步骤
1、自定义类MyThread继承Thread类。
2、MyThread类里面重写run()
3、创建对象
4、启动线程
下面的代码:
/* * 该类要重写run()方法,为什么呢? * 不是类中的所有代码都需要被线程执行的。 * 而这个时候,为了区分哪些代码能够被线程执行,java提供了Thread类中的run()用来包含那些被线程执行的代码。 */ public class MyThread extends Thread { @Override public void run() { // 一般来说,被线程执行的代码肯定是比较耗时的。所以我们用循环改进 for (int x = 0; x < 300; x++) { System.out.println(x); } } } public class MyThreadDemo { public static void main(String[] args) { // 创建两个线程对象 MyThread my1 = new MyThread(); MyThread my2 = new MyThread(); my1.start(); my2.start(); } }
步骤:
1、自定义类MyRunnable实现Runnable接口
2、重写run()方法
3、创建MyRunnable类的对象
4、创建Thread类的对象,并把C步骤的对象作为构造参数传递
public class MyRunnable implements Runnable { @Override public void run() { for (int x = 0; x < 100; x++) { // 由于实现接口的方式就不能直接使用Thread类的方法了,但是可以间接的使用 System.out.println(Thread.currentThread().getName() + ":" + x); } } } /* * 方式2:实现Runnable接口 * 步骤: * A:自定义类MyRunnable实现Runnable接口 * B:重写run()方法 * C:创建MyRunnable类的对象 * D:创建Thread类的对象,并把C步骤的对象作为构造参数传递 */ public class MyRunnableDemo { public static void main(String[] args) { // 创建MyRunnable类的对象 MyRunnable my = new MyRunnable(); // 创建Thread类的对象,并把C步骤的对象作为构造参数传递 // Thread(Runnable target) Thread t1 = new Thread(my); Thread t2 = new Thread(my); t1.setName("zhangsan"); t2.setName("lisi"); // Thread(Runnable target, String name) Thread t1 = new Thread(my, "zhangsan"); Thread t2 = new Thread(my, "lisi"); t1.start(); t2.start(); } }
实现接口方式的好处:
1. 可以避免由于Java单继承带来的局限性。
2. 适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码,数据有效分离,较好的体现了面向对象的设计思想。
获取和设置线程名称
Thread类的基本获取和设置方法:
public final String getName()
public final void setName(String name)
其实通过构造方法也可以给线程起名字
如何获取main方法所在的线程名称呢?
public static Thread currentThread()
这样就可以获取任意方法所在的线程名称
示例代码如下:
public class MyThread extends Thread { public MyThread() { } public MyThread(String name){ super(name); } @Override public void run() { for (int x = 0; x < 100; x++) { System.out.println(getName() + ":" + x); } } } public class MyThreadDemo { public static void main(String[] args) { // 创建线程对象 //无参构造+setXxx() MyThread my1 = new MyThread(); MyThread my2 = new MyThread(); // 调用方法设置名称 my1.setName("zhangsan"); my2.setName("lisi"); my1.start(); my2.start(); //带参构造方法给线程起名字 MyThread my1 = new MyThread("zhangsan"); MyThread my2 = new MyThread("lisi"); my1.start(); my2.start(); //我要获取main方法所在的线程对象的名称,该怎么办呢? //public static Thread currentThread():返回当前正在执行的线程对象 System.out.println(Thread.currentThread().getName()); } }
线程调度
假如我们的计算机只有一个 CPU,那么 CPU 在某一个时刻只能执行一条指令,线程只有得到 CPU时间片,也就是使用权,才可以执行指令。那么Java是如何对线程进行调用的呢?
线程有两种调度模型:
- 分时调度模型 所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片
- 抢占式调度模型 优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。
Java使用的是抢占式调度模型。
public final int getPriority()
public final void setPriority(int newPriority)
示例代码如下:
public class ThreadPriority extends Thread { @Override public void run() { for (int x = 0; x < 100; x++) { System.out.println(getName() + ":" + x); } } } /* * public final int getPriority():返回线程对象的优先级 * 如何设置线程对象的优先级呢? * public final void setPriority(int newPriority):更改线程的优先级。 * * 注意: * 线程默认优先级是5。 * 线程优先级的范围是:1-10。 * 线程优先级高仅仅表示线程获取的 CPU时间片的几率高,但是要在次数比较多,或者多次运行的时候才能看到比较好的效果。 * * */ public class ThreadPriorityDemo { public static void main(String[] args) { ThreadPriority tp1 = new ThreadPriority(); ThreadPriority tp2 = new ThreadPriority(); ThreadPriority tp3 = new ThreadPriority(); tp1.setName("zhangsan"); tp2.setName("lisi"); tp3.setName("wangwu"); // 获取默认优先级 System.out.println(tp1.getPriority()); System.out.println(tp2.getPriority()); System.out.println(tp3.getPriority()); // 设置线程优先级 // tp1.setPriority(100000); //设置正确的线程优先级 tp1.setPriority(10); tp2.setPriority(1); tp1.start(); tp2.start(); tp3.start(); } }