1.1 并发与并行
- 并发:指两个或多个事件在同一个时间段内发生
- 并行:指两个或多个事件在同一时刻发生(同时发生)

在操作系统中,安装了多个程序,并发指的是在一段时间内宏观上有多个程序同时运行,这在单 CPU 系统中,每一时刻只能有一道程序执行,即微观上这些程序是分时的交替运行,只不过是给人的感觉是同时运行,那是因为分时交替运行的时间是非常短的。
而在多个 CPU 系统中,则这些可以并发执行的程序便可以分配到多个处理器上(CPU),实现多任务并行执行,即利用每个处理器来处理一个可以并发执行的程序,这样多个程序便可以同时执行。目前电脑市场上说的多核CPU,便是多核处理器,核 越多,并行处理的程序越多,能大大的提高电脑运行的效率。
注意:单核处理器的计算机肯定是不能并行的处理多个任务的,只能是多个任务在单个CPU上并发运行。同理,线程也是一样的,从宏观角度上理解线程是并行运行的,但是从微观角度上分析却是串行运行的,即一个线程一个线程的去运行,当系统只有一个CPU时,线程会以某种顺序执行多个线程,我们把这种情况称之为线程调度。
1.2 线程与进程
- 进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。
- 线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程

打开任务管理器,第一个呈现的即为进程,在详细信息里可以设置进程的优先级。
线程调度
- 分时调度
所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。 - 抢占式调度
优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为
抢占式调度。
1.3 创建线程方法
Java 虚拟机允许应用程序并发地运行多个执行线程。
每个线程都有一个优先级,高优先级线程的执行优先于低优先级线程。每个线程都可以或不可以标记为一个守护程序。当某个线程中运行的代码创建一个新 Thread 对象时,该新线程的初始优先级被设定为创建线程的优先级,并且当且仅当创建线程是守护线程时,新线程才是守护程序。
当 Java 虚拟机启动时,通常都会有单个非守护线程(它通常会调用某个指定类的 main 方法)。Java 虚拟机会继续执行线程,直到下列任一情况出现时为止:
- 调用了 Runtime 类的 exit 方法,并且安全管理器允许退出操作发生。
- 非守护线程的所有线程都已停止运行,无论是通过从对 run 方法的调用中返回,还是通过抛出一个传播到 run 方法之外的异常。
创建新执行线程有两种方法。一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。接下来可以分配并启动该子类的实例。创建线程的另一种方法是声明实现 Runnable 接口的类。该类然后实现 run 方法。然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动。
1.3.1 创建线程方式一:继承Thread类
Java中通过继承Thread类来创建并启动多线程的步骤如下:
- 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把
run()方法称为线程执行体。 - 创建Thread子类的实例,即创建了线程对象
- 调用线程对象的start()方法来启动该线程
自定义线程类:
public class MyThread extends Thread{
// 定义指定线程的构造方法
public MyThread(String name) {
super(name); // 调用父类的String参数的构造方法,指定线程的名字
}
/*
重写run方法,完成该线程执行的逻辑
*/
@Override
public void run(){
for (int i = 0; i < 10; i++) {
System.out.println(getName() + ":正在执行!" + i);
}
}
}
测试类:
/*
创建多线程程序的第一种方式:创建Thread的子类
java.lang.Thread类:是描述线程的类,我们想要实现多线程程序,就必须继承Thread类
实现步骤:
1. 创建一个Thread类的子类
2. 在Thread类的子类中重写Thread类的run方法,设置线程任务(开启线程要做什么)
3. 创建Thread类的子类对象
4. 调用Thread类中的方法start方法,开启新的线程,执行run方法
void start() 使该线程开始执行;Java虚拟机调用该线程的run方法
结果是两个线程并发地运行;当前线程(main线程)和另一个线程(创建的新线程,执行其run方法)
*/
public class _01Thread {
public static void main(String[] args) {
// MyThread mt = new MyThread("新的线程");
// // 开启新线程
// mt.start();
//
// for (int i = 0; i < 10; i++) {
// System.out.println("main线程!" + i );
// }
new ThreadGetName().start();
new ThreadGetName().start();
System.out.println(Thread.currentThread().getName());
new ThreadSetName("Asia").start();
}
}
1.3.2 创建线程方式二:实现Runnable接口
采用Java.lang.Runnable接口,重写run方法。
步骤如下:
- 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
- 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正
的线程对象。 - 调用线程对象的start()方法来启动线程
// 创建一个Runnable接口的实现类
public class RunnableImpl implements Runnable{
@Override
public void run(){ // 设置线程任务
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + "---->" + i);
}
}
}
/*
创建多线程程序的第二种方式:实现Runnable接口
java.lang.Runnable
Runnable 接口应该由那些打算通过某一线程执行其实例的类来实现。类必须定义一个称为run的无参数方法。
java.lang.Thread类的构造方法
Thread(Runnable target) 分配新的构造方法
Thread(Runnable target, String name) 分配新的Thread对象
实现步骤:
1. 创建一个Runnable接口的实现类
2. 在实现类中重写Runnable接口的run方法,设置线程任务
3. 创建一个Runnable接口的实现类对象
4. 创建Thread类对象,构造方法中传递Runnable接口的实现类对象
5. 调用Thread类中的start方法,开启新的线程执行run方法
实现Runnable接口创建多线程程序的好处:
1. 避免了单线程的局限性
一个类只能继承一个类(一个人只能有一个亲爹),类继承了Thread类就不能继承其他的类
实现了Runnable接口,还可以继承其他的类,实现其他的接口
2. 增强了程序的扩展性,降低了程序的耦合性(解耦)
实现Runnable接口的方式,把设置线程任务和开启新线程进行了分离(解耦)
实现类中,重写了run方法:用来设置线程任务
创建Thread类对象,调用start方法:用来开启新线程
1. 适合多个相同的程序代码的线程去共享同一个资源。
2. 可以避免java中的单继承的局限性。
3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
4. 线程池只能放入实现Runnable或Callable类线程,不能直接放入继承Thread的类。
*/
public class _03Runnable {
public static void main(String[] args) {
RunnableImpl mt = new RunnableImpl();
Thread t = new Thread(mt);
t.start();
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + "--->" + i);
}
}
}
1.3.3 Thread和Runnable的区别
如果一个类继承Thread,则不适合资源共享。但是实现了Runnable接口的话,则很容易的实现资源共享。
实现Runnable接口比继承Thread类所具有的优势:
- 适合多个相同的程序代码的线程去共享同一个资源。
- 可以避免java中的单继承的局限性。
- 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
- 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
1.3.4 匿名内部类实现线程的创建
使用线程的内匿名内部类方式,可以方便的实现每个线程执行不同的线程任务操作。
使用匿名内部类的方式实现Runnable接口,重新Runnable接口中的run方法。
/*
匿名内部类方式实现线程的创建
匿名:没有名字
内部类:写在其他类内部的类
匿名内部类作用:简化代码
把子类继承父类,重写父类的方法,创建子类对象合一步完成
把实现类实现类接口,重写接口中的方法,创建实现类对象合成一步完成
匿名内部类的最终产物:子类/实现类对象,而这个类没有名字
格式:
new 父类/接口(){
重复父类/接口中的方法
};
*/
public class _04InnerClassThread {
public static void main(String[] args) {
// 线程的父类是Thread
// new MyTHread().start();
new Thread(){
// 重写run方法,设置线程任务
@Override
public void run(){
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + "--->" + i);
}
}
}.start();
// 线程的接口Runnable
// Runnable r = new RunnableImpl(); // 多态
Runnable r = new Runnable(){
@Override
public void run(){
for (int i = 0; i < 20; i++) {
System.out.println("Asia" + "--->" + i);
}
}
};
Thread t = new Thread(r);
t.start();
// 简化接口的方式
new Thread(new Runnable(){
@Override
public void run(){
for (int i = 0; i < 20; i++) {
System.out.println("Free");
}
}
}).start();
}
}


被折叠的 条评论
为什么被折叠?



