闭关修炼(一)多线程

2021年,记得元旦快了乐~~



线程与进程的区别

  • 什么是应用程序?
    可以执行的软件,在一个应用程序中,都会有一个进程。

  • 在进程中,程序代码如何执行?
    在进程中,一定有一个主线程,java中的main

  • 什么是线程?
    进程中的一条执行顺序/流程/路径。

  • 什么是进程?
    进程中有多个不同的执行路径,进程是线程的集合,是正在执行中的程序,任务管理器中可以看见进程。

  • 使用多线程的好处?
    提高程序效率。
    线程之间互不影响,因为线程都在自己独立运行。
    一个进程可中有多个线程,提高程序效率,提高用户体验。

  • 注意事项
    多线程并不提高单路径执行速度,而是多路径同时进行提高程序效率。

多线程应用场景

  1. 多线程下载
  2. 爬虫
  3. ajax异步上传
  4. 分布式job,同一时刻执行多个任务调度
  5. mq

继承方式创建多线程

创建线程有5种方式

  1. 继承Thread类
  2. runnable接口
  3. 使用匿名内部类方式
  4. callable
  5. 线程池

继承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("主线程执行完毕");
    }

多线程运行状态

在这里插入图片描述

  1. 新建状态
    new Thread

  2. 就绪状态
    就绪状态,等待cpu调度

  3. 运行状态
    获取cpu调度权

  4. 死亡状态
    run方法执行完毕,或者stop方法

  5. 休眠(阻塞)状态
    调用了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");
                });
            }
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值