多线程技术


学习目标

  • 线程与进程
  • 为什么要使用多线程
  • 多线程应用场景
  • 多线程创建方式
  • 线程生命周期
  • 面试总结
  • 练习题

线程与进程的区别

线程与进程的关系
总之:进程是所有线程的集合,每个线程是进程中的一条执行路径。

多线程作用和应用场景

  • 多线程可以提高程序的效率
  • 应用场景:迅雷等多线程下载、分批发送短信等

多线程的创建方式

第一种继承Thread类,重写run方法

package com.zh;

/**
 * 创建多线程
 */
class CreateThread extends Thread{
    /**
     * run方法执行 需要线程执行的任务,代码。
     */
    @Override
    public void run() {
        for(int i=0;i<100;i++){
            System.out.println("run()的执行i:"+i);
        }
    }
}
public class ThreadDemo2 {
    public static void main(String[] args){
        System.out.println("创建进程 main");
        //1、定义一个类,继承Thread类,重写run方法
        //2、启动线程
        CreateThread createThread = new CreateThread();
        //不能调用run(),需要的是线程,而不是调用方法,调用run方法相当于主线程执行
        createThread.start();
        System.out.println("线程启动了 main");
        for (int i=0;i<100;i++){
            System.out.println("main中i:"+i);
        }
    }
}

结果:主线程和子线程同时执行
线程

第二种实现Runnable接口,重写run方法

package com.zh;
class CreateRunnable implements Runnable{
    /**
     * run方法也是执行 线程需要执行的任务  执行的代码
     */
    @Override
    public void run() {
        for(int i=0;i<100;i++){
            System.out.println("run()的执行i:"+i);
        }
    }
}
public class ThreadDemo3 {
    public static void main(String[] args){
        //1、创建一个类,实现Runnable接口 重写run方法
        //2、启动线程
        System.out.println("创建进程 main");
        CreateRunnable createRunnable = new CreateRunnable();
        Thread thread = new Thread(createRunnable);
        thread.start();
        System.out.println("线程启动了 main");
        for (int i=0;i<100;i++){
            System.out.println("main中i:"+i);
        }
    }
}

第三种使用匿名内部类方法

  • 匿名内部类:new user().add(); new 里面实现方法,直接new,不起名称。
package com.zh;

public class ThreadDemo4 {
    public static void main(String[] args){
        //使用匿名内部类方式创建线程
        System.out.println("创建进程 main");
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=0;i<100;i++){
                    System.out.println("run()的执行i:"+i);
                }
            }
        });
        System.out.println("创建进程结束 main");
        //启动线程
        thread.start();
        System.out.println("线程启动了 main");
        for (int i=0;i<100;i++){
            System.out.println("main中i:"+i);
        }
    }
}

获取线程对象及其名称

常用线程API方法

  • start() 启动线程
  • currentThread() 获取当前线程对象
  • getID() 获取当前线程ID
  • getName() 获取当前线程名称
  • sleep(long mill) 休眠线程
  • Stop() 停止线程

实例

继承Thread型

package com.zh;
class DemoThread extends Thread{
    @Override
    public void run() {
        for (int i = 0;i<10;i++){
            //sleep(这里是毫秒数),在run方法中不能抛出异常,只能try catch
            try {
                //sleep作用让当前线程从运行状态变为休眠状态,如果时间到期就会回到运行状态。sleep不能释放锁,多线程之间实现同步
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //获取线程的ID ID是多线程随机进行分配不重复主键
            getId();
            //获取线程的Name 得到的形式:Thread-数字
            getName();
            System.out.println("run() i:"+i);
        }
    }
}
public class ThreadDemo5 {
    public static void main(String[] args){
        DemoThread demoThread = new DemoThread();
        demoThread.start();
    }
}

实现Runnable接口型

package com.zh;
class ThreadRun implements Runnable{

    @Override
    public void run() {
        for (int i=0;i<10;i++){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //currentThread()获取当前线程对象
            Object object = Thread.currentThread().getId();
            System.out.println(object);
            System.out.println(Thread.currentThread().getName());
            System.out.println("run() i:"+i);
        }
    }
}
public class ThreadDemo6 {
    public static void main(String[] args){
        System.out.println("main()");
        ThreadRun threadRun = new ThreadRun();
        Thread thread = new Thread(threadRun);
        thread.start();
    }
}

常用线程构造函数

  • Thread() 分配一个新的Thread对象
  • Thread(String name) 分配一个新的Thread对象,具有指定的name 正如其名
  • Thread(Runable r) 分配一个新的Thread对象
  • Thread(Runable r,String name) 分配一个新的Thread对象

多线程运行状态

线程执行状态

多线程分批处理数据

提出问题

需求:给10万个用户发送一条信息
为了提高程序效率,请使用多线程技术分批发送数据。

分析问题

并不是线程越多越好,是根据服务器(电脑)配置来决定的,因为每开一个线程,都会占用CPU资源。

需求分析

分批跑数据类似分页

解决问题

  • 主线程代码
package com.zh;

import com.zh.entity.UserEntity;
import com.zh.util.ListUtils;

import java.util.ArrayList;
import java.util.List;
class UserThread extends Thread{

    /**
     * 每个线程分批多少数据
     * @param listUser
     */
    private List<UserEntity> listUser;

    public UserThread(List<UserEntity> listUser){
        this.listUser = listUser;
    }
    @Override
    public void run() {
        for (UserEntity userEntity :listUser){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("name:"+getName()+userEntity.toString());
            //第三方短信接口
        }
    }
}
/**
 * 多线程分批处理数据
 */
public class BatchThread {
    public static void main(String[] args){
        List<UserEntity> listUser = null;
        //1、初始化用户
        List<UserEntity> initUser = initUser();
        //2、定义每个线程最多跑多少数据
        int userCount = 2;
        //3、计算线程数,并计算将每个线程跑那些数据
        List<List<UserEntity>> splitList = ListUtils.splitList(initUser,userCount);
        for (List<UserEntity> list : splitList){
//            System.out.println(list.toString());
            listUser = list;
            //4、让子线程进行分批异步处理数据
            UserThread userThread = new UserThread(listUser);
            userThread.start();
        }
    }
    /**
     * 初始化用户信息
     * @return
     */
    public static List<UserEntity> initUser(){
        List<UserEntity> listUser = new ArrayList<>();
        for (int i=0;i<11;i++){

            listUser.add(new UserEntity("userId:"+i,"userName:"+i));
        }
        return listUser;
    }

}
  • 实体类
package com.zh.entity;

/**
 * 用户实体类
 */
public class UserEntity {
    private String userId;
    private String userName;

    public UserEntity(String userId, String userName) {
        this.userId = userId;
        this.userName = userName;
    }

    @Override
    public String toString() {
        return "UserEntity{" +
                "userId='" + userId + '\'' +
                ", userName='" + userName + '\'' +
                '}';
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }
}
  • 工具类
package com.zh.util;

import java.util.ArrayList;
import java.util.List;

public class ListUtils {
    public static <T> List<List<T>> splitList(List<T>list, int pageSize) {
        int listSize = list.size();
        int page = (listSize + (pageSize - 1)) / pageSize;
        List<List<T>>listArray = new ArrayList<List<T>>();
        for (int i = 0; i<page; i++) {
            List<T>subList = new ArrayList<T>();
            for (int j = 0; j<listSize; j++) {
                int pageIndex = ((j + 1) + (pageSize - 1)) / pageSize;
                if (pageIndex == (i + 1)) {
                    subList.add(list.get(j));
                }
                if ((j + 1) == ((j + 1) * pageSize)) {
                    break;
                }
            }
            listArray.add(subList);
        }
        return listArray;
    }

}
package com.zh.util;

import java.util.ArrayList;
import java.util.List;

public class ListUtils {
    public static <T> List<List<T>> splitList(List<T>list, int pageSize) {
        int listSize = list.size();
        int page = (listSize + (pageSize - 1)) / pageSize;
        List<List<T>>listArray = new ArrayList<List<T>>();
        for (int i = 0; i<page; i++) {
            List<T>subList = new ArrayList<T>();
            for (int j = 0; j<listSize; j++) {
                int pageIndex = ((j + 1) + (pageSize - 1)) / pageSize;
                if (pageIndex == (i + 1)) {
                    subList.add(list.get(j));
                }
                if ((j + 1) == ((j + 1) * pageSize)) {
                    break;
                }
            }
            listArray.add(subList);
        }
        return listArray;
    }
}
  • 执行结果

多线程demo结果

面试题

  • 使用继承Thread类还是使用实现Runnable接口好?

辨析:使用实现Runnable接口好,原因实现了接口还可以继续继承,继承了类就不能再继承。即多实现单继承。


文章作者: rep-rebirth
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 rep-rebirth !
评论
评论
  目录