一、基本概念
-
程序(program):是为了完成特定任务、使用某种语言编写的一组指令的集合——一段静态的代码,静态对象
-
进程(Process):一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程,进程也是程序的一次执行过程,是系统运行程序的基本单位,系统运行一个程序是一个进程从创建运行到消亡的过程。进程具有:独立性,动态性,并发性
- 独立性:进程时一个能独立运行得基本单位,同时也是系统分配资源和调度得独立单位
- 动态性:进程的实质时程序的一次执行过程,进程是动态产生,动态消亡的。
- 并发性:任何进程都可以同其他进程一起并发执行
- 进程作为资源分配的单元,系统在运行的时候会给每一个进程分配不同的内存区域
-
线程(Thread):进程中一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程,一个进程中是可以有多个线程的,这个应用程序也可以称为多线程程序
- 单线程:一个进程如果只有一条执行路径,则称为单线程程序
- 多线程:一个进程如果有多条执行路径,则称为多线程程序
- 线程作为调度和执行的单位,每个线程都拥有独立的运行栈和计数器(PC)
- 一个进程中的多个线程是共享 堆和方法区的。
-
并发:一个CPU(采用时间片)同时执行多个任务 比如:多个人做通一件事
-
并行:多个CPU同时直接多个任务 比如:多个人同时做不同的事情
-
单核CPU:一次只能执行一个线程,CPU按时间片执行,同一个时间片只能执行一个线程。时间片切换快,会造成和多线程一样执行的假象。实际是单线程,一个时间只执行一个线程。
-
多核CPU:多个CPU可同时执行多个线程。真正意义上一个时间片多线程同时执行。
二、线程
2.1 线程的创建和使用
- Java的多线程是通过java.lang.Thread类来体现的
- 创建线程有俩种方法
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口
2.1.1 Thread类
2.1.1.1 特性
- 每个线程都是通过某个特定Thread对象的run()方法来完成操作的,经常把run()方法的主体称为线程体
- 通过该Thread对象的start()方法来启动这个线程,run是赋值封装被线程执行的代码,直接调用相对于普通方法,并没有开启线程
- 开启线程的是start()方法,先启动线程,然后由JVM调用此线程的run()方法,线程开启不一定立即执行,由cpu调度执行
2.1.1.2 构造器
- Thread():创建新的Thread对象
- Thread(String threadname):创建线程并指定线程实例名
- Thread(Runnable target):指定创建线程的目标对象,它实现了Runnable接口中的run方法
- **Thread(Runnable target, String name):**创建新的Thread对象
2.1.1.3 Thread的常用方法
- void start(): 启动线程,并执行对象的run()方法
- run(): 线程在被调度时执行的操作
- String getName(): 返回线程的名称
- 注:线程由默认名字,格式:Thread-编号
- void setName(String name):设置该线程名称
- 通过构造方法也可以设置线程名字,需要再类中调用super
public class MyThread extends Thread {
public MyThread() {
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName() + "@@@" + i);
}
}
}
public class ThreadMethodDemo1 {
public static void main(String[] args) {
MyThread mt1 = new MyThread("小白");
MyThread mt2 = new MyThread("小衣");
mt1.start();
mt2.start();
}
}
- static Thread currentThread(): 返回当前线程。在Thread子类中就是this,通常用于主线程和Runnable实现类
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "=" +i);
}
}
}
public class ThreadMethodDemo1 {
public static void main(String[] args) {
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
mt1.start();
mt2.start();
}
}
- static void yield():暂停当前正在执行的线程对象,并执行其他线程
- 将线程从运行状态转为就绪状态
- 让cpu重新调度,礼让不一定成功!
public class TestYield {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield).start();
new Thread(myYield).start();
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程开始执行");
Thread.yield(); // 礼让
System.out.println(Thread.currentThread().getName() + "线程停止执行");
}
}
- public static void sleep(long time):让线程休眠指定的时间,单位为毫秒
- sleep(时间)指定当前线程阻塞的毫秒数;
- 抛出InterruptedException异常
- sleep时间借宿后线程重新进入到就绪状态
public class SleepDemo1 {
public static void main(String[] args) throws InterruptedException {
MyRunnable myRunnable = new MyRunnable();
Thread t1 = new Thread(myRunnable);
Thread t2 = new Thread(myRunnable);
t1.start();
t2.start();
}
}
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + "-" + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
- public final void join():等待这个线程死亡。 (可以理解成插队)
- join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
public class TestJoin implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("线程Vip来了");
}
}
public static void main(String[] args) throws InterruptedException {
TestJoin testJoin = new TestJoin();
Thread t1 = new Thread(testJoin);
t1.start();
for (int i = 0; i < 1000; i++) {
if (i==200){
t1.join(); // 插队
}
System.out.println("main" + i);
}
}
}
-
public static boolean interrupted():中断线程,不推荐使用
-
stop():强制线程生命期结束,不推荐使用
- 推荐让线程自动停止
-
boolean isAlive():返回boolean,判断线程是否还活着
2.1.1.4 创建线程——Thread
-
定义一个类继承Thread
-
在类中重写run()方法
-
创建对象
-
调用start()方法启动线程,由线程调用run方法
public class Demo1 { public static void main(String[] args) { // 创建线程对象 MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); // 开启线程 t1.start(); t2.start(); } } public class MyThread extends Thread { @Override public void run(){ // 代码就是线程在开启之后执行的代码 for (int i = 0; i < 100; i++) { System.out.println("线程启动了" + i); } } }
注意事项
- run是赋值封装被线程执行的代码,直接调用相对于普通方法,并没有开启线程
- 开启线程的是start()方法:1.先启动线程,2.然后由JVM调用此线程的run()方法
- 线程开启不一定立即执行,由cpu调度执行
- 一个线程对象只能调用一次start()方法启动,如果重复调用了,则将抛出以上的异常“IllegalThreadStateException”。
2.1.1.5 创建线程——Runnable接口
-
定义一个类RunnableTest实现Runnable接口
-
在类中重写run()方法
-
创建RunnableTest对象
-
创建Thread类的对象,把RunnableTest对象作为构造方法的参数
-
调用start()方法启动线程
public class MyRunnable implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(i); } } } public class RunnableDemo1 { public static void main(String[] args) { // 创建了一个参数的对象 MyRunnable myRunnable = new MyRunnable(); // 创建线程对象,把参数传递进去 // 线程启动后调用的是参数的run方法 Thread thread = new Thread(myRunnable); thread.start(); for (int i = 0; i < 1000; i++) { System.out.println("main" + i); } } }
2.1.1.6 创建线程——Callable和Future接口
- 定义一个类MyCallable实现Callable接口
- 在MyCallable类中重写call()方法
- 创建Mycallable类的对象
- 创建Future的来实现类FutureTask对象,把MyCallable对象作为构造方法的参数
- 创建Thread类的对象,把FutureTask对象作为构造方法的参数
- 启动线程
Future接口
- 可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。
- FutrueTask是Futrue接口的唯一的实现类
- FutureTask 同时实现了Runnable, Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
// 返回指表示线程运行完毕之后的结果
return "答应";
}
}
public class CallableDemo1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 线程开启之后需要执行里面的call方法
MyCallable mc = new MyCallable();
// 可以获取线程完毕之后的结果,也可以作为参数传递给Thread对象
FutureTask<String> ft = new FutureTask<>(mc);
// 创建线程对象
Thread t1 = new Thread(ft);
// 开启线程
t1.start();
String s = ft.get();
System.out.println(s);
}
}
注意事项:
- FutureTask中的get方法要字线程启动之后才能调用,get可以获取线程执行后的结果
2.1.1.7 三种方式
区别:
- 继承Thread:线程代码存放Thread子类run方法中。
- 实现Runnable:线程代码存在接口的子类的run方法。
- 实现Callable接口:线程代码存在接口的子类的call方法。
实现的好处好处:
- 避免了单继承的局限性
- 多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源。
Runnable与Callable相比较:
- 相比run()方法,可以有返回值
- 方法可以抛出异常
- 支持泛型的返回值
- 需要借助FutureTask类,比如获取返回结果
2.1.2 线程调度
多线程的并发运行:计算机的CPU,再任意时刻只能执行一条机器指令。每个线程只有获取CPU的使用权才能执行代码,各个线程轮流获得CPU的试用期,分别执行各自的任务。
调度模式
- 分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片
- 抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取CPU时间片相对多一些
- 时间片策略:同优先级线程组成先进先出队列(先到先服务)
2.1.2.1 线程的优先级
**优先级范围:1~10,默认值是:5,优先级越高,抢到时间片几率越高 **
- MAX_PRIORITY:10
- MIN _PRIORITY:1
- NORM_PRIORITY:5
优先级的方法:
public final void setPriority(int newPriority)
:设置线程的优先级public final int getPriority()
:获取线程的优先级z
注意:
- 线程创建时继承父线程的优先级
- 低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用
- 线程优先级的设置最好再 start() 之前。
package state;
// 测试线程的优先级
public class TestPriority {
public static void main(String[] args) {
// 主线默认优先级
System.out.println(Thread.currentThread().getName() + "---->" + Thread.currentThread().getPriority());
MyPriority myPriority = new MyPriority();
Thread thread = new Thread(myPriority);
Thread thread1 = new Thread(myPriority);
Thread thread2 = new Thread(myPriority);
Thread thread3 = new Thread(myPriority);
// 先设置优先级
thread.start();
thread1.setPriority(1);
thread1.start();
thread2.setPriority(4);
thread2.start();
thread3.setPriority(Thread.MAX_PRIORITY); // MAX_PRIORITY=10
thread3.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "---->" + Thread.currentThread().getPriority());
}
}