目录
一.线程的概念
1.什么是线程
2.引入多线程的目的
3.了解线程和进程的区别
4.了解多线程的执行
二.学习使用Java来创建线程
1.学习创建线程的几种方法
(1)通过继承Thread类来创建线程
(2)实现Runnable接口来创建线程
(3)使用匿名内部类来创建线程
(4)使用lambda表达式创建线程
三.了解Thread类的常见方法即如何使用
1.Thread类的常见构造方法
2.Thread常见的属性
3.线程的启动
4.线程的中断
(1)中断线程的2种方法
(2)使用自定义标记来通知线程结束
(3)使用interrupt()方法来通知线程结束
?(4)有关Thread.interrupted()和Thread.currentThread().isInterrupted()的区别
2.Thread.currentThread().isInterrupted()
5.线程的等待和休眠(join(),sleep()方法的使用)
(1)为什么需要线程进行等待
(2)线程等待的方法
(3)线程等待实例
?(4)线程休眠(sleep())
6.观察线程的所有状态
7.yield()礼让线程
一.线程的概念
1.什么是线程
一个线程就是一个 "执行流". 每个线程之间都可以按照顺讯执行自己的代码. 多个线程之间 "同时" 执行着多份代码。
2.引入多线程的目的
为了并发编程,而且线程比进程轻量;线程的创建,销毁,调度比进程创建,销毁,调度快。
3.了解线程和进程的区别
(1)其中进程是包含线程的,每个进程至少有一个线程,是主线程。
(2)进程与进程之间不共享内存空间,进程中的线程之间共享内存空间。
(3)进程是系统分配资源的最小单位,线程是系统调度的最小单位。
4.了解多线程的执行
如果对于一个从0加到1亿这个简单的操作来说,使用之前的main方法来执行,假设这里的执行时间为T,如果使用多线程来并发执行,假设我们这里创建了10个线程,那么相当于是每个线程只需要执行1千万的加法操作即可,大概效率提高了10倍,这就是使用多线程的好处,目的当然是为了提高程序的执行效率。
二.学习使用Java来创建线程
1.学习创建线程的几种方法
这里在创建线时需要注意:首先需要重写run方法,之后启动线程通过调用start方法来进行启动
(1)通过继承Thread类来创建线程
运行结果:
?代码:
package thread.example;
public class CreatThread1 extends Thread {
@Override
public void run() {
System.out.println("线程执行代码");
}
public static void main(String[] args) {
CreatThread1 thread = new CreatThread1();
thread.start();
}
}
(2)实现Runnable接口来创建线程
注意:在实现该接口,需要重写run方法,同时还需要借助Thread来进行启动。
执行结果:
?代码:
package thread.example;
public class CreateThread2 implements Runnable {
@Override
public void run() {
System.out.println("创建的线程执行");
}
public static void main(String[] args) {
Thread thread = new Thread(new CreateThread2());
thread.start();
}
}
(3)使用匿名内部类来创建线程
运行结果:
?代码:
package thread.example;
public class CreateThread {
public static void main(String[] args) {
//使用匿名内部类来创建线程
//直接创建Thread子类对象
Thread thread = new Thread() {
@Override
public void run() {
System.out.println("Thread创建的线程");
}
};
thread.start();
//创建Runnable子类对象
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Runnable创建的线程");
}
});
thread2.start();
}
}
(4)使用lambda表达式创建线程
运行结果:
?代码:
package thread.example;
public class CreateThread3 {
public static void main(String[] args) {
//使用lambda表达式创建线程
Thread thread = new Thread(()->{
System.out.println("lambda表达式创建线程");
});
thread.start();
}
}
三.了解Thread类的常见方法即如何使用
1.Thread类的常见构造方法
方法 | 说明 | Thread()? | 创建线程对象 | Thread(Runnable target) | 使用 Runnable 对象创建线程对象 | Thread(String name) | 创建线程对象,并命名 | Thread(Runnable target, String name) | ?使用 Runnable 对象创建线程对象,并命名 |
2.Thread常见的属性
属性 | 获取方法 | ID | getId() | 名称 | getName() | 状态 | getState() | 优先级 | getPriority() | 是否为后台线程 | isDaemon() | 是否存活 | isAlive() | 是否被中断 | isInterrupted() |
属性的用途了解:
- ID 是线程的唯一标识,不同线程不会重复。
- 名称是各种调试工具会用到。
- 状态表示线程当前所处的一个情况。
- 优先级高的线程理论上来说更容易被调度到。
- 关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行。
- 是否存活,即简单的理解,为 run 方法是否运行结束。
- 线程的中断,也就是被阻塞到中断那里等待,直到得到执行权继续执行。
?
3.线程的启动
创建一个线程是通过重写Thread中的run方法来实现,其中的run方法重写后相当于是线程已经准备就绪,只有调用start方法后操作系统才会在底层创建出一个线程,之后才会真正的执行起来。
4.线程的中断
(1)中断线程的2种方法
- 通过共享标记来进行通知(自定义变量作为标记需要使用volatile修饰)
- 通过interrupt()方法进行通知
(2)使用自定义标记来通知线程结束
package thread.example;
public class InterruptThread {
public static volatile boolean flag = false;//设置标记来中断线程
public static void main(String[] args) throws InterruptedException {
//这里使用匿名类来创建线程
Thread thread1 = new Thread(){
@Override
public void run() {
while (!flag) {
System.out.println("线程正在执行");
try {
Thread.sleep(10*100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程执行结束");
}
};
thread1.start();
//通过sleep方法来阻塞另一个线程
Thread.sleep(10*1000);
System.out.println("通知thread1结束");
flag = true;
}
}
(3)使用interrupt()方法来通知线程结束
可以通过使用Thread.interrupted() 或者 Thread.currentThread().isInterrupted() 代替自定义标志位。因为Thread内部也是通过变量来作为线程中断的标志。
有关该方法的使用如下:
方法 | 说明 | public void interrupt() | ?中断对象关联的线程,如果线程正在阻塞,则以异常方式通知,否则设置标志位 | public static boolean interrupted() | ?判断当前线程的中断标志位是否设置,调用后清除标志位 | public boolean isInterrupted() | ?判断对象关联的线程的标志位是否设置,调用后不清除标志位 |
package thread.example;
public class InterruptThread2 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(){
@Override
public void run() {
//两种方法都可以
// while (!Thread.interrupted()) {
while (!Thread.currentThread().isInterrupted()){
System.out.println("thread执行");
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
break;//跳出循环
}
}
System.out.println("thread结束");
}
};
thread.start();
Thread.sleep(1000);
System.out.println("线程结束");
thread.interrupt();
}
}
?(4)有关Thread.interrupted()和Thread.currentThread().isInterrupted()的区别
1.Thread.interrupted()
会清除标记位,如下所示:
package thread.example;
public class InterruptThread2 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(){
@Override
public void run() {
//两种方法都可以
// while (!Thread.interrupted()) {
while (!Thread.currentThread().isInterrupted()){
System.out.println("thread执行");
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
break;//跳出循环
}
}
System.out.println("thread结束");
}
};
thread.start();
Thread.sleep(1000);
System.out.println("线程结束");
thread.interrupt();
}
}
2.Thread.currentThread().isInterrupted()
不会清除标记位,如下所示:
package thread.example;
public class InterruptThread3 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
for (int i=0; i<10; i++) {
//清除标记位
System.out.println(Thread.currentThread().isInterrupted());
}
});
thread.start();
thread.interrupt();//相当于是将标记为设为true
}
}
注意:如果使用使用这两种通知方式的时候,如果线程里面包含wait/join/sleep 等方法而阻塞挂起,这时候就会抛出一个异常,从而都会清除标记位,这时候有2种选择,一个是忽略该异常,继续执行;另一个是跳出该异常,结束循环。
5.线程的等待和休眠(join(),sleep()方法的使用)
(1)为什么需要线程进行等待
因为多线程是抢占式随机执行,但是有时候我们需要上一个线程的结果才能执行到下一个线程,这时候就需要线程进行等待,等到上一个执行结束再执行下一个线程。
(2)线程等待的方法
方法 | 解释 | public void join() | ?等待线程结束 | public void join(long millis) | ?等待线程结束,最多等 millis 毫秒 | public void join(long millis, int nanos) | ?同理,但可以更高精度 |
(3)线程等待实例
package thread.example;
public class WaitThread {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(){
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("thread1执行");
}
}
};
Thread thread2 = new Thread(){
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("thread2执行");
}
}
};
thread1.start();
thread1.join();
thread2.start();
}
}
?(4)线程休眠(sleep())
线程休眠也相当于是阻塞,只不过,这个阻塞是有时间限制的,这个时间可以进行设置,一般休眠时间大于设置的时间。
可以通过Thread.sleep()直接进行调用。
6.观察线程的所有状态
- NEW: 安排了工作, 还未开始行动。
- RUNNABLE: 可工作的. 又可以分成正在工作中和即将开始工作。
- BLOCKED: 表示等待获取锁,相当于排队等着其他事情。
- WAITING: 表示等待其他线程发来通知,如果没有通知,相当于是死等,相当于排队等着其他事情。
- TIMED_WAITING: 表示等待其他线程发来通知,而且这个等待也有时间限制,相当于排队等着其他事情。
- TERMINATED: 工作完成了。
7.yield()礼让线程
使用该方法相当于是将当前自己所持有的cpu让出来然后自己重新排队等待。
|