文章目录
-
- 1 多线程相关概念
- 2 多线程概念
- 3 多线程优缺点
- 4 多线程实现方式
- 5 实现runnable和callable的区别
- 6 继承thread和实现runnable接口的区别
- 7 多线程匿名(Anonymous)内部类用法
- 8 Thread常用方法
- 8 多线程状态
1 多线程相关概念
并发(Concurrent):在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行。
同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行。
并行(Parallel):当系统有一个以上CPU时,当一个CPU执行一个进程时,另一个CPU可以执行另一个进程,两个进程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。其实决定并行的因素不是CPU的数量,而是CPU的核心数量,比如一个CPU多个核也可以并行。
进程(Process):是正在运行的程序实体,并且包括这个运行的程序中占据的所有系统资源,比如说CPU(寄存器),IO,内存,网络资源等。同样一个程序,同一时刻被两次运行了,那么他们就是两个独立的进程。
线程(Thread):是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
进程与线程的区别:
- 线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位;进程是系统资源分配的单位,线程是系统调度的单位。
- 一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线;
- 进程之间相互独立,进程之间不能共享资源,而线程共享所在进程的地址空间和其它资源。同时线程还有自己的栈和栈指针,程序计数器等寄存器。
- 调度和切换:线程上下文切换比进程上下文切换要快得多。
2 多线程概念
多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理或同时多线程处理器。在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理“。
3 多线程优缺点
优点:
(1) 用户界面可以在进行其它工作的同时CPU一直处于活动状态,可以让程序运行速度更快。
(2)占用大量处理时间的任务可以定期将处理器时间让给其它任务,可以提高CPU利用率。
使用实例演示多线程好处(在了解了多线程实现方式之后进行编写)
package com.aaa.mt.demo3;
import java.io.*;
/**
* @ fileName:PingIP
* @ description:
* @ author:zhz
* @ createTime:2021/11/30 10:58
* @ version:1.0.0
*/
public class PingIP {
/**
* 封装pingIP方法
* @param ip
*/
public static void pingIP(String ip){
InputStream inputStream = null;
BufferedReader bufferedReader = null;
try {
//Runtime每个Java应用程序都有一个Runtime类的Runtime ,允许应用程序与运行应用程序的环境进行接口。
Runtime runtime = Runtime.getRuntime();
//使用与环境接口的类的exec 可以得到当前运行进程类
Process process = runtime.exec("ping "+ip);
//获取到执行结果流
inputStream = process.getInputStream();
//BufferedReader使用按行读取的字符流并提高效率
//InputStreamReader 把字节流转字符流的类
bufferedReader = new BufferedReader(new InputStreamReader(inputStream,"GBK"));
//定义变量接受读的字符串
String charLine = null;
//定义是否ping得通得标识符
boolean isSuc =false;
//按行读取
while((charLine=bufferedReader.readLine())!=null){
if(charLine.indexOf("TTL")!=-1){
isSuc=true;
break;
}
}
if(isSuc){
System.out.println(ip+"通");
}else{
System.out.println(ip+"不通");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(inputStream!=null){
inputStream.close();
}
if(bufferedReader!=null){
bufferedReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
//返回1970-1-1午夜到现在的毫秒数
long startTime = System.currentTimeMillis();
for (int i = 1; i <= 20; i++) {
pingIP("192.168.41."+i);
}
long endTime = System.currentTimeMillis();
System.out.println("用时:"+(endTime-startTime)/1000);
}
}
package com.aaa.mt.demo3;
/**
* @ fileName:MTPingIP
* @ description:
* @ author:zhz
* @ createTime:2021/11/30 11:24
* @ version:1.0.0
*/
public class MTPingIP extends Thread {
private String ip;
public MTPingIP(String ip) {
this.ip = ip;
}
@Override
public void run() {
//执行业务代码
PingIP.pingIP(ip);
}
}
package com.aaa.mt.demo3;
/**
* @ fileName:Test
* @ description:
* @ author:zhz
* @ createTime:2021/11/30 11:27
* @ version:1.0.0
*/
public class Test {
public static void main(String[] args) {
//返回1970-1-1午夜到现在的毫秒数
long startTime = System.currentTimeMillis();
// main线程 它和启动20个线程是并行运行
System.out.println("当前线程名称"+Thread.currentThread().getName());
for (int i = 1; i <= 20; i++) {
//循环启动20线程 20个线程一起ping
new MTPingIP("192.168.41."+i).start();
}
long endTime = System.currentTimeMillis();
System.out.println("用时:"+(endTime-startTime)/1000);
}
}
缺点:
(1)等候使用共享资源时造成程序的运行速度变慢。这些共享资源主要是独占性的资源 ,如打印机等。
(2)对线程进行管理要求额外的 CPU开销,线程的使用会给系统带来上下文切换的额外负担。
(3)线程的死锁。即对共享资源加锁实现同步的过程中可能会死锁。(后面有实例)
4 多线程实现方式
1,继承thread
package com.aaa.mt.demo1;
/**
* @ fileName:MTExtendsThread
* @ description:继承Thread完成多线程
* @ author:zhz
* @ createTime:2021/11/30 9:22
* @ version:1.0.0
*/
public class MTExtendsThread extends Thread{
@Override
public void run() {
//执行线程业务的方法
for (int i = 0; i < 10; i++) {
//返回正在执行的线程实例
Thread thread = Thread.currentThread();
//getName()获取当前线程实例的名称
System.out.println(thread.getName()+"执行打印"+i);
}
}
}
2 , 实现Runnable
package com.aaa.mt.demo1;
/**
* @ fileName:MTImplementsRunnable
* @ description:
* @ author:zhz
* @ createTime:2021/11/30 9:36
* @ version:1.0.0
*/
public class MTImplementsRunnable implements Runnable{
@Override
public void run() {
//执行线程业务的方法
for (int i = 0; i < 10; i++) {
//返回正在执行的线程实例
Thread thread = Thread.currentThread();
//getName()获取当前线程实例的名称
System.out.println(thread.getName()+"执行打印"+i);
}
}
}
3,实现Callable
package com.aaa.mt.demo1;
import java.util.UUID;
import java.util.concurrent.Callable;
/**
- @ fileName:MTImplementsCallable
- @ description:
- @ author:zhz
- @ createTime:2021/11/30 9:44
- @ version:1.0.0
*/
public class MTImplementsCallable implements Callable {
@Override
public Object call() throws Exception {
//执行线程业务的方法
for (int i = 0; i < 10; i++) {
//返回正在执行的线程实例
Thread thread = Thread.currentThread();
//getName()获取当前线程实例的名称
System.out.println(thread.getName()+"执行打印"+i);
}
//获取随机数
UUID uuid = UUID.randomUUID();
System.out.println("随机数字符串为:"+uuid);
System.out.println(1/0);
return uuid;
}
}
4,使用线程池
package com.aaa.mt.demo1;
/**
* @ fileName:MTPool
* @ description:
* @ author:zhz
* @ createTime:2021/11/30 10:30
* @ version:1.0.0
*/
public class MTPool implements Runnable {
@Override
public void run() {
//执行线程业务的方法
for (int i = 0; i < 10; i++) {
//返回正在执行的线程实例
Thread thread = Thread.currentThread();
//getName()获取当前线程实例的名称
System.out.println(thread.getName()+"执行打印"+i);
}
}
}
//实例化线程类
MTPool mtPool =new MTPool();
//使用Executors创建固定长度为4的线程池
ExecutorService executorService = Executors.newFixedThreadPool(4);
//调用execute启动线程
executorService.execute(mtPool);
5 实现runnable和callable的区别
1,都是执行多线程,但是方法名称不同 run() 和call()
2, 实现Runnable方法是没有返回值,无法获取线程业务方法执行结果 而Callable相反
3, 实现Runnable方法没有抛出异常 而Callable有异常处理,并且获取异常
6 继承thread和实现runnable接口的区别
一个类只能继承一个父类,存在局限;一个类可以实现多个接口。在实现Runnable接口的时候调用Thread的Thread(Runnable run)或者Thread(Runnable run,String name)构造方法创建进程时,使用同一个Runnable实例,建立的多线程的实例变量也是共享的;但是通过继承Thread类是不能用一个实例建立多个线程,故而实现Runnable接口适合于资源共享;当然,继承Thread类也能够共享变量,能共享Thread类的static变量;
使用卖票实例演示上面表述:
package com.aaa.mt.demo2;
/**
* @ fileName:SellTicketExtendsThread
* @ description: 使用多线程卖票,继承Thread来实现业务
* @ author:zhz
* @ createTime:2021/11/30 10:40
* @ version:1.0.0
*/
public class SellTicketExtendsThread extends Thread {
//定义总票数
private int ticketNum = 20;
//卖票人姓名
private String name;
/**
* 构造方法
* @param name
*/
public SellTicketExtendsThread(String name) {
this.name = name;
}
@Override
public void run() {
while(ticketNum>0){
System.out.println(name+"正在卖票,剩余"+(--ticketNum)+"张");
}
}
}
package com.aaa.mt.demo2;
/**
* @ fileName:SellTicketImplementsRunnable
* @ description:
* @ author:zhz
* @ createTime:2021/11/30 10:49
* @ version:1.0.0
*/
public class SellTicketImplementsRunnable implements Runnable{
//定义总票数
private int ticketNum = 20;
@Override
public void run() {
while(ticketNum>0){
System.out.println(Thread.currentThread().getName()+"正在卖票,剩余"+(--ticketNum)+"张");
}
}
}
package com.aaa.mt.demo2;
/**
* @ fileName:Test
* @ description:
* @ author:zhz
* @ createTime:2021/11/30 10:45
* @ version:1.0.0
*/
public class Test {
public static void main(String[] args) {
/* SellTicketExtendsThread sellTicketExtendsThread =new SellTicketExtendsThread("马云");
sellTicketExtendsThread.start();
*//* sellTicketExtendsThread.start();
sellTicketExtendsThread.start();*//*
SellTicketExtendsThread sellTicketExtendsThread1 =new SellTicketExtendsThread("马化腾");
sellTicketExtendsThread1.start();
SellTicketExtendsThread sellTicketExtendsThread2 =new SellTicketExtendsThread("刘强东");
sellTicketExtendsThread2.start();*/
SellTicketImplementsRunnable sellTicketImplementsRunnable =new SellTicketImplementsRunnable();
new Thread(sellTicketImplementsRunnable,"马云").start();
new Thread(sellTicketImplementsRunnable,"马化腾").start();
new Thread(sellTicketImplementsRunnable,"刘强东").start();
}
}
7 多线程匿名(Anonymous)内部类用法
package com.aaa.mt.demo4;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
/**
* @ fileName:AnonymousInternalClass
* @ description:
* @ author:zhz
* @ createTime:2021/11/30 11:35
* @ version:1.0.0
*/
public class AnonymousInternalClass {
public static void main(String[] args) {
//继承Thread启动线程
new Thread(){
@Override
public void run() {
for (int i = 0; i < 10; i++) {
//返回正在执行的线程实例
Thread thread = Thread.currentThread();
//getName()获取当前线程实例的名称
System.out.println(thread.getName()+"执行打印"+i);
}
}
}.start();
System.out.println("-------------------------------------");
//实现Runnable
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
//返回正在执行的线程实例
Thread thread = Thread.currentThread();
//getName()获取当前线程实例的名称
System.out.println(thread.getName()+"执行打印"+i);
}
}
}).start();
System.out.println("-------------------------------------");
//实现Callable
new Thread(new FutureTask<String>(
new Callable<String>() {
@Override
public String call() throws Exception {
for (int i = 0; i < 10; i++) {
//返回正在执行的线程实例
Thread thread = Thread.currentThread();
//getName()获取当前线程实例的名称
System.out.println(thread.getName()+"执行打印"+i);
}
return null;
}
})).start();
}
}
8 Thread常用方法
currentThread() 返回对当前正在执行的线程对象的引用。
setName(String name) 将此线程的名称更改为等于参数 name 。(具体用法参考项目demo4包)
getName() 返回此线程的名称。
start() 导致此线程开始执行; Java虚拟机调用此线程的run方法。
run() 如果这个线程使用单独的Runnable运行对象构造,则调用该Runnable对象的run方法; 否则,此方法不执行任何操作并返回。 线程执行业务的方法。
setPriority(int newPriority) 更改此线程的优先级。
getPriority() 返回此线程的优先级。
sleep(long millis) 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。
stop() 强制结束当前线程。Deprecated
join() 等待这个线程死亡。一旦调用一个线程的join方法后,当前线程产生阻塞,直到在调用 join方法的线程执行完毕后,再去执行。
yield() 对调度程序的一个暗示,即当前线程愿意暂时让出当前使用的处理器。当前让出执行权。
8 多线程状态
五种状态:
- 新建(程序还没有开始运行线程中的代码)
- 就绪(当start()方法返回后,线程就处于就绪状态,处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间)
- 运行(线程获得CPU时间后,它才进入运行状态,真正开始执行run())
- 阻塞(等待wait、带超时的等待sleep)
- 终止(死亡,正常退出或者异常终止)