Java多线程详解(1)
一、认识线程
1.1、什么是进程
进程是操作系统对一个正在运行的程序的一种抽象,换言之,可以把进程看做程序的一次运行过程;同时,在操作系统内部,进程又是操作系统进行资源分配的基本单位。
1.2、什么是线程
一个线程就是一个“执行流”,每个线程之间都可以按照顺序执行自己的代码,多个线程之间“同时”执行着多份代码。
1.3、为什么要有线程
(1)“并发编程”成为“刚需”
1、要想提升算力,就需要多核CPU,而并发编程更能充分利用多核CPU资源。
2、为了让等待IO的时间能去做其他事情,也需要并发编程
(2)线程比进程更轻量
1、创建线程比创建进程更快
2、销毁线程比销毁进程更快
3、调度线程比调度进程更快
1.4、进程和线程的区别
1、进程中包含线程,每一个进程都至少有一个线程
2、进程是申请系统资源的最小单位
3、线程是系统调度的最小单位
4、线程之间共享进程申请的系统资源
5、一个线程如果崩溃,就会影响整个进程
二、线程的创建方式
(1)继承Thread类
继承Thread来创建一个线程类
class MyThread extends Thread{@Overridepublic void run() {System.out.println("创建线程方法1...");}
}
创建MyThread类的实例
MyThread t1=new MyThread();
调用start()方法启动线程
t1.start();
(2)实现Runnable接口
实现Runnable接口
class MyRunnable implements Runnable{@Overridepublic void run() {System.out.println("创建线程方法2...");}
}
创建Thread类实例,调用Thread的构造方法时将Runnable对象作为参数
Thread t2=new Thread(new MyRunnable());
调用start()方法
t2.start();
(3)使用匿名内部类创建Thread子类对象
Thread t3=new Thread(){@Overridepublic void run() {System.out.println("创建线程方法3...");}
};
(4)匿名内部类创建Runnable子类对象
Thread t4=new Thread(new Runnable() {@Overridepublic void run() {System.out.println("创建线程方法4...");}
});
(5)lambda表达式
Thread t5 = new Thread(()->{System.out.println("创建线程方法5...");
});
三、多线程的优势
多线程的优势:增加运行速度
示例:分别使用func1()和func2()计算对a和b累加和累减1000000000次的时间,其中func1()串行累加,func2()使用线程并行累减,比较串行和并行的时间
public class Test01 {private static long count=1000000000;private static long a=0;private static long b=0;public static void main(String[] args) throws InterruptedException {func1();func2();}//串行private static void func1(){//记录func1()开始运行的时间long begin=System.currentTimeMillis();//对a累加for(long i=0;i<count;i++){a++;}//对b累加for(long i=0;i<count;i++){b++;}//记录func1()完成累加后的时间long end=System.currentTimeMillis();System.out.println("串行运行时间:"+(end-begin));}//并行private static void func2() throws InterruptedException {//记录func2()开始运行的时间long begin=System.currentTimeMillis();//用线程对a累减Thread t1 = new Thread(()->{for(long i=0;i<count;i++){a--;}});//用线程对b累减Thread t2 = new Thread(()->{for(long i=0;i<count;i++){b--;}});//启动线程t1和t2t1.start();t2.start();//等待线程t1和t2运行完成t1.join();t2.join();//记录func2()完成累减后的时间long end=System.currentTimeMillis();System.out.println("并行运行时间:"+(end-begin));}
}
其运行结果如下:
由此可见,当任务量偏大时,使用多线程确实效率更高
注意:(1)不是任何时候多线程的效率都比单线程高,当任务量很小时,单线程效率可能比多线程高 (2)创建线程本身也有一定的系统开销,这个开销没有创建进程的大 (3)线程在CPU上调度也需要时间
四、Thread类及常见方法
1、常见构造方法
(1)Thread()(说明:创建线程对象)
(2)Thread(Runnable target)(说明:使用Runnable对象创建线程对象)
(3)Thread(String name)(说明:创建线程对象,并命名)
(4)Thread(Runnable target,String name)(说明:使用Runnable对象创建线程对象,并命名)
2、几个常见属性
注意:关于后台进程,JVM会在一个进程的所有非后台线程结束后,才会结束运行
3、启动一个线程 - start()
调用start()方法,才真的在操作系统的底层创建出一个线程
4、中断一个线程
方法一:自定义一个标志位,通过修改这个标志位,通知线程中断
public class Dome_Thread03 {//定义一个标志位static boolean isQuit=false;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{while(!isQuit){System.out.println("hello thread...");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("线程结束...");});//启动线程t1.start();//休眠5秒Thread.sleep(5000);//更改标志位isQuit=true;}
}
运行结果如下:
方法二:使用interrupt()方法来通知(注意:调用interrupt()方法时,如果线程在运行状态,直接中断线程,不会报异常,符合程序预期,如果线程在等待状态,就会报一个中断异常,要在异常处理代码块中进行中断逻辑实现)
public class Dome_Thread04 {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{while(!Thread.currentThread().isInterrupted()){System.out.println("hello thread...");try {Thread.sleep(1000);} catch (InterruptedException e) {System.out.println("休眠被中断...");throw new RuntimeException(e);}}System.out.println("线程已退出...");});System.out.println("线程是否存活:"+t1.isAlive());//启动线程t1.start();Thread.sleep(1000);System.out.println("线程是否存活:"+t1.isAlive());//中断线程t1.interrupt();//等待线程销毁Thread.sleep(100);//查看线程是否存活System.out.println("线程是否存活:"+t1.isAlive());}
}
运行结果如下:
5、等待一个线程 - join()
有时我们需要等一个线程完成工作后才进行下一步工作,这时就需要用到join()
6、获取当前线程引用 -Thread.currentThread()
示例:
public class Test02 {public static void main(String[] args) {//获取当前线程Thread t=Thread.currentThread();//打印当前线程名字System.out.println(t.getName());}
}
运行结果如下:
7、休眠当前线程 - Thread.sleep()
注意:当sleep括号里面仅有一个参数时,时间单位为毫秒,有两个参数时,第一个参数为毫秒,第二个参数为纳秒
五、线程的状态
首先看一下线程的全部状态:
public class Dome_Thread06 {public static void main(String[] args) {for(Thread.State state:Thread.State.values()){System.out.println(state);}}
}
其运行结果如下:
其各自代表的含义如下:
(1)NEW:表示创建好了一个Java线程对象,安排好了任务,但还没有启动(没有调用start()方法之前是不会创建PCB的,和PCB没有任何关系)
(2)RUNNABLE:运行+就绪的状态,在执行任务时最常见的状态之一,在系统中有对应PCB
(3)BLOCKED:等待锁的状态,阻塞的一种
(4)WAITING:没有等待时间,一直死等,直到被唤醒
(5)TIMED_WAITING:指定了等待时间的阻塞状态,过时不候
(6)TERMINATED:结束,完成状态,PCB已经销毁,但是Java线程对象还在
未完待续