2021年,记得元旦快了乐~~
文章目录
线程与进程的区别
-
什么是应用程序?
可以执行的软件,在一个应用程序中,都会有一个进程。 -
在进程中,程序代码如何执行?
在进程中,一定有一个主线程,java中的main -
什么是线程?
进程中的一条执行顺序/流程/路径。 -
什么是进程?
进程中有多个不同的执行路径,进程是线程的集合,是正在执行中的程序,任务管理器中可以看见进程。 -
使用多线程的好处?
提高程序效率。
线程之间互不影响,因为线程都在自己独立运行。
一个进程可中有多个线程,提高程序效率,提高用户体验。 -
注意事项
多线程并不提高单路径执行速度,而是多路径同时进行提高程序效率。
多线程应用场景
- 多线程下载
- 爬虫
- ajax异步上传
- 分布式job,同一时刻执行多个任务调度
- mq
继承方式创建多线程
创建线程有5种方式
- 继承Thread类
- runnable接口
- 使用匿名内部类方式
- callable
- 线程池
继承Thread接口,重写(override)run方法,run方法就是线程需要执行的代码。
class MyThread extends Thread{
@Override
public void run() {
for (int i =0 ; i< 30; i++){
System.out.println(i);
}
}
}
启动线程,实例化类,调用start方法,如果调用run方法还是单独的执行run方法。
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
for (int i =31 ; i< 60; i++){
System.out.println("main" + i);
}
}
启动多线程,代码不会从上往下执行。
同步和异步的概念?
同步
main函数中有方法①和方法②,每个方法都需要花费10秒时间,则需要20秒完成main函数。
代码从上往下执行,被称为同步。
同步中,main函数的方法之间存在依赖关系,存在执行顺序,方法②等待方法①执行完毕后才能执行。
也叫做单线程。
异步
可以理解为多线程,方法①和方法②同时执行,每个方法都需要花费10秒时间,则需要10秒完成main函数。提高程序效率,线程间独立,互不影响。
CPU调度时 异步中有执行权的概念,但在宏观上看是同时进行的。
runnable接口创建多线程
创建类,实现runnable接口,重写run方法
class MyThread2 implements Runnable{
public void run() {
for (int i =0 ; i< 30; i++){
System.out.println("run" + i);
}
}
}
启动线程方式,接口作为参数传递给Thread类,调用start方法
public static void main(String[] args) {
MyThread2 myThread2 = new MyThread2();
Thread thread = new Thread(myThread2);
thread.start();
}
使用继承方式创建线程还是使用runnable接口创建线程好?
使用接口方式,开发面向接口编程,继承后不能再继承,接口可以。
使用匿名内部类方式创建多线程
什么是匿名内部类?
没有名称的内部类。
例子:
abstract class Person{
abstract void add();
}
public static void main(String[] args) {
Person person = new Person() {
void add() {
System.out.println("hello");
}
};
person.add();
}
线程中也是类似,实例化Thread类,runnable接口使用匿名内部类进行实现
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
public void run() {
System.out.println("Hello~");
}
});
thread.start();
}
Callable方式创建多线程
待更
线程池方式创建多线程
待更
多线程常见API
getId()
获取线程的id,id唯一
class MyThread extends Thread{
@Override
public void run() {
for (int i =0 ; i< 30; i++){
System.out.println(getId()+ "run" + i);
}
}
}
主线程id如何获取?
System.out.println(Thread.currentThread().getId());
主线程id为1
任何一个程序都会有一个主线程,本质也是一个线程。
getName()
获取线程名称
System.out.println(Thread.currentThread().getName());
主线程的name是main
子线程名称默认是Thread-0,Thread-1,Thread-2…
sleep()
暂停
class MyThread extends Thread{
@Override
public void run() {
for (int i =0 ; i< 30; i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getId()+ "run" + i);
}
}
}
Thread.currentThread()
获取当前线程,在用Runnable接口的时候需要用这种方式获取当前线程。
stop()
不安全,已过时
class MyThread extends Thread{
@Override
public void run() {
for (int i =0 ; i< 30; i++){
if (i==5){
stop();
}
System.out.println(getId()+ "run" + i);
}
}
}
Thread()
new Thread(new Runnable() {
public void run() {
System.out.println(1);
}
}, "线程名");
守护线程与非守护线程
什么是守护线程?
和main函数相关。
守护线程和主线程一起销毁。
如gc线程,垃圾回收机制中的单独的一个线程,专门用来回收垃圾,主线程结束,守护线程一起结束。
什么是非守护线程?
与main函数互不影响。
自己创建线程叫做用户线程,如果主线程停止了,不会影响用户线程,用户线程也叫非守护线程。
非守护线程演示代码:
public static void main(String[] args) {
new Thread(new Runnable() {
@lombok.SneakyThrows
public void run() {
for (int i = 0; i <= 30; i++) {
Thread.sleep(300);
System.out.println(i);
}
}
}, "线程名").start();
System.out.println("主线程执行完毕");
}
执行结果:
主线程结束和非守护线程没有依赖关系。
设置守护线程
thread.setDaemon(true);
让守护线程和主线程一起销毁。
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@SneakyThrows
public void run() {
for (int i = 1000; i <= 3000; i++) {
System.out.println(i);
}
}
}, "线程名");
thread.setDaemon(true);
thread.start();
for (int i = 0; i <500; i++){
System.out.println(i);
}
System.out.println("主线程执行完毕");
}
多线程运行状态
-
新建状态
new Thread -
就绪状态
就绪状态,等待cpu调度 -
运行状态
获取cpu调度权 -
死亡状态
run方法执行完毕,或者stop方法 -
休眠(阻塞)状态
调用了sleep方法或者wait()方法,运行状态转到阻塞状态,再到就绪状态
join()方法作用
现有线程A和线程B,线程A调用了线程B的join方法,A等待B线程执行完毕之后再继续执行。也就是说线程A在调用B的join方法后,释放了执行权,让B先执行。
主线程让子线程先执行,方法:在主函数中调用子线程的join方法,例子如下:
@SneakyThrows
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable() {
@SneakyThrows
public void run() {
for (int i = 1000; i <= 1500; i++) {
System.out.println(i);
}
System.out.println("子线程执行完毕!");
}
});
thread1.start();
thread1.join();
for (int i = 0; i <= 50; i++) {
System.out.println(i);
}
}
面试题顺序执行T1,T2,T3
现有T1,T2,T3三个线程,怎么保证T2在T1执行完后执行,T3在T2执行完后执行?
@SneakyThrows
public static void main(String[] args) {
final Thread thread1 = new Thread(new Runnable() {
public void run() {
for (int i = 0; i <= 10; i++) {
System.out.println(i);
}
System.out.println("子线程1执行完毕!");
}
});
final Thread thread2 = new Thread(new Runnable() {
@SneakyThrows
public void run() {
thread1.join();
for (int i = 11; i <= 20; i++) {
System.out.println(i);
}
System.out.println("子线程2执行完毕!");
}
});
Thread thread3 = new Thread(new Runnable() {
@SneakyThrows
public void run() {
thread2.join();
for (int i = 21; i <= 30; i++) {
System.out.println(i);
}
System.out.println("子线程3执行完毕!");
}
});
thread1.start();
thread2.start();
thread3.start();
}
案例多线程分批处理数据
端口扫描
public void threadScanPort() {
ExecutorService executorService = Executors.newFixedThreadPool(100);
portCount.set(0);
int totalThreadNum = 100;
for (int n = 0; n <= totalThreadNum; n++) {
executorService.execute(new ScanHandler(n, totalThreadNum));
}
}
class ScanHandler implements Runnable {
//用于端口扫描的总共线程数
private int totalThreadNum;
// 线程号
private int threadNo;
public ScanHandler(int threadNo, int totalThreadNum) {
this.totalThreadNum = totalThreadNum;
this.threadNo = threadNo;
}
@Override
public void run() {
int startPort = Integer.parseInt(startPortField.getText());
int endPort = Integer.parseInt(endPortField.getText());
String host = targetIp.getText();
//startPort和endPort为成员变量,表示需要扫描的起止端口
for (int port = startPort + threadNo; port <= endPort; port = port + totalThreadNum) {
try {
Socket socket = new Socket();
socket.connect(new InetSocketAddress(host, port), 200);
socket.close();
String msg = "端口 " + port + " is open\n";
Platform.runLater(() -> {
taDisplay.appendText(msg);
});
} catch (IOException e) {
System.out.println(port + "is close");
}
portCount.incrementAndGet();//扫描的端口数+1
}
if (portCount.get() == (endPort - startPort + 1)) {
portCount.incrementAndGet();//加1,使得不再输出下面的线程扫描结束的信息
Platform.runLater(() -> {
taDisplay.appendText("\n----------------多线程扫描结束--------------------\n");
});
}
}
}