`
fly_ever
  • 浏览: 154151 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Java并发锁机制

    博客分类:
  • java
阅读更多

       在进行多线程编程时,经常遇到多个线程同时对一个变量进行修改的问题。这时候为了保证不出现意想不到的结果,需要为这些变量加锁,以保证同一时刻只有一个线程能够修改。

      在Java语言中,为了解决这种同步互斥的访问,有两种方法:synchronized和Lock.

1,synchronized

      synchronized是Java语言中的一个关键词,是Java语言内置的特性。

      通过使用synchronized来修饰一个方法或者一个代码块,来获得相应对象的锁,使这个方法或者代码块实现同步互斥访问。

 

      synchronized方法

      当一个线程执行某个对象的synchronized方法时,其他线程不能访问该对象的所有synchronized方法,因为一个对象只有一把锁。但是可以访问非synchronized方法。

      另外,每个类也有一个锁,用了控制对static 成员变量的访问。

      当一个线程访问一个对象的非static synchronized方法时,另一个线程可以访问该对象所属类的static synchronized方法。因为他们分别占用的是对象锁和类锁,不存在同步互斥问题。

 

      synchronized代码块

      当synchronized修饰代码块时,是如下格式使用,即会指定一个对象或者某个属性,需要获取该对象或属性的锁,才能执行代码块:

synchronized(obj) {
         
    }

      synchronized代码块比synchronized方法更加灵活,

 

      当一个线程获得锁,执行到synchronized修饰的代码时,其他的线程只能是一直等待状态。当前已获取锁的线程只有在以下情况下释放锁:

      a, 执行完现在的代码块,线程释放对锁的占用;

      b, 执行时出现异常,JVM会让线程自动释放该锁;

      c, 线程调用wait()从而进入等待状态,自动释放锁。

      

      因此synchronized存在如下一些缺陷:

      a,未获取锁的线程只能一直等待当前的线程执行结束,才能获得锁。无法选择中断,如果当前的线程有一些耗时的操作的话,会很浪费资源。

      b,通过synchronized来实现同步互斥的粒度太大。synchronized修饰的方法或代码块,实际上并不是方法或代码块中的所有代码都需要同步互斥访问。

      c,对资源的操作分为读操作和写操作。两个读操作并不会出现并发互斥问题,如果使用synchronized的话,即使是两个读操作,也会在同一时刻只能有一个读操作能执行,其他的线程执行读操作会被阻塞。

       d,我们无法得知线程是否已经成功获得锁。

       由于存在以上这些问题,因此Java又引进了Lock机制。

2,Lock

       Lock是一个Java接口,基于JDK层面的实现,来实现同步互斥访问。Lock的实现机制是以对volatile变量的读/写和CAS来实现多线程同步互斥访问的。

        volatile:

        由于CPU执行代码时,为了提高效率,会维护一个高速缓存。使用Volatile修饰的变量,都会在执行代码时强制从内存中读取最新变量值,而不是使用高速缓存的值。Volatile还会禁止指令重排。因此保证了两个特性:1,可见性,2有序性。

 

        CAS: Compare  and Swap(比较并交换)

        CAS针对内存的操作:内存值V,旧的预期值A,要修改的新值B,当且仅当旧的预期值A与内存值V相同时,将内存值V修改为新值B,否则什么都不做。

        借助CAS完成这个过程,是通过JNI调用C语言操作CPU底层指令完成的。这样就能保证这些内存操作的原子性。

 

        我们分析一个使用Lock加锁的实例来查看,Lock是如何保证多线程的同步互斥访问的。

 

public Lock lk = new ReentrantLock();
		public void testlock(){
			lk.lock();
			try {
				//需要互斥访问的具体操作代码
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally{
				lk.unlock();
			}
		}
       Lock的使用,是通过调用lk.lock()和lk.unlock()来完成同步互斥访问的。

 

       具体的lock()方法具体执行流程如下,以使用非公平锁为例说明,公平锁的流程稍有区别:



 

获取锁的过程即是对AQS中state状态进行修改的过程。state字段使用volatile修饰,使用CAS的方法compareAndSetState进行修改。如果state字段的值为0时,表示空闲,可以获取锁。大于0时,表示已经获得锁。当锁的模式为独占锁时,表示锁的重入次数,当锁的模式为共享锁时,表示锁当前共享的线程数。

 

3,Lock相关的类分析

了解了获取锁的大概流程后,我们来具体看看与锁相关的类。

    Lock

    Lock是一个接口,有以下几种方法:

    lock():  获取锁;

    lockInterruptibly():  获取锁,如果获取失败在等待锁时,能够响应中断,抛出中断异常。

    trylock(): 获取锁,并且立即返回,获取锁成功则返回True,失败则返回False.

    trylock(long,TimeUnit):与trylock类似,只是获取锁失败后则继续等待指定的时间。并且可以响应中断。

 

    Condition

    Condition:接口,提供了一些方法,以达到对锁更精确的控制。

    await(): 线程等待状态,直到收到信号或者被中断。

    awaitUninterruptibly():线程等待状态,直到收到信号。

    awaitNanos(long):线程等待状态,直到收到信号或者被中断,或者到达指定时间为止。

    await(long,TimeUnit):线程等待状态,直到收到信号或者被中断,或者到达指定时间为止。

    signal(): 唤醒一个等待线程。

    signalAll():唤醒所有等待线程。

 

    AbstractOwnableSynchronizer:抽象类,由一个线程独占的同步器。

 

    AbstractQueuedSynchronizer(AQS):

    继承AbstractOwnableSynchronizer的抽象类,维护一个等待获取锁的线程CLH队列。包含两个内部类: Node和ConditionObject.

     AbstractQueuedSynchronizer-Node类: 等待队列的Node类。

     Node分为两种模式:独占锁和共享锁模式。

     Node分为5种状态: SIGNAL = -1  表示当前节点的后继节点包含的线程需要运行,需要unparking;

                                      CANCELLED = 1 表示当前节点由于过期或者中断被取消了;

                                      CONDITION = -2 表示当前节点在等待condition,即在Condition队列中;

                                      PROPAGATE = -3 表示后续的acquireShared应该执行

                                      0:表示当前节点在同步队列中,等待锁。

   AbstractQueuedSynchronizer-ConditionObject类:Condition接口的实现类。

    signal():移除一个等待时间最长的线程,即Condition等待队列的第一个线程,把这个线程从condition队列一道同步队列中,即把Node状态由CONDITION(-2)修改为0。

    signalAll():同signal一样,移除Condition队列中的所有线程。

 

 

   ReentrantLock

   ReentrantLock是一个可重入锁,Lock接口的实现类。默认创建的ReentrantLock是非公平锁,ReentrantLock(boolean) 支持创建公平锁和非公平锁。包含一个抽象内部类Sync(继承AbstractQueuedSynchronizer)以及其两个子类FairSync和NonfairSync,分别实现公平锁和非公平锁。

     FairSync类和NonfairSync类都只包含lock()和tryAcquire()。这两个方法实现的不同,展示了公平锁和非公平锁的不同。

     FairSync的lock方法直接调用acquire(1),AQS的acquire方法如下,即先调用tryAcquire(arg)和addWaiter(),acquireQueued,然后根据条件调用selfinterrupt方法。

 

    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
      FairSync重写的tryAcquire方法,先判断是否队列中有前置节点,没有前置节点才会尝试获取锁,这样就保证了多个线程获取锁的公平性。获取锁失败则返回False,然后让acquire方法继续完成加入独占锁等待队列。

 

 

       NonfairSync的lock方法则是首先抢占式的获取锁,直接调用compareAndSetState方法,失败之后才会调用acquire(1).

       NonfairSync重写的tryAcquire方法,尝试获取锁,获取失败加入等待队列。

       tryAcquire方法中,都会判断当前线程是否已经获取锁的线程。如果是的话,则会更新state值为state+1,此时就是完成重入锁的过程。

 

     ReentrantReadWriteLock

     同时,JUC包还提供了ReadLock 和 WriteLock.

     ReadLock和WriteLock都作为ReentrantReadWriteLock的内部类,实现了Lock接口。ReentrantReadWriteLock也包含一个抽象类Sync继承自AbstractQueuedSynchronizer,和两个子类FairSync和NonfairSync。在AQS类中,state的值代表锁的状态,在Sync类中,对state进行了如下定义:高16位代表读锁的数量,低16位代表写锁的重入次数。如下图所示:



 

    读锁和写锁的工作过程:

通过ReentrantReadWriteLock能直接获得读锁和写锁,可传入参数决定是公平锁或非公平锁。

 

 public ReentrantReadWriteLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
        readerLock = new ReadLock(this);
        writerLock = new WriteLock(this);
    }
 读锁的获取过程:

 

lock方法直接调用AQS的acquireShared(1),即以共享模式获取锁,然后调用Sync重写的tryAcquireShared(1),进行判断,如果是其他的线程已经持有独占锁,则直接返回-1,获取锁失败,进入共享锁等待队列; 否则获取锁成功。

 

 protected final int tryAcquireShared(int unused) {
            /*
             * Walkthrough:
             * 1. If write lock held by another thread, fail.
             * 2. Otherwise, this thread is eligible for
             *    lock wrt state, so ask if it should block
             *    because of queue policy. If not, try
             *    to grant by CASing state and updating count.
             *    Note that step does not check for reentrant
             *    acquires, which is postponed to full version
             *    to avoid having to check hold count in
             *    the more typical non-reentrant case.
             * 3. If step 2 fails either because thread
             *    apparently not eligible or CAS fails or count
             *    saturated, chain to version with full retry loop.
             */
            Thread current = Thread.currentThread();
            int c = getState();
            //如果是其他的线程已经持有独占锁,则直接返回-1.
            if (exclusiveCount(c) != 0 &&
                getExclusiveOwnerThread() != current)
                return -1;
            //获得共享锁的数量
            int r = sharedCount(c);
            //如果不需要阻塞读锁,并且读锁数量没达到最大值,并且成功更新读锁的数量(在高16位加1);
、          //判断是否需要阻塞读锁,readerShouldBlock(),是公平锁和非公平锁的区别所在。
            if (!readerShouldBlock() &&
                r < MAX_COUNT &&
                compareAndSetState(c, c + SHARED_UNIT)) {
                //r=0,表示当前线程就是第一个读锁。
                if (r == 0) {
                    firstReader = current;
                    firstReaderHoldCount = 1;
                } else if (firstReader == current) {
                //firstReader就是当前线程,则是当前线程重入了,更新firstReaderHoldCount.
                    firstReaderHoldCount++;
                } else {
                //当前线程和第一个线程不同,记录当前线程读锁+1.
                    HoldCounter rh = cachedHoldCounter;
                    if (rh == null || rh.tid != getThreadId(current))
                        cachedHoldCounter = rh = readHolds.get();
                    else if (rh.count == 0)
                        readHolds.set(rh);
                    rh.count++;
                }
                return 1;
            }
            //否则,循环尝试
            return fullTryAcquireShared(current);
        }
 公平锁和非公平锁的区别

 

在调用readerShouldBlock时体现: 非公平锁只需要判断第一个线程节点是否是独占模式;公平锁则是判断队列中是否有前置线程在排队等待。

在调用writeShouldBlack时体现:非公平锁直接返回false,不需要阻塞;公平锁则是判断队列中是否有前置线程在排队等待。

 

写锁的获取过程:

直接调用AQS的acquire(1),然后调用Sync重写的tryAcquire(1),

 

protected final boolean tryAcquire(int acquires) {
            /*
             * Walkthrough:
             * 1. If read count nonzero or write count nonzero
             *    and owner is a different thread, fail.
             * 2. If count would saturate, fail. (This can only
             *    happen if count is already nonzero.)
             * 3. Otherwise, this thread is eligible for lock if
             *    it is either a reentrant acquire or
             *    queue policy allows it. If so, update state
             *    and set owner.
             */
            Thread current = Thread.currentThread();
            int c = getState();
            int w = exclusiveCount(c);
            //c!=0代表当前已经有锁(读锁或者写锁)。
            if (c != 0) {
                // (Note: if c != 0 and w == 0 then shared count != 0)
                //如果写锁为0,或者当前线程不是独占线程,
                //即代表当前有锁,但不符合重入条件,返回false。
                if (w == 0 || current != getExclusiveOwnerThread())
                    return false;
                //写锁数量超过最大数;
                if (w + exclusiveCount(acquires) > MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                // Reentrant acquire
                //设置当前写锁重入数
                setState(c + acquires);
                return true;
            }

            //当前还没有锁的处理过程:
            //写锁应该阻塞或者直接修改标志位获取锁的操作(CAS)失败,返回false
            if (writerShouldBlock() ||
                !compareAndSetState(c, c + acquires))
                return false;
            //成功获取锁,则设置当前线程为独占线程;
            setExclusiveOwnerThread(current);
            return true;
        }
 读锁和写锁的释放unlock,操作流程也跟lock类似,分别调用releaseShared(1)->tryReleaseShared(1)和release(1)->tryRelease(1)。
protected final boolean tryRelease(int releases) {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            int nextc = getState() - releases;
            boolean free = exclusiveCount(nextc) == 0;
            //只有当写锁的数量为0时,才把当前独占线程置为null
            if (free)
                setExclusiveOwnerThread(null);
            setState(nextc);
            return free;
        }
 当把写锁的数量降为0,才能返回true,否则一直返回false。
 
protected final boolean tryReleaseShared(int unused) {
            Thread current = Thread.currentThread();
            if (firstReader == current) {
                // assert firstReaderHoldCount > 0;
                if (firstReaderHoldCount == 1)
                    firstReader = null;
                else
                    firstReaderHoldCount--;
            } else {
                HoldCounter rh = cachedHoldCounter;
                if (rh == null || rh.tid != getThreadId(current))
                    rh = readHolds.get();
                int count = rh.count;
                if (count <= 1) {
                    readHolds.remove();
                    if (count <= 0)
                        throw unmatchedUnlockException();
                }
                --rh.count;
            }
            for (;;) {
                int c = getState();
                int nextc = c - SHARED_UNIT;
                if (compareAndSetState(c, nextc))
                    // Releasing the read lock has no effect on readers,
                    // but it may allow waiting writers to proceed if
                    // both read and write locks are now free.
                    return nextc == 0;
            }
        }
 读锁的释放,只有把读锁的数量降为0,才返回True,否则一直返回false。
LockSupport
  LockSupport类是一个线程阻塞工具类,通过调用Unsafe类的park和unpark功能实现线程的阻塞和唤醒。      LockSupport.park(Object): 阻塞线程,Object代表阻塞线程正在等待的object,方便定位阻塞问题。           LockSupport.unpark(Thread):唤醒线程;
   park方法和unpark方法维护一个信号值,可看做线程的一个运行许可(permit),只有两个值,0和1。默认是0。
当调用unpark方法,permit值设为1,即线程得到可继续运行的许可。
当调用park方法,如果permit值为1,则线程可以继续运行,然后把permit值修改为0,如果permit值为0,则线程没有继续运行的许可,则阻塞当前线程,直至permit值被其他线程修改为1之后,park再将其置为0,然后返回。
public static void main(String[] args) {
		// TODO Auto-generated method stub
			
		System.out.println("main thread start");
		LockSupport.park();
		System.out.println("main thread park");
		
}
//运行结果:
main thread start
 
System.out.println("main thread park");//这句代码并没有执行,而程序也没有结束,只是阻塞了。
//因为默认的permit为0,此时调用LockSupport.park(),
//需要等到其他线程把permit修改为1之后才会继续往下执行.
public static void main(String[] args) {
		// TODO Auto-generated method stub
				
		System.out.println("main thread start");
		LockSupport.unpark(Thread.currentThread());
		LockSupport.park();
		System.out.println("main thread park");
		
	}
//先调用LockSupport.unpark(Thread.currentThread()),则程序并没有阻塞,能正常运行结束。
 
    或者使用另一个线程,调用主线程mainThread.unpark,则主线程能正常执行完成。代码如下所示:
public static void main(String[] args) {
		// TODO Auto-generated method stub
		Producer p1 = new Producer(Thread.currentThread());
		Thread t1 = new Thread(p1);
		t1.start();
		
		System.out.println("main thread start");
		LockSupport.park();
		System.out.println("main thread park");
		
}
public class Producer implements Runnable {

	private Thread mainThread;

	public Producer(Thread mainThread) {
		super();
		this.mainThread = mainThread;
	}

	public void run() {
		// TODO Auto-generated method stub
		System.out.println("start thread");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//在此调用主线程的unpark。则能够唤醒主线程,使之继续往下执行。
		LockSupport.unpark(this.mainThread);
		System.out.println("end thread");
	}

}
 
如果一个线程连续两次调用LockSupport.park(),则这个线程一定会进入阻塞状态。
LockSupport对中断的响应
    通过LockSupport.park() 阻塞线程,可以响应线程的中断,但是不会抛出中断异常InterruptedException
public static void main(String[] args) {
		// TODO Auto-generated method stub
		Producer p1 = new Producer(Thread.currentThread());
		Thread t1 = new Thread(p1);
		t1.start();
		
		System.out.println("main thread start");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("main thread start to interrupt subthread");
		t1.interrupt();
		System.out.println("main thread end");
	}


public class Producer implements Runnable {

	private Thread mainThread;

	public Producer(Thread mainThread) {
		super();
		this.mainThread = mainThread;
	}

	public void run() {
		// TODO Auto-generated method stub
		System.out.println("start subthread");
		LockSupport.park();
		System.out.println("end subthread, subthread interrupted status:" + Thread.currentThread().isInterrupted());
	}

}
 
LockSupport的park和unpark与wait,notify,notifyAll的区别:
1,  wait,notify,notifyAll的使用依赖监视器,即需要写在同步代码synchronized里面。导致需要维护一个共享的对象,而LockSupport则不需要。
2,wait和notify的调用有严格的先后顺序,而LockSupport中维护的是一个许可,不需要担心调用顺序,unpark在park之前或者之后,都能唤醒线程。
 
StampedLock:
   StampedLock是JDK1.8新增的类。ReentrantReadWriteLock虽然分别提供了读写锁,读和读之间能够完全并发,但是读和写还是有冲突,如果多读少写的情况下,容易造成写锁的饥饿状态。因此StampedLock继续对锁机制进行了改进。
   StampedLock有三种锁状态:1,写锁。2,悲观读锁,3,乐观读锁。注意StampedLock提供的锁都是不可重入锁。
    StampedLock每次获取锁时,都会返回一个stamp,使用乐观读锁时,并不真正去通过CAS修改锁的状态,只是判断当前是否有写锁,没有写锁的话,就会直接返回一个非0的stamp值,在进行读操作前,再通过validate验证下锁,就可以读操作了。
 
 
Semaphore多线程使用时的一种基于计数的信号量机制。
在线程操作前,先申请获取许可,申请成功就继续线程处理,线程处理完后归还申请。
如果调用acquire()申请时,已申请的线程已超过阈值,则申请阻塞,直至能申请成功为止,不能结束该方法,也不能返回。
acquire():能响应中断
acquireUninterruptibly():不能响应中断
tryAcquire():不阻塞,直接返回结果,获取申请是否成功。
release:归还申请
 
 
 
  • 大小: 130.2 KB
  • 大小: 42.2 KB
分享到:
评论

相关推荐

    MATLAB实现基于YALMIP+CPLEX的电动汽车削峰填谷多目标优化调度

    内容概要:本文详细介绍了利用MATLAB及其工具箱YALMIP和CPLEX进行电动汽车削峰填谷的多目标优化调度方法。通过设定不同的权重系数,将电池损耗成本、峰谷差以及负荷波动三个关键因素整合为目标函数,实现了对电动汽车充放电行为的有效管理。文中不仅展示了具体的数学模型和代码实现,还讨论了求解过程中的一些技巧和注意事项,如充放电互斥约束的设计、求解器的选择与配置等。此外,作者通过实例演示了优化前后负荷曲线的变化情况,证明了该方法能够显著改善电网负荷分布。 适合人群:从事智能电网、能源管理系统开发的研究人员和技术人员,尤其是那些希望深入了解电动汽车参与电网调度的技术细节的人。 使用场景及目标:适用于需要解决电网负荷不均衡问题的实际应用场景,旨在通过合理的电动汽车调度策略,在保障用户利益的同时提高整个电力系统的运行效率和稳定性。 其他说明:文中提到的方法可以作为研究和开发类似系统的参考案例,同时也强调了在实际应用中需要根据具体情况进行调整和优化。

    三菱FX5U PLC ST语言加密与授权管理系统的解析及应用

    内容概要:本文详细介绍了三菱FX5U PLC的加密与授权管理系统,主要基于ST(Structured Text)语言实现。文中展示了多个关键模块的设计思路和技术细节,如授权验证逻辑、时间预警机制、通信检测、程序块加密以及硬件抽象层设计。授权验证逻辑通过预存密钥数组进行分期授权和永久授权的管理;时间预警机制利用定时器和系统时钟确保授权到期前提醒用户;通信检测采用心跳包机制保障通信安全;程序块加密通过预处理器指令和无效代码混淆保护源代码;硬件抽象层设计使得系统能够适应不同型号的PLC。此外,还讨论了移植性和客户破解防范措施。 适合人群:从事工业控制系统开发的技术人员,尤其是对PLC编程和系统安全有兴趣的专业人士。 使用场景及目标:适用于需要对PLC系统进行高级加密和授权管理的工程项目,旨在提高系统的安全性和可控性,防止未经授权的操作和访问。 其他说明:本文不仅提供了具体的代码实现,还深入探讨了设计理念和技术难点,帮助读者全面理解并应用于实际项目中。

    OpenSSH 是一个免费的 SSH 连接工具,广泛用于安全的远程登录和文件传输 本项目提供了RPM包的形式对OpenSSH进行升级,以确保您使用的是最新版本 特性 提供 RPM 包升级到最新版本

    1. 开启root登录 2.启用PAM 3.关闭selinux 4.操作以下内容 cat > /etc/pam.d/sshd << EOF #%PAM-1.0 auth required pam_sepermit.so auth include password-auth account required pam_nologin.so account include password-auth password include password-auth session required pam_limits.so session required pam_selinux.so close session required pam_loginuid.so session optional pam_keyinit.so force revoke session include password-auth EOF

    Delphi 12.3控件之Pascal Expert 9.12.14 For Delphi 12 CS.7z

    Delphi 12.3控件之Pascal Expert 9.12.14 For Delphi 12 [CS].7z

    数据科学领域中层次聚类算法的详细解析及其Python实现

    内容概要:本文全面介绍了层次聚类算法,一种无监督学习方法,广泛应用于数据挖掘、机器学习和模式识别。文章首先阐述了聚类算法的基础理论,特别是层次聚类的独特之处——生成树状图展示数据点之间的相似性关系。随后,详细讲解了凝聚型层次聚类的工作原理,包括初始化、合并和重复步骤,并探讨了多种距离度量方法(如单链、完全链、平均链和重心法)。文中通过Python代码实例展示了如何使用SciPy库进行层次聚类,并生成树状图。此外,文章还讨论了层次聚类在生物信息学、图像分割和文本数据分析中的具体应用场景,以及评估聚类质量的方法(如轮廓系数和Calinski-Harabasz指数)。最后,文章总结了层次聚类的优点和缺点,并展望了未来的优化方向和挑战。 适合人群:数据科学家、机器学习工程师、研究人员和其他对聚类算法感兴趣的从业者。 使用场景及目标:①理解层次聚类的基本原理和工作流程;②掌握如何使用Python实现层次聚类;③学会评估聚类质量和优化聚类性能;④了解层次聚类在不同领域的应用。 其他说明:本文不仅提供了理论知识,还包括丰富的代码示例,使读者能够在实践中加深对层次聚类的理解。同时,文章还探讨了层次聚类

    30kW三相PFC充电桩核心技术解析:基于TMS320F28069的高精度控制与算法实现

    内容概要:本文深入探讨了一款30kW三相PFC充电桩的技术细节,涵盖硬件设计、主控芯片选择、关键算法实现及其性能表现。文中详细介绍了主电路采用的T型三相维也纳结构,主控芯片TMS320F28069的特点及其应用,包括CLA协处理器的优势。同时,对锁相环算法、PWM控制、中点平衡控制等进行了详细的解释,并提供了相应的代码示例。此外,还分享了一些实战经验和注意事项,如DQ变换的正确实现、滞回控制的应用技巧等。最后,附上了27页的量产测试报告,展示了该程序在实际生产环境中的稳定性和可靠性。 适合人群:从事电力电子、嵌入式系统开发的专业人士,尤其是对大功率充电设备感兴趣的工程师和技术爱好者。 使用场景及目标:适用于希望深入了解30kW三相PFC充电桩内部工作机制的研发人员。通过学习本文,可以掌握从硬件设计到软件实现的完整流程,为类似项目提供宝贵的参考资料和技术支持。 其他说明:本文不仅提供了理论知识,还结合了大量的实战经验,帮助读者避免常见陷阱,提高开发效率。

    ssm个人健康信息管理lw+ppt.zip

    Java项目基于ssm框架的课程设计,包含LW+ppt

    基于YOLOv8深度学习的布料外观缺陷检测系统(带GUI界面)(Python源码+Pyqt5界面+2100多张标注好的数据集+安装使用教程+训练好的模型+评估指标曲线+演示图片视频),开箱即用

    基于YOLOv8深度学习的布料外观缺陷检测系统(带GUI界面)(Python源码+Pyqt5界面+2100多张标注好的数据集+安装使用教程+训练好的模型+评估指标曲线+演示图片视频),开箱即用 高准确率,6个类别:带沙,断沙,面球,破洞,脱沙,污渍。 1、该资源内项目代码都经过本人训练测试并运行成功,功能都OK的情况下才上传的,请放心下载使用!有问题请私信我,提供技术支持。 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载使用,也适合小白学习进阶,当然也可作为毕设项目、课程设计、日常作业、实战项目演示等。 3、可参考学习,也可在此基础上略做修改,以实现其他功能,也可直接用于毕设、课设和作业等。 升级版YOLO11资源详情展示:https://blog.csdn.net/m0_37302966/article/details/146467358 更多资源下载:https://blog.csdn.net/m0_37302966/article/details/146387003

    计算机大学生如何让别人能够访问到自己的项目(springboot+vue简单版)

    远程连接工具

    9月最新开心彩虹易码支付最新版转账付款功能,支持通过插件扩展

    9月最新开心彩虹易码支付最新版转账付款功能,支持通过插件扩展 1. 新增微信公众号消息提醒功能 2. 重构转账付款功能,支持通过插件扩展 3. 商户后台新增代付功能 4. 后台新增付款记录列表 5. 支付宝插件新增预授权支付 6. 优化支付通道列表,支持翻页与快速复制通道 7. 新增创建订单人机验证,防止被恶意创建订单 8. 修复其他一些已知问题 9. 增加两套前端模板以及订单查询 安装教程:上传源码,访问即可安装。

    MATLAB中正余弦算法优化广义回归神经网络(SCA-GRNN)用于数据回归预测的技术解析

    内容概要:本文详细介绍了如何利用正余弦算法(SCA)优化广义回归神经网络(GRNN)进行数据回归预测的方法。首先解释了GRNN的基本结构及其平滑因子σ的重要性,然后展示了SCA通过正弦和余弦震荡寻找全局最优σ值的具体实现步骤。文中提供了完整的MATLAB代码片段,涵盖数据预处理、适应度函数设计、SCA参数更新规则以及最终模型评估等多个方面。实验结果显示,经过SCA优化后的GRNN在多个测试案例中均表现出更高的预测精度,特别是在处理中小型数据集时优势明显。 适合人群:具有一定MATLAB编程经验的数据分析师、机器学习爱好者及研究人员。 使用场景及目标:适用于需要提高数据回归预测准确性的小型至中型数据集处理任务。主要目标是通过自动化参数寻优减少人工干预,从而获得更加精确稳定的预测结果。 其他说明:作者强调了数据归一化的必要性和SCA参数配置的最佳实践,如适当设定搜索范围、种群大小和迭代次数等。此外还提到可以通过引入交叉验证等方式进一步增强模型鲁棒性。

    MainActivity.kt

    MainActivity.kt

    Delphi 12.3控件之ReportBuilder v22.04 for D10.2-D12.7z

    Delphi 12.3控件之ReportBuilder v22.04 for D10.2-D12.7z

    芯原微电子笔试试卷真题,IC工程师笔试真题,自己下载

    芯原微电子笔试试卷真题,IC工程师笔试真题,自己下载。

    数据集相关资源,AFQMC 蚂蚁金融语义相似度数据集,AFQMC(Ant Financial Question Matching Corpus)蚂蚁金融语义相似度数据集,用于问题相似度计算

    数据集相关资源,AFQMC 蚂蚁金融语义相似度数据集,AFQMC(Ant Financial Question Matching Corpus)蚂蚁金融语义相似度数据集,用于问题相似度计算。

    TIDBPTCA真题.html

    TIDBPTCA真题.html

    COMSOL等离子体模块中针-针电极空气流注放电模型的详细实现与应用

    内容概要:本文深入探讨了利用COMSOL Multiphysics软件中的等离子体模块建立针-针电极空气流注放电模型的方法。文中详细介绍了模型的几何结构设定、物理场配置(如电子、正负离子的载流子选择)、化学反应的设置(含21组带电粒子反应)以及Helmholtz光电离过程的具体实现方法。此外,还提供了关于求解器配置、边界条件处理等方面的实用技巧,确保模型能够稳定且高效地运行。通过该模型可以直观地观察到空气流注放电过程中的电场分布、粒子密度变化等情况。 适合人群:从事等离子体物理研究的专业人士,特别是那些对高压放电现象感兴趣的科研工作者和技术人员。 使用场景及目标:适用于研究等离子体行为及其在不同条件下的演化规律,特别是在针-针电极间的空气流注放电特性方面。该模型可用于验证理论预测、探索新型放电器件的设计思路,以及优化现有设备的工作性能。 其他说明:文中不仅提供了详细的建模步骤,还包括了一些实际操作中的注意事项和优化建议,有助于提高仿真的成功率并减少计算成本。同时,作者鼓励读者尝试调整模型参数以获得不同的仿真效果,从而进一步加深对该领域的理解。

    Java开发中的单元测试、日志框架、Maven和Tomcat详解

    内容概要:本文详细介绍了Java开发中的四个关键技术领域:JUnit单元测试、Java日志框架、Maven项目管理和Tomcat服务器。首先,阐述了JUnit的基本概念、核心功能及其在提高代码质量和开发效率中的重要作用。接着,讲解了几种主流的日志框架(如Log4j、Log4j2、Logback和SLF4J)的特点和使用方法。随后,深入探讨了Maven的功能特点、依赖管理和项目结构,强调了其在简化项目构建和管理方面的优势。最后,介绍了Tomcat服务器的工作原理、特点及其在Java Web应用中的应用。 适合人群:具备一定Java编程基础的研发人员,尤其是希望深入了解Java开发工具链的专业人士。 使用场景及目标:① 学习如何使用JUnit进行高效的单元测试;② 掌握常见Java日志框架的配置和使用;③ 理解Maven的依赖管理和项目管理机制;④ 掌握Tomcat服务器的配置和使用,搭建Java Web应用。 其他说明:本文不仅涵盖了理论知识,还包括了大量的实践案例和代码示例,有助于读者更好地理解和应用这些技术。

    “传统节日”宣传介绍教学课件.pptx

    “传统节日”宣传介绍教学课件

    三菱Q系PLC伺服定位控制FB程序编写指南:结构化文本实现稳定高效的工业自动化控制

    内容概要:本文详细介绍了一套用于三菱Q系列PLC的伺服定位控制功能块(FB)程序的设计与实现方法。该程序采用结构化文本(Structured Text)编写,分为参数配置、运动控制和状态监控三大模块。文中强调了程序的结构清晰、注释详尽以及实战经验的应用,确保系统的稳定性和易维护性。具体亮点包括:轴参数配置结构体、运动控制逻辑的状态检测、异常处理机制、速度参数的安全处理、位置窗口比较法的位置到达判定、详细的报警处理模块等。此外,作者还分享了许多实际应用中的经验和技巧,如加减速曲线的选择、软极限保护、伺服使能控制等。 适合人群:从事工业自动化控制领域的工程师和技术人员,尤其是对三菱Q系列PLC和伺服控制系统有一定了解并希望提升编程技能的人群。 使用场景及目标:适用于需要编写高效稳定的三菱Q系列PLC伺服定位控制程序的场合。目标是帮助工程师掌握如何设计结构清晰、易于维护且性能可靠的伺服控制程序,减少调试时间和提高系统稳定性。 其他说明:本文不仅提供了具体的编程指导,还分享了大量来自生产一线的实际经验,有助于读者更好地理解和应对实际工作中可能出现的问题。

Global site tag (gtag.js) - Google Analytics