多线程提升代码效率演示-代码体现
多线程是用来实现并发编程,以期望来提升代码的运行效率。
下面通过代码举例:
我们在主线程中计算a和b的值对比用多线程计算a和b的值所需要的时间。
package 线程;
public class ThreadDemo91 {
private static final long count=10_0000_0000;
public static void main(String[] args) throws InterruptedException{
//调用并发
concurrency();
//调用串行
serial();
}
private static void concurrency() throws InterruptedException{
long begin =System.nanoTime();
//利用一个线程计算a的值
Thread thread =new Thread(new Runnable() {
@Override
public void run() {
int a=0;
for(long i=0;i<count;i++){
a--;
}
}
});
thread.start();
//主线程内计算b的值
int b=0;
for(long i=0;i<count;i++){
b--;
}
//等待线程thread运行结束
thread.join();
//统计耗时
long end=System.nanoTime();
double ms=(end-begin)*1.0/1000/1000;
System.out.printf("并发:%f 毫秒%n",ms);
}
private static void serial(){
//全部在主线程内计算a、b的值
long begin=System.nanoTime();
int a=0;
for(long i=0;i<count;i++){
a--;
}
int b=0;
for(long i=0;i<count;i++){
b--;
}
long end=System.nanoTime();
double ms=(end-begin)*1.0/1000/1000;
System.out.printf("串行:%f 毫秒%n",ms);
}
}
运行结果如下:
- 当并发次数操作不超过百万次时,速度会比串行要慢一些,原因是线程创建和上下文切换需要时间
- 当次数比较大的时候,并发优势非常明显
JDK中观察线程信息
我们在java代码中通过创建的线程,可以通过JDK内置的工具jconcole进行观察。
这个工具在我们安装jdk1.8的bin目录下,如下图:
双击这个jconsole.exe文件,进入如下界面:
选择本地进程,选择当前正在运行的线程,我这里正在运行的线程是ThreadDemo90,选择,然后点击连接
继续点击。
我的ThreadDemo90代码如下:
package 线程;
public class ThreadDemo90 {
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(new Runnable() {
@Override
public void run() {
while(true){
System.out.println("7827346");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"Mythread");
t.start();
while(true){
System.out.println("dui");
Thread.sleep(1000);
}
}
}
Thread类中方法介绍
常见的构造方法如下:
每个构造方法对应的代码如下:
//构造方法1
Thread t1=new Thread();
//构造方法2
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
//do somthing
}
});
//构造方法3
Thread t3=new Thread("我的线程名字");
//构造方法4
Thread t4=new Thread(new Runnable(){
@Override
public void run() {
}
},"我的线程名字");
Thread类对象有name最大的意义,就是方便调试~
Thread对象的相关属性
isDaemon():其中isDaemon方法用来判断是否是后台线程,JVM会在一个进程的所有非后台线程结束后才会结束运行。该线程是否是后台线程,影响了JVM进程是否能够退出~
我们在代码中创建的线程一般默认不是后台线程。此时,如果main方法结束了,线程还没结束,JVM进程不会结束。如果当前线程是后台线程,此时,如果main方法结束了,线程还没结束,JVM进程就会直接结束,同时也会结束这个后台线程,一起带走~
isAlive():另外我们通过isAlive()方法来判断当前线程是否存活,这个方法判定的依据就是内核中的PCB是否销毁,系统中的线程是否销毁~
PCB(process control block)就是操作系统中的进程管理模块,可以这么理解,一个线程的基础就是一个PCB,一个进程中可能含有多个PCB,每次创建一个新的线程,就产生一个新的PCB。这个pcb中含有控制该线程的全部信息。
我们在java进程中通过Thread创建了一个新的对象,通过调用该对象start方法,就会在内核态中创建出一个PCB,然后这个PCB就会在内核中参与调度和执行,就开始执行Thread对象中的代码,当代码执行完毕后,这里的PCB(内核中的线程)随之就销毁了~
如下图:
相关属性的使用方法如下:
package 线程;
public class ThreadDemo89 {
public static void main(String[] args) {
Thread thread=new Thread(()->{
for(int i =0;i<10;i++){
System.out.println(Thread.currentThread().getName()+":我还或者");
try {
Thread.sleep(1*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+":我即将死去");
});
System.out.println(Thread.currentThread().getName()+":ID:"+thread.getId());
System.out.println(Thread.currentThread().getName()+":名称:"+thread.getName());
System.out.println(Thread.currentThread().getName()+":状态:"+thread.getState());
System.out.println(Thread.currentThread().getName()+":优先级:"+thread.getPriority());
System.out.println(Thread.currentThread().getName()+":后台线程:"+thread.isDaemon());
System.out.println(Thread.currentThread().getName()+":活着:"+thread.isAlive());
System.out.println(Thread.currentThread().getName()+":被中断:"+thread.isInterrupted());
}
}
运行结果如下:
如何中断一个线程
方法1:共享标记中断
我们可以设置一个静态boolean变量作为标记,这样main线程和新创建的线程都可以共享这个变量,当a线程在执行的时候,我们可以在b线程中修改a和b共享变量,让a线程停下。
代码如下:
package 线程;
public class ThreadDemo88 {
private static boolean flg=true;
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(){
@Override
public void run() {
while(flg){
System.out.println("线程运行中...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程结束!!");
}
};
t.start();
//主循环中也等待3秒
Thread.sleep(6000);
//3秒中之后,就把flag改成false;
flg=false;
}
}
运行结果如下:
通过本段代码我们可以看到,我们在main线程中修改flg的值,就可以控制t线程停止。
方法2:interrupt方法
其实Thread类中本身就内置了一个boolean变量作为线程是否被中断的标记。
一般我们需要调用isInterrupted()方法来判断当前线程的标记位。未被中断的线程一般的标记位都默认是false;然后可以通过interrupt方法来修改当前的标记位。但如果当前线程处于sleep、wait或者join等情况时,一般会抛出一个 InterruptedException异常,提醒我们当前线程的状态不是正在运行的状态,而是休眠,等待或者被阻塞的状态。这个时候就需要在异常抛出后的处理代码中做一些操作,像break,return等都可以及时终止循环,让线程停下来。
注意:run()方法执行完毕,线程就会被销毁。我们所说的让线程停止/中断,其实本质就是怎么让run方法中的代码停止执行,或者说快速执行完。
使用代码如下:
package 线程;
public class ThreadDemo87 {
public static void main(String[] args) {
Thread t=new Thread(){
@Override
public void run() {
//Thread.currentThread().isInterrupted() 当前线程的标记为一般都是false,因为是处于一个未被打断的状态。
while(!Thread.currentThread().isInterrupted()){
System.out.println("线程运行中....");
try {
//每次休眠1秒钟
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
//在这里加一个break,就能保证这个循环结束~
//加了一个break,就能保证interrupt方法在当前线程阻塞的时候抛出异常后,
//继续执行catch里的代码时,能及时终止循环,让这个线程结束。
break;
}
}
}
};
t.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.interrupt();
}
}
注意区别两个方法:
isInterrupted()和interrupted()方法.
- isInterrupted()是Thread 的实例方法。
- interrupted()是Thread的类方法。前面的修饰是有static的。
- 这两个方法功能类似,都可以用来判断线程的标记位是否被修改,或者说被设置。
== 当我们用interrupt方法把标记位设置成true后,此时,注意,是将一般线程默认的标记位false设置成true后,此时如果调用isInterrupted()方法,该方法就会告诉你,当前的标记位是true,并且把标记位修改为false,这样下一次查看标记位就变成了false。但如果此时调用的是interrupted()方法,该方法也会告诉你当前的标记位是true,但是不会修改标记位。下一次还是true。==