并发与并行
- 并发:指两个或多个事件在同一时间段内发生。
- 并行:指两个或多个事件在同一时刻发生(同时发生)。
进程与线程
-
进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。
-
线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
-
简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程。
线程调度
- 分时调度:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间。
- 抢占式调度:优先让优先级高的銭程使用CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
Java中的主线程
主线程:执行主(main)方法的线程。
单线程
单线程:Java程序中,只有一个线程,执行从main方法开始,从上到下的执行。
举例:
public class Student {
public static void method1() {
for (int i = 0; i < 5; i++) {
System.out.println("我正在执行Student的方法1");
}
}
public static void method2() {
for (int i = 0; i < 5; i++) {
System.out.println("我正在执行Student的方法2");
}
}
public Student() {
}
}
public class Demo01MainThread {
public static void main(String[] args) {
Student.method1();
Student.method2();
}
}
控制台输出:
我正在执行Student的方法1
我正在执行Student的方法1
我正在执行Student的方法1
我正在执行Student的方法1
我正在执行Student的方法1
我正在执行Student的方法2
我正在执行Student的方法2
我正在执行Student的方法2
我正在执行Student的方法2
我正在执行Student的方法2
从控制台输出可以知道,程序从上到下的执行,从方法1开始执行,到方法2开始执行。
多线程
创建多线程程序的第一种方式
创建Thread类的子类
java.lang.Thread类:是描述线程的类,我们想要实现多线程程序,就必须继承Thread类
实现步骤:
- 创建一个 Thread类的子类。
- 在Thread类的子类中重写Thread类中的run方法,设置线程任务(开启线程要做什么)。
- 创建Thread类的子类对象。
- 调用Thread类中的start方法,开启新的线程,执行run方法
void start()使该线程开始执行。Java虚拟机调用该线程的run方法。结果是两个线程并发地运行;当前线程(main线程)和另一个线程(创建的新线程,执行其run方法)。多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。Java程序属于抢占式调度,哪个线程的优先级高,哪个线程优先执行;同一个优先级,随机选择一个执行。
-
举例
public class Person extends Thread { /** * 重写Thread类的run方法,设置任务 */ @Override public void run() { for (int i = 0; i < 6; i++) { System.out.println("我正在执行重写的run方法中的线程:" + i); } } }
public class Demo02MainThread { public static void main(String[] args) { // 创建Thread的子类对象 Thread myThread = new Person(); // 调用Thread类中的start方法,开启新的线程,执行run方法 myThread.start(); // main方法中的要执行的线程 for (int i = 0; i < 6; i++) { System.out.println("我正在执行main方法的线程:" + i); } } }
-
控制台输出
我正在执行main方法的线程:0 我正在执行main方法的线程:1 我正在执行重写的run方法中的线程:0 我正在执行main方法的线程:2 我正在执行重写的run方法中的线程:1 我正在执行重写的run方法中的线程:2 我正在执行重写的run方法中的线程:3 我正在执行重写的run方法中的线程:4 我正在执行重写的run方法中的线程:5 我正在执行main方法的线程:3 我正在执行main方法的线程:4 我正在执行main方法的线程:5
多线程的原理
Thread类的常用方法
public String getName()
// 获取当前线程名称。
public void start()
// 导致此线程开始执行;Java虚拟机调用此线程的run方法。
public void run()
// 此线程要执行的任务在此处定义代码。
public static void sleep(long millis
// 返回对当前正在执行的线程对象的引用。
getName()方法、start()方法、run()方法
public class MyThread extends Thread {
@Override
public void run() {
// 此线程要执行的任务
}
}
public class Demo01MyThread {
public static void main(String[] args) {
MyThread myThread1 = new MyThread();
myThread1.start();
String run1Name = myThread1.getName();
MyThread myThread2 = new MyThread();
myThread2.start();
String run2Name = myThread2.getName();
MyThread myThread3 = new MyThread();
myThread3.start();
String run3Name = myThread3.getName();
System.out.println("线程1名称:" + run1Name);
System.out.println("线程2名称:" + run2Name);
System.out.println("线程3名称:" + run3Name);
}
}
控制台输出:
线程1名称:Thread-0
线程2名称:Thread-1
线程3名称:Thread-2
currentThread()方法
public class MyThread extends Thread {
@Override
public void run() {
// 此线程要执行的任务
System.out.println(Thread.currentThread());
}
}
public class Demo02MyThread {
public static void main(String[] args) {
MyThread myThread1 = new MyThread();
myThread1.start();
MyThread myThread2 = new MyThread();
myThread2.start();
MyThread myThread3 = new MyThread();
myThread3.start();
}
}
控制台输出:
Thread[Thread-0,5,main]
Thread[Thread-2,5,main]
Thread[Thread-1,5,main]
了解一下setName()方法
setName():设置线程的名称
public class Demo03MyThread {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.setName("李华");
myThread.start();
}
}
控制台输出:
Thread[李华,5,main]
sleep()方法
public class MyThread2 extends Thread {
@Override
public void run() {
// 此线程要执行的任务
for (int i = 0; i < 11; i++) {
try {
// 参数为毫秒
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print("第" + i + "秒 ");
}
}
}
public class Demo01MyThread2 {
public static void main(String[] args) {
MyThread2 thread2 = new MyThread2();
thread2.start();
}
}
控制台输出:
第0秒 第1秒 第2秒 第3秒 第4秒 第5秒 第6秒 第7秒 第8秒 第9秒 第10秒
创建多线程的第二种方式
实现Runnable接口,来创建多线程。
实现步骤
- 创建一个 Runnable接口的实现类。
- 在实现类中重写 Runnable接口的run方法,设置线程任务。
- 创建一个 Runnable接口的实现类对象。
- 创建Thread类对象,构造方法中传递 Runnable接口的实现类对象。
- 调用 Threads类中的 start方法,开启新的线程执行run方法
举例
// 创建一个 Runnable接口的实现类。
public class MyRunnable implements Runnable {
@Override
public void run() {
// 在实现类中重写 Runnable接口的run方法,设置线程任务。
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + " ~~~~~~~~~ " + i);
}
}
}
public class DemoRunnable {
public static void main(String[] args) {
// 创建一个 Runnable接口的实现类对象。
MyRunnable myRunnable = new MyRunnable();
// 创建Thread类对象,构造方法中传递 Runnable接口的实现类对象。
Thread thread = new Thread(myRunnable);
// 调用 Threads类中的 start方法,开启新的线程执行run方法
thread.start();
// main方法线程
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + " ~~~~~~~~~ " + i);
}
}
}
控制台输出:
main ~~~~~~~~~ 0
Thread-0 ~~~~~~~~~ 0
main ~~~~~~~~~ 1
Thread-0 ~~~~~~~~~ 1
Thread-0 ~~~~~~~~~ 2
main ~~~~~~~~~ 2
使用Runnable相比Thread有哪些优势
- 适合多个相同的程序代码的线程去共享同一个资源。
- 可以避兔Java中的单继承的局限性。
- 増加程序的健壮性,实现解耦(把设置线程任务和开启线程分开)操作,代码可以被多个线程共享,代码和线程独立。
- 线程池只能放入实现 Runable或 Callable类线程,不能直接放入继承 Thread的类。
内部类实现创建多线程
public class MyRunnable implements Runnable {
@Override
public void run() {
// 在实现类中重写 Runnable接口的run方法,设置线程任务。
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + " ~~~~~~~~~ " + i);
}
}
}
public class DemoSimpleThread {
public static void main(String[] args) {
// 创建一个 Runnable接口的实现类对象。
MyRunnable myRunnable = new MyRunnable();
new Thread(myRunnable) {
@Override
public void run() {
System.out.print("匿名内部类的方式实现多线程的创建:");
System.out.println(Thread.currentThread().getName() + "线程");
}
}.start();
// main线程
System.out.println("main线程");
}
}
控制台输出:
main线程
匿名内部类的方式实现多线程的创建:Thread-0线程