学习目标
- 线程与进程
- 为什么要使用多线程
- 多线程应用场景
- 多线程创建方式
- 线程生命周期
- 面试总结
- 练习题
线程与进程的区别
总之:进程是所有线程的集合,每个线程是进程中的一条执行路径。
多线程作用和应用场景
- 多线程可以提高程序的效率
- 应用场景:迅雷等多线程下载、分批发送短信等
多线程的创建方式
第一种继承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接口好,原因实现了接口还可以继续继承,继承了类就不能再继承。即多实现单继承。