目录
多线程是提升程序性能的一种非常重要的方式,使用多线程可以让程序充分利用CPU的资源,提高CPU的使用效率,从而解决高并发带来的负载均衡问题。
1、线程的概念
每个运行的程序都是一个进程,在一个进程中还可以有多个执行单元同时运行,这些执行单元就可以看作程序执行的一条条线索,称为线程。
操作系统中每一个进程中至少存在一个线程。
例如,当一个Java程序启动时,就会产生一个进程,该进程中会默认创建一个线程,在这个线程上会运行main()方法中的代码。
一个进程可以根据需要创建多个线程,从而实现”并发编程“的效果。
1.1 线程的注意事项
1、一个进程中是可以有多个线程的,每个线程都是可以独立进行调度的。
2、一个进程可以使用一个PCB表示,一个进程也可能使用多个PCB表示,每个PCB对应到每个线程上。
3、同一个进程的多个线程之间,共用一份内存空间和文件资源。
1.2 进程和线程的区别
1、进程包含线程,一个进程里面可以有多个线程。
2、进程和线程都是用来实现并发编程场景的,但是线程比进程更高效。
3、同一个进程的线程之间,共用一份的资源,省去了申请资源的开销。
4、进程和进程之间,具有独立性,不会相互影响。线程和线程之间,是可能会相互影响的。
5、进程是资源分配的基本单位,线程是调度执行的基本单位。
2、线程的创建
线程的创建主要有五种方式:
1、继承Thread,重写run
2、实现Runnable,重写run
3、继承Thread,重写run,使用匿名内部类
4、实现Runnable,重写run,使用匿名内部类
5、使用lambda
2.1 继承Thread创建
1、使用Thread这个类,可以创建Thread对象,进一步就可以创建操作系统内部的线程。
2、start和run都是Thread的成员,run只是描述了线程的入口(线程该做什么任务),start则是真正的调用了系统的API,在系统中创建了线程,再调用run。
3、在多线程中,main()方法和MyThread类的run()方法可以同时运行,互不影响。
4、可以使用sleep()方法来降低线程运行的速度。
class MyThread extends Thread{
@Override
public void run() {
while (true) {
System.out.println("hello Thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class demo {
public static void main(String[] args) {
Thread t=new MyThread();
t.start();
while (true){
System.out.println("hello main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2.2 实现Runnable创建
通过继承Thread类实现多线程,具有一定的局限性。因为Java只支持单继承,一个类一旦继承了某个父类就无法再继承Thread类,就无法通过继承Thread类创建线程。
Thread类提供了另一个构造方法Thread(Runnable target),其中Runnable是一个接口,它只有run()方法。当通过Thread(Runnable target)构造方法创建线程对象时,只需要为该方法传递一个实现了Runnable接口的实例对象,就可以调用Runnable接口中的run()方法作为运行代码。
class MyRunnable implements Runnable{
@Override
public void run() {
while (true){
System.out.println("hello Runnable");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class demo{
public static void main(String[] args) {
Runnable runnable=new MyRunnable();
Thread t=new Thread(runnable);
t.start();
while (true) {
System.out.println("hello main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2.3 使用匿名内部类创建
使用匿名内部类创建就是将继承的Thread类和实现的Runnable类,写成一个匿名内部类,在一个类中创建。
继承Thread,重写run
public class demo {
public static void main(String[] args) {
Thread t=new Thread(){
public void run() {
while (true) {
System.out.println("hello Thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t.start();
while (true){
System.out.println("hello main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
实现Runnable,重写run
public class demo{
public static void main(String[] args) {
Runnable runnable=new Runnable(){
public void run() {
while (true){
System.out.println("hello Runnable");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread t=new Thread(runnable);
t.start();
while (true) {
System.out.println("hello main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2.4 使用lambda表达式
lambda表达式,本质上是一个匿名函数,主要实现”回调函数“的效果,让语法表达更加简化。
public class demo {
public static void main(String[] args) {
Thread t=new Thread(()-> {
while (true){
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
while (true){
System.out.println("hello main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3、线程启动
public class demo {
public static boolean isQuit=false;
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(()->{
while (!isQuit){
System.out.println("线程在工作中");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
Thread.sleep(5000);
isQuit=true;
System.out.println("线程工作结束");
}
}
4、线程终止
Thread.currentThread()方法可以获取当前线程的实例
isInterrupted()方法调用Thread内部的标志位,根据这个标志位就可以判断线程是否结束
t.interrupt()方法把Thread对象内部的标准位设置为true
public class demo {
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(()->{
while (!Thread.currentThread().isInterrupted()){
System.out.println("线程工作中");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
Thread.sleep(1000);
System.out.println("让t线程中止");
t.interrupt();
}
}
5、线程等待
让一个线程等待另一个线程执行结束,再继续执行,本质上就是控制线程的顺序。
在主线程中调用t.join(),就是让主线程等待t线程先结束。
public class demo {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("线程工作中");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
System.out.println("join等待开始");
t.join();
System.out.println("join等待结束");
}
}
t.join()的工作原理
1、如果t线程正在运行中,此时调用join的线程就会堵塞,一直阻塞到t线程执行结束为止
2、如果t线程已经执行结束了,此时调用join的线程,就会直接返回,不会涉及到堵塞