学习目标
- 线程与进程
 - 为什么要使用多线程
 - 多线程应用场景
 - 多线程创建方式
 - 线程生命周期
 - 面试总结
 - 练习题
 
线程与进程的区别

总之:进程是所有线程的集合,每个线程是进程中的一条执行路径。
多线程作用和应用场景
- 多线程可以提高程序的效率
 - 应用场景:迅雷等多线程下载、分批发送短信等
 
多线程的创建方式
第一种继承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()获取当前线程IDgetName()获取当前线程名称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;
    }
}
- 执行结果
 

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