目录
- 什么是Thread类
- Thread类的基本用法
- 线程创建
- 最基本的创建方法
- 实现runnable接口
- 使用匿名内部类
- lambda表达式
- Thread中的一些重要方法
- 启动一个线程
- 中断线程
- 手动的设置一个标志位
- 使用Thread中内置的一个标志位进行判断
- 线程等待
- 获取当前线程引用
Java这个生态中最常用的并发编程方式是多线程。
什么是Thread类
在java标准库中,就提供了一个Thread类,来表示/操作线程。Thread类也可以视为Java标准库提供的API,创建好的Thread实例,其实和操作系统中的线程是一一对应的关系,操作系统,提供了一组关于线程的API,Java对于这组API进一步封装,就成了Thread类。
Thread类的基本用法
线程创建
最基本的创建方法
- 方法一
lass MyThread extends Thread{
@Override
public void run() {
System.out.println("hello thread");
}
}
//最基本的创建线程的方法
public class Demo1 {
public static void main(String[] args) {
Thread t=new MyThread();
t.start();
}
}
此方法是最简单的方法,创建子类继承自Thread,并且重写run方法。
- 方法二(体现线程的并发执行)
class MyThread2 extends Thread{
@Override
public void run() {
while(true){
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Demo2 {
public static void main(String[] args) {
Thread t=new MyThread2();
t.start();
while(true){
System.out.println("hello main");
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
实现runnable接口
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("hello");
}
}
public class Demo3 {
public static void main(String[] args) {
Thread t=new Thread(new MyRunnable());
t.start();
}
}
通过Runnable来描述任务的内容,进一步把描述好的任务交给Thread实例
使用匿名内部类
public class Demo4 {
public static void main(String[] args) {
//创建了一个匿名内部类,继承自Thread类,同时重写run方法,同时在new出这个匿名内部类的实例
Thread t=new Thread(){
@Override
public void run() {
System.out.println("hello thread");
}
};
t.start();
}
}
public class Demo5 {
public static void main(String[] args) {
//new 的Runnable,针对这个创建的匿名内部类,同时new出的Runnable实例传给Thread的构造方法
Thread t=new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hello thread");
}
});
t.start();
}
}
lambda表达式
public class Demo6 {
public static void main(String[] args) {
//使用lambda代替了Runnable而已
Thread t=new Thread(() -> {
System.out.println("hello thread");
});
t.start();
}
}
多线程能够提高任务完成的效率
有两个整数变量,分别对这两变量自增10亿次。
分别使用1个线程和2个线程
//多线程能够提高任务完成的效率
public class Demo7 {
private static final long count=10_0000_0000;
public static void serial(){
long beg=System.currentTimeMillis();
long a=0;
for(long i=0;i<count;i++){
a++;
}
long b=0;
for(long i=0;i<count;i++){
b++;
}
long end=System.currentTimeMillis();
System.out.println("消耗时间"+(end-beg)+"ms");
}
public static void concurrent() throws InterruptedException {
long beg=System.currentTimeMillis();
Thread t1=new Thread(()->{
long a=0;
for(long i=0;i<count;i++){
a++;
}
});
t1.start();
Thread t2=new Thread(()->{
long b=0;
for(long i=0;i<count;i++){
b++;
}
});
t2.start();
//此处不能直接记录结束时间,现在这个求结束时间的代码是在main线程中
//main和t1 t2是并发执行的关系,此处 t1 t2还没执行完,这里就开始记录结束时间,显然不准确
//正确的做法是让main等待 t1 t2跑完了,在来记录结束时间
//join的效果就是等待线程结束,t1.join就是让main等待t1结束,t2.join就是让main等待t2结束
t1.join();
t2.join();
long end=System.currentTimeMillis();
System.out.println("消耗时间:"+(end-beg)+"ms");
}
public static void main(String[] args) throws InterruptedException {
serial();
concurrent();
}
}
多线程不是万能的,并不是上了多线程,速度一定能提高。还得看具体的场景。
多线程特别适合那种CPU密集型的程序,程序要进行大量的计算,使用多线程可以更充分的利用CPU的多核资源。
Thread中的一些重要方法
启动一个线程
start决定了系统中是不是真的创建出线程。
t.run单纯的只是一个普通的方法,描述了任务的内容
t.start则是一个特殊的方法,内部会在系统中创建线程
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();
}
}
}
如果把t.start 换成t.run 效果是不同的
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.run();
while(true){
System.out.println("hello main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
start和run的区别
public static void main(String[] args) {
Thread t=new Thread(() -> {
for(int i=0;i<3;i++){
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.run();
while(true){
System.out.println("hello main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
执行效果
中断线程
手动的设置一个标志位
设置一个标志位(自己创建的,Boolean),来控制线程是否要执行结束。
private static boolean isQuite=false;
public static void main(String[] args) {
Thread t=new Thread(() ->{
while(!isQuite){
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
//只要把这个isQuite设为true,此时这个循环就退出了
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
isQuite=true;
System.out.println("终止t进程");
}
执行效果此处因为多个线程共用一个虚拟地址空间,因此main线程修改的isQuite和t线程判定的isQuite是同一个值
使用Thread中内置的一个标志位进行判断
public static void main(String[] args) {
Thread t=new Thread(() ->{
while(!Thread.currentThread().isInterrupted()){
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}) ;
t.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//在主线程中,调用interrupt方法,来中断这个线程
//t.interrupt的意思就是让t线程被中断
t.interrupt();
}
当下的代码,一旦触发了异常之后,就进入catch语句,在catch中,就只是打了一个日志。printStackTrace是打印当前异常位置的代码调用栈,打完日志之后,就直接继续执行。
那么,上述的情况该如何解决呢?
加一个break即可。
public static void main(String[] args) {
Thread t=new Thread(() ->{
while(!Thread.currentThread().isInterrupted()){
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
//当触发异常之后,立即退出循环
break;
}
}
}) ;
t.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//在主线程中,调用interrupt方法,来中断这个线程
//t.interrupt的意思就是让t线程被中断
t.interrupt();
}
执行效果
线程等待
多个线程之间,调度顺序是不确定的。
线程之间的执行是按照调度器来安排的,这个 过程可以视为是“无序,随机”,但是这样不太好,有些时候,我们需要能够控制线程之间的顺序。
线程等待,就是其中一种控制线程执行顺序的手段。
此处的线程等待,主要是控制线程结束的先后顺序。
public static void main(String[] args) {
Thread t=new Thread(() -> {
for(int i=0;i<5;i++){
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
//在主线程中就可以使用一个等待操作,来等待t线程执行结束
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
join操作默认情况下,是“死等”,这是不合理的。
因此join提供了另外一个版本,就是可以执行等待时间,最长等多久,如果等不到,就不等了。
public static void main(String[] args) {
Thread t=new Thread(() -> {
for(int i=0;i<5;i++){
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
//在主线程中就可以使用一个等待操作,来等待t线程执行结束
try {
t.join(10000);//进入join也会产生阻塞,这个阻塞不会一直持续下去
} catch (InterruptedException e) {
e.printStackTrace();
}
}
获取当前线程引用
public static void main(String[] args) {
Thread t=new Thread(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
};
t.start();
//这个操作是在main线程之后调用的,因此拿到的就是main这个线程的实例
System.out.println(Thread.currentThread().getName());
}
}
另一种方法
public static void main(String[] args) {
Thread t=new Thread(){
@Override
public void run() {
//System.out.println(Thread.currentThread().getName());
System.out.println(this.getName());
}
};
t.start();
//这个操作是在main线程之后调用的,因此拿到的就是main这个线程的实例
System.out.println(Thread.currentThread().getName());
}