`
xiangshouxiyang
  • 浏览: 50177 次
  • 性别: Icon_minigender_1
  • 来自: 厦门
社区版块
存档分类
最新评论

配合线程池定义可继承的线程变量InheritableThreadLocal

阅读更多

        说到可继承的线程变量,大家可能会想到jdk里的实现java.lang.InheritableThreadLocal。它拥有和线程变量ThreadLocal一样的功能,并且,在当前线程上创建一个新的线程实例Thread时,会把这些线程变量从当前线程传递给新的线程实例。(此时线程变量不再线程安全,需要考虑线程安全问题)

InheritableThreadLocal:

 

public class InheritableThreadLocal<T> extends ThreadLocal<T> {
    /**
     * Computes the child's initial value for this inheritable thread-local
     * variable as a function of the parent's value at the time the child
     * thread is created.  This method is called from within the parent
     * thread before the child is started.
     * <p>
     * This method merely returns its input argument, and should be overridden
     * if a different behavior is desired.
     *
     * @param parentValue the parent thread's value
     * @return the child thread's initial value
     */
    protected T childValue(T parentValue) {
        return parentValue;
    }

    /**
     * Get the map associated with a ThreadLocal.
     *
     * @param t the current thread
     */
    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }

    /**
     * Create the map associated with a ThreadLocal.
     *
     * @param t the current thread
     * @param firstValue value for the initial entry of the table.
     * @param map the map to store.
     */
    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
}

 该类继承了ThreadLocal并重写了和ThreadLocalMap相关的方法。这个ThreadLocalMap其实是java线程对象Thread类的两个属性

 

class Thread implements Runnable {
    .......

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

    /*
     * InheritableThreadLocal values pertaining to this thread. This map is
     * maintained by the InheritableThreadLocal class.
     */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
    ........
}

 这两个ThreadLocalMap其实就是线程变量实际存放的地方,我们用了线程变量set内容,其实就是往ThreadLocalMap里put内容,key是你定义的ThreadLocal本身,value是你往线程变量set的内容。因为内容是存在线程本身上,所以,同一个线程跨了多少个方法都可以访问到,不同线程就访问不到或访问到不同的对象,实现了线程安全。

   ThreadLocal的set方法:

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

 其中

ThreadLocalMap map = getMap(t);

 这行代码,ThreadLocal的getMap:

ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

 而InheritableThreadLocal的getMap:

/**
     * Get the map associated with a ThreadLocal.
     *
     * @param t the current thread
     */
    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }

    即InheritableThreadLocal重写了和ThreadLocalMap相关的方法,其实就是把set的内容放在线程对象的inheritableThreadLocals属性上, 而普通的ThreadLocal则是把set的内容放在线程对象的threadLocals属性上。我们平时新建一个线程,new Thread(),在Thread类实例化的时候调用init()方法:

private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
            /* Determine if it's an applet or not */

            /* If there is a security manager, ask the security manager
               what to do. */
            if (security != null) {
                g = security.getThreadGroup();
            }

            /* If the security doesn't have a strong opinion of the matter
               use the parent thread group. */
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }

        /* checkAccess regardless of whether or not threadgroup is
           explicitly passed in. */
        g.checkAccess();

        /*
         * Do we have the required permissions?
         */
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }

        g.addUnstarted();

        this.group = g;
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        this.name = name.toCharArray();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
        this.target = target;
        setPriority(priority);
        if (parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

        /* Set thread ID */
        tid = nextThreadID();
    }

 其中

if (parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

 这行代码把当前线程对象的inheritableThreadLocals属性传递给新建的线程对象inheritableThreadLocals属性,即实现了线程变量的传递。

    上面是可继承的线程变量inheritableThreadLocals的实现原理。但是在实际的应用场景里,绝大多数都是使用线程池来进行多线程编程,所以jdk提供的inheritableThreadLocals类实用性不高。在线程池(ThreadPoolExecutor)中运行一个Runable实例并不会去新建一个线程,而是把Runable实例添加到队列中(在核心线程数已实例化满的时候),让ThreadPoolExecutor的workers去从队列里拿出Runable实例(这是一个典型的生产者消费者模式),然后运行Runable实例.run()方法。故jdk的inheritableThreadLocals这种实现方式没法适用。

     所以我就想着写一个能在executor上传递的可继承线程变量。而要实现这个功能,单单线程变量本身是不够的,还需要线程池的配合。通过我以前写的博客 http://xiangshouxiyang.iteye.com/blog/2354074 《线程池增强实现》给的思路,设计了自定义的InheritableThreadLocal:

package com.hcd;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 可在特定线程池中继承的线程变量(配合InheritableThreadLocalExecutor使用)
 * Created by cd_huang on 2017/8/28.
 */
public class InheritableThreadLocal<T> extends ThreadLocal<T>{
	private static List<InheritableThreadLocal> inheritableExecutorThreadLocalList =new CopyOnWriteArrayList<>();

	public InheritableThreadLocal(){
        this(true);
	}

	public InheritableThreadLocal(boolean isAdd){
		/**
		 * 一般线程变量本身也不需要被垃圾回收
		 */
		if(isAdd){
			inheritableExecutorThreadLocalList.add(this);
		}
	}

	/**
	 * 从map里取出内容set线程变量(protected方法,可重写,但不提倡直接调用)
	 * @param map
	 */
	protected void setThreadLocalFromMap(Map map){
		T obj = (T)map.get(this);
		this.set(obj);
	}

	/**
	 * get线程变量装到map里(protected方法,可重写,但不提倡直接调用)
	 * @param map
	 */
	protected void getThreadLocalputMap(Map map){
		T obj = this.get();
		map.put(this,obj);
	}

	/**
	 * 移除掉线程变量(protected方法,可重写,但不提倡直接调用)
	 */
	protected void removeThreadLocal(){
        this.remove();
	}

	/**
	 * 把当前线程可传递的线程变量内容放在map里,在task放进线程池队列前调用
	 * @return
	 */
	public static Map<Object,Object> getThreadLocalsMap(){
		Map<Object,Object> threadLocalMap =new HashMap<>();
		List<InheritableThreadLocal> list =inheritableExecutorThreadLocalList;
		for(InheritableThreadLocal threadLocal:list){
			threadLocal.getThreadLocalputMap(threadLocalMap);
		}
		return threadLocalMap;
	}

	/**
	 * 把map里的内容重新set线程变量内容,在task真正运行run方法前调用
	 * @param threadLocalMap
	 */
	public static void setThreadLocalsFromMap(Map<Object,Object> threadLocalMap){
		List<InheritableThreadLocal> list =inheritableExecutorThreadLocalList;
		for(InheritableThreadLocal threadLocal:list){
			threadLocal.setThreadLocalFromMap(threadLocalMap);
		}
	}

	/**
	 * 把setThreadLocalsFromMap方法set的线程变量内容清空,在task真正运行run方法后调用
	 */
	public static void removeThreadLocals(){
		List<InheritableThreadLocal> list =inheritableExecutorThreadLocalList;
		for(InheritableThreadLocal threadLocal:list){
			threadLocal.removeThreadLocal();
		}
	}
}

 与之配合的InheritableThreadLocalExecutor:

package com.hcd;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.concurrent.*;

/**
 * 支持可继承的线程变量的线程池(配合InheritableThreadLocal使用)
 * Created by cd_huang on 2017/8/29.
 */
public class InheritableThreadLocalExecutor extends ThreadPoolExecutor {

	private static Logger logger = LoggerFactory.getLogger(InheritableThreadLocalExecutor.class);

	public InheritableThreadLocalExecutor(int corePoolSize,
	                                      int maximumPoolSize,
	                                      long keepAliveTime,
	                                      TimeUnit unit,
	                                      BlockingQueue<Runnable> workQueue) {
		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
	}

	public InheritableThreadLocalExecutor(int corePoolSize,
	                                      int maximumPoolSize,
	                                      long keepAliveTime,
	                                      TimeUnit unit,
	                                      BlockingQueue<Runnable> workQueue,
	                                      ThreadFactory threadFactory) {
		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
	}
	public InheritableThreadLocalExecutor(int corePoolSize,
	                                     int maximumPoolSize,
	                                     long keepAliveTime,
	                                     TimeUnit unit,
	                                     BlockingQueue<Runnable> workQueue,
	                                     RejectedExecutionHandler handler) {
		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
	}

	public InheritableThreadLocalExecutor(int corePoolSize,
	                                     int maximumPoolSize,
	                                     long keepAliveTime,
	                                     TimeUnit unit,
	                                     BlockingQueue<Runnable> workQueue,
	                                     ThreadFactory threadFactory,
	                                     RejectedExecutionHandler handler) {
		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
	}

	/**
	 * 重写执行线程实例的方法
	 * @param command
	 */
	@Override
	public void execute(Runnable command) {
		if (command == null){
			throw new NullPointerException();
		}
		TaskWithThreadLocal task =new TaskWithThreadLocal(command,InheritableThreadLocal.getThreadLocalsMap());
		super.execute(task);
	}

	/**
	 * 新线程执行时带上指定的线程信息
	 * @param
	 */
	private static class TaskWithThreadLocal implements Runnable{
		private Map<Object,Object> threadLocalMap;
		private Runnable delegate;

		public TaskWithThreadLocal(Runnable delegate, Map<Object,Object> threadLocalMap){
			this.delegate =delegate;
			this.threadLocalMap =threadLocalMap;
		}

		/**
		 * 重写run方法,在执行run方法前设置线程变量,执行run方法后清除线程变量
		 * 同时,打印了运行时的异常信息,并吞掉了delegate.run()运行时的异常,不往外抛
		 * (线程池默认会在任务运行异常后抛出异常,并销毁掉线程对象本身,也就是如果每个任务都运行异常了,那么用线程池的效率还不如直接新建线程,详情见ThreadPoolExecutor类1123行runWorkers方法 )
		 * jdk线程池这样处理的意义应该是希望通过将异常抛出,将异常交给线程对象本身自带的异常处理拦截器或JVM默认的全局异常处理拦截器捕获并处理,
		 * 这里直接去调用拦截器处理,不往外抛异常,避免线程实例的销毁
		 */
		@Override
		public void run() {
			InheritableThreadLocal.setThreadLocalsFromMap(threadLocalMap);
			try{
				try{
					delegate.run();
					//由于callable的call()方法执行过程的异常会被它的调用上级FutureTask的run()方法中处理而使异常不往外抛,为了打印异常日志这里统一进行异常日志打印的处理
					if(delegate instanceof FutureTask){
						try{
							((FutureTask)delegate).get();
						}catch (Throwable e){
							logger.error(e.getMessage(),e);
							Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(),e);
						}
					}
				}catch (Throwable e){
					logger.error(e.getMessage(),e);
					Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(),e);
				}
			}finally {
				InheritableThreadLocal.removeThreadLocals();
			}
		}
	}
}

 在Runable实例放进线程池的时候,new 一个TaskWithThreadLocal类,把线程变量放到threadLocalMap里面。实际放到线程池队列里的是TaskWithThreadLocal,TaskWithThreadLocal类的run方法执行时,会执行真正想执行的Runable实例的run方法。用TaskWithThreadLocal类传递threadLocalMap内容,有点类似jdk的InheritableThreadLocal类在Thread实例化时传递ThreadLocal.ThreadLocalMap inheritableThreadLocals。

     举个例子,比如我们用线程变量记录userId。UserIdUtil:

public class UserIdUtil {
	private static final InheritableThreadLocal<String> userIdLocal = new InheritableThreadLocal<>();

	public static String getUserId(){
		return userIdLocal.get();
	}

	public static void setUserId(String userId){
		userIdLocal.set(userId);
	}

	public static void removeUserId(){
		userIdLocal.remove();
	}
}

只需要把原本的ThreadLocal类改成InheritableThreadLocal类,即可在InheritableThreadLocalExecutor线程池中传递线程变量。

 

 

 

 

 

分享到:
评论
4 楼 xiangshouxiyang 2019-04-23  
应该是主线程的线程变量可以传递给runable方法中。。你这个用例是  runable想传递到主线程。。。当然不行了。。。

Pengjx2014 写道
public class UserIdUtil {

    private static final InheritableThreadLocal<String> userIdLocal = new InheritableThreadLocal<>();

    public static String getUserId() {
        return userIdLocal.get();
    }

    public static void setUserId(String userId) {
        userIdLocal.set(userId);
    }

    public static void removeUserId() {
        userIdLocal.remove();
    }

    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = () -> {
            UserIdUtil.setUserId("12");
            System.out.println("execute");
        };
        InheritableThreadLocalExecutor executor = new InheritableThreadLocalExecutor(1, 2, 1000, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
        executor.execute(runnable);
        Thread.sleep(12);
        System.out.println(userIdLocal.get());
    }
}
执行结果如下:
execute
null


使用方式不对吗

3 楼 Pengjx2014 2018-09-12  
public class UserIdUtil {

    private static final InheritableThreadLocal<String> userIdLocal = new InheritableThreadLocal<>();

    public static String getUserId() {
        return userIdLocal.get();
    }

    public static void setUserId(String userId) {
        userIdLocal.set(userId);
    }

    public static void removeUserId() {
        userIdLocal.remove();
    }

    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = () -> {
            UserIdUtil.setUserId("12");
            System.out.println("execute");
        };
        InheritableThreadLocalExecutor executor = new InheritableThreadLocalExecutor(1, 2, 1000, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
        executor.execute(runnable);
        Thread.sleep(12);
        System.out.println(userIdLocal.get());
    }
}
执行结果如下:
execute
null


使用方式不对吗
2 楼 xiaozhang51 2017-08-30  
厉害了
1 楼 ypzeng2016 2017-08-29  
达神,强无敌

相关推荐

    Java进阶知识点汇总.pdf

    - **InheritableThreadLocal**:继承自`ThreadLocal`,用于创建可继承的线程局部变量。 ##### java.lang.ref包下的类 - **Reference**:基类,所有引用类型都继承自该类。 - **PhantomReference**(虚引用):仅...

    ABB常用机器人技术参数.pdf

    ABB常用机器人技术参数.pdf

    西门子1200 PLC FB284功能块实现多设备控制:V90伺服、相机角度调整及FANUC机器人DP通讯

    内容概要:本文详细介绍了如何利用西门子1200 PLC及其FB284功能块实现对3台V90伺服电机、相机角度调整以及FANUC机器人的控制。主要内容涵盖FB284功能块的基础参数设置、多台伺服电机的具体控制方法、相机角度调整的实现、DP通讯配置FANUC机器人控制,以及PLC程序注解和触摸屏程序的设计。通过具体代码示例和实际操作步骤,帮助读者理解和掌握这一系列控制技术。 适合人群:具备一定PLC基础知识的工控初学者和技术人员。 使用场景及目标:① 学习并掌握FB284功能块的使用方法;② 实现多台V90伺服电机的协同控制;③ 掌握相机角度调整的技术细节;④ 完成FANUC机器人通过DP通讯的控制配置;⑤ 提高PLC程序的可读性和易维护性。 其他说明:文中提供了丰富的代码片段和配置示例,便于读者实践操作。此外,还分享了一些实际项目中的经验和技巧,有助于提高项目的稳定性和效率。

    《计算机常用工具软件(第3版)》第6章--图形图像工具.ppt

    《计算机常用工具软件(第3版)》第6章--图形图像工具.ppt

    未来产业全球未来产业新赛道布局与发展策略分析:涵盖人工智能、量子科技、氢能等关键技术领域

    内容概要:本文由《未来产业新赛道研究报告》整理而成,涵盖了未来产业在全球范围内的发展态势和竞争形势。报告指出,引领型国家通过全方位体制机制创新,在先进制造、人工智能、量子科技、新一代通信等领域建立了全面领先优势。文中引用了麦肯锡和GVR的数据,预测了人工智能和人形机器人等未来产业的巨大经济潜力。报告还详细介绍了国外和国内对未来产业赛道的重点布局,如量子科技、人工智能、先进网络和通信技术、氢能与储能、生物技术等。此外,报告列举了中国重点省市如北京、上海等的具体发展方向,以及知名研究机构对未来产业热点的分析。最后,报告提出了构建我国未来产业重点赛道目录的建议,包括通用人工智能、高级别自动驾驶、商业航天、人形机器人、新型储能、低空经济、清洁氢、算力芯片、细胞与基因治疗和元宇宙等十大重点赛道。 适用人群:对科技趋势和未来产业发展感兴趣的政策制定者、投资者、企业家和研究人员。 使用场景及目标:①帮助政策制定者了解全球未来产业发展动态,为政策制定提供参考;②为企业提供未来产业布局的方向和重点领域;③为投资者提供投资决策依据,识别未来的投资机会;④为研究人员提供未来科技发展趋势的全景图。 其他说明:报告强调了未来产业在全球经济中的重要性,指出了中国在未来产业布局中的战略定位和发展路径。同时,报告呼吁加强国家顶层设计和行业系统谋划,探索建立未来产业技术预见机制,深化央地联动,推动未来产业高质量发展。

    《网络设备安装与调试(神码版)》2交换机的配置.pptx

    《网络设备安装与调试(神码版)》2交换机的配置.pptx

    自动驾驶路径规划:Lattice算法中的参考线、Frenet坐标系及多项式拟合的Matlab与C++实现

    内容概要:本文详细介绍了自动驾驶路径规划中Lattice算法的基础部分,主要包括三个关键概念和技术实现:参考线生成、Frenet坐标系转换和五次多项式拟合。首先解释了参考线的作用及其生成方法,如三次样条插值和平滑曲线生成。其次探讨了Frenet坐标系的优势,展示了如何将笛卡尔坐标系下的车辆位置投影到参考线上,从而简化路径规划问题。最后讨论了五次多项式的应用,强调其能够确保轨迹的光滑性和舒适性,并提供了详细的Matlab和C++代码实现。 适合人群:对自动驾驶技术感兴趣的开发者、研究人员以及有一定编程基础并希望深入了解路径规划算法的人群。 使用场景及目标:适用于研究和开发自动驾驶系统,特别是进行路径规划模块的设计与实现。主要目标是帮助读者掌握Lattice规划的基本原理和技术细节,以便应用于实际工程项目中。 其他说明:文中不仅有理论讲解,还附带了大量的代码实例,便于读者理解和实践。此外,作者提醒了一些常见的陷阱和注意事项,如避免过拟合、选择合适的插值算法等。

    《网络操作系统(Linux)》项目4-磁盘管理.pptx

    《网络操作系统(Linux)》项目4-磁盘管理.pptx

    《计算机应用基础实训指导》实训十八-PowerPoint-2010的动画和切换.pptx

    《计算机应用基础实训指导》实训十八-PowerPoint-2010的动画和切换.pptx

    安川机器人DX100使用说明书.1.pdf

    安川机器人DX100使用说明书.1.pdf

    《计算机专业英语》Unit-3-What-is-Hardware.ppt

    《计算机专业英语》Unit-3-What-is-Hardware.ppt

    汇川H5U-A16自动贴布网胶机的PLC与威纶通触摸屏集成及优化

    内容概要:本文详细介绍了汇川H5U-A16自动贴布网胶机的PLC控制系统及其与威纶通触摸屏的集成方法。主要内容涵盖伺服轴控制、气缸动作、矩阵托盘管理、OEE统计等方面的编程技巧和优化措施。文中展示了如何将复杂的硬件动作抽象为可复用的功能块(FB),并通过参数配置实现灵活的系统控制。此外,还讨论了如何利用威纶通触摸屏进行实时监控和数据分析,以及如何通过合理的IO表管理和注释提高系统的可维护性和扩展性。 适合人群:从事工业自动化领域的工程师和技术人员,尤其是熟悉PLC编程和触摸屏应用的专业人士。 使用场景及目标:适用于需要开发或优化自动贴布网胶机及其他类似自动化设备的企业。主要目标是提升设备的可靠性和效率,降低维护成本,缩短开发周期。 其他说明:本文不仅提供了具体的编程示例,还分享了许多实战经验和技巧,如如何避免常见的错误和陷阱,如何应对特定硬件特性的挑战等。这些内容对于理解和掌握工业自动化系统的开发非常有价值。

    电力系统暂态稳定性分析:基于Matlab/Simulink的故障仿真与优化

    内容概要:本文详细介绍了利用Matlab和Simulink进行电力系统暂态稳定性分析的方法和技术。首先构建了一个单机无穷大系统的仿真模型,涵盖了同步电机、无穷大电网、输电线路等基础模块的搭建。接着深入探讨了不同类型故障(如短路、断线)的配置方法及其对系统稳定性的影响。针对常见的暂态问题,提出了多种解决方案,包括并联补偿器的应用、自动重合闸的设计以及仿真加速技巧。同时,通过具体案例展示了如何调整关键参数来优化系统性能,确保暂态过程中系统的稳定性和可靠性。 适合人群:从事电力系统研究与开发的技术人员,尤其是对电力系统暂态稳定性感兴趣的工程师和研究人员。 使用场景及目标:适用于需要评估电力系统在突发故障情况下的稳定性的场合,帮助用户掌握故障仿真技术,优化系统设计,提高电力系统的可靠性和安全性。 其他说明:文中提供的代码片段和仿真技巧均经过实际验证,能够显著提升仿真的效率和准确性。建议读者结合自己的项目需求灵活应用相关技术和方法。

    FPGA电机控制:基于Verilog与Nios2的永磁同步电机SVPWM控制系统设计

    内容概要:本文详细介绍了利用FPGA实现永磁同步电机(SPM)的SVPWM控制系统的具体实现方法。系统采用Verilog进行底层硬件时序控制,包括SVPWM模块中的扇区判断、PWM生成以及死区时间控制等;Nios2软核处理器则用于执行控制算法,如磁场定向控制(FOC)、Clarke变换和PID调节器。两者通过Avalon总线连接,实现高效的软硬件协同工作。此外,文中还讨论了一些常见的调试技巧和优化方法,如定点数运算、硬件CRC校验模块的应用等。 适合人群:具备一定FPGA开发经验和电机控制理论基础的技术人员,尤其是从事嵌入式系统开发、自动化控制领域的工程师。 使用场景及目标:适用于需要高精度、高性能电机控制的应用场合,如工业自动化设备、机器人关节控制等。目标是通过软硬件协同设计提高系统的实时性和可靠性,降低电流谐波失真,增强抗干扰能力。 其他说明:文中提供了完整的工程源码和技术细节,有助于读者深入理解和实践。同时,作者分享了许多实用的经验教训,帮助读者避开常见陷阱,提高开发效率。

    《移动商务网页设计与制作》第11章--Web-Worker-处理线程.ppt

    《移动商务网页设计与制作》第11章--Web-Worker-处理线程.ppt

    chromedriver-win64-135.0.7049.114.zip

    chromedriver-win64-135.0.7049.114.zip

    《计算机系统维护》第14章--硬盘分区的调整.ppt

    《计算机系统维护》第14章--硬盘分区的调整.ppt

    这篇文章详细探讨了交错并联Buck变换器的设计、仿真及其实现,涵盖了从理论分析到实际应用的多个方面(含详细代码及解释)

    内容概要:本文深入研究了交错并联Buck变换器的工作原理、性能优势及其具体实现。文章首先介绍了交错并联Buck变换器相较于传统Buck变换器的优势,包括减小输出电流和电压纹波、降低开关管和二极管的电流应力、减小输出滤波电容容量等。接着,文章详细展示了如何通过MATLAB/Simulink建立该变换器的仿真模型,包括参数设置、电路元件添加、PWM信号生成及连接、电压电流测量模块的添加等。此外,还探讨了PID控制器的设计与实现,通过理论分析和仿真验证了其有效性。最后,文章通过多个仿真实验验证了交错并联Buck变换器在纹波性能、器件应力等方面的优势,并分析了不同控制策略的效果,如P、PI、PID控制等。 适合人群:具备一定电力电子基础,对DC-DC变换器特别是交错并联Buck变换器感兴趣的工程师和技术人员。 使用场景及目标:①理解交错并联Buck变换器的工作原理及其相对于传统Buck变换器的优势;②掌握使用MATLAB/Simulink搭建交错并联Buck变换器仿真模型的方法;③学习PID控制器的设计与实现,了解其在电源系统中的应用;④通过仿真实验验证交错并联Buck变换器的性能,评估不同控制策略的效果。 其他说明:本文不仅提供了详细的理论分析,还给出了大量可运行的MATLAB代码,帮助读者更好地理解和实践交错并联Buck变换器的设计与实现。同时,通过对不同控制策略的对比分析,为实际工程应用提供了有价值的参考。

    基于单片机的秒表计时器设计(程序+仿真+电路)(51+SEG4+BZ+KEY5) #0400

    包括:源程序工程文件、Proteus仿真工程文件、电路原理图文件、配套技术手册 1、采用51/52单片机(通用)作为主控芯片; 2、数码管前两位显示分钟,后两位显示秒; 3、可以切换正计时/倒计时; 4、可设置倒计时时间,倒计时结束蜂鸣器报警; 5、计时过程中可记录时刻点 (存储十组记录数据),可翻看记录的数据。

    《计算机应用基础实训指导》实训一-文字录入.pptx

    《计算机应用基础实训指导》实训一-文字录入.pptx

Global site tag (gtag.js) - Google Analytics