网站建设基本技术,wordpress破解登录密码破解,青岛模板化网站建设,山西大同最新通告今天一、ReentrantLock
特点#xff1a;独占、可重入、公平/非公平、可中断、支持多个条件变量
1、常用api
ReentrantLock实现了Lock接口#xff0c;Lock类规范定义了如下方法 lock()#xff1a;获取锁#xff0c;调用该方法的线程会获取锁#xff0c;当锁获得后#xff0…一、ReentrantLock
特点独占、可重入、公平/非公平、可中断、支持多个条件变量
1、常用api
ReentrantLock实现了Lock接口Lock类规范定义了如下方法 lock()获取锁调用该方法的线程会获取锁当锁获得后该方法返回lockInterruptibly()可中断得获取锁和lock()方法不同之处在于该方法会响应中断即在锁的获取中可以中断当前线程tryLock()尝试非阻塞的获取锁调用该方法后立即返回。如果能够获取到返回true否则返回falsetryLock(long, TimeUnit)超时获取锁当前线程在三种情况下会被返回1、当前线程在超时时间内获取了锁 2、当前线程在超时时间内被中断 3、超时时间结束返回falseunLock()释放锁newCondition()获取等待通知组件该组件和当前的锁绑定当前线程只有获取了锁才能调用该组件的await()方法而调用后当前线程将释放锁
2、使用
使用范式 如果把lock.lock()加锁操作放在try里面可能try里面其它代码导致加锁失败最后lock.unlock()解锁操作时由于没加锁成功抛出IllegalMonitorStateException异常
public class ReentrantLockTest {private final ReentrantLock lock new ReentrantLock();// 库存数量private static int inventoryQuantity 5;// 减库存方法private void reduceInventory() {lock.lock();try {if (inventoryQuantity 0) {try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()购买了商品剩余库存数--inventoryQuantity);} else {System.out.println(Thread.currentThread().getName()已经没有库存了);}} finally {lock.unlock();}}public static void main(String[] args) throws InterruptedException {ReentrantLockTest lockTest new ReentrantLockTest();for (int i 1; i 10; i) {new Thread(() - {lockTest.reduceInventory();}).start();}}
}
打印结果
Thread-0购买了商品剩余库存数4 Thread-1购买了商品剩余库存数3 Thread-2购买了商品剩余库存数2 Thread-3购买了商品剩余库存数1 Thread-5购买了商品剩余库存数0 Thread-4已经没有库存了 Thread-6已经没有库存了 Thread-9已经没有库存了 Thread-7已经没有库存了 Thread-8已经没有库存了
3、公平锁和非公平锁
ReentrantLock支持公平锁和非公平锁默认是非公平锁
公平锁线程在获取锁时按照等待的先后顺序获取锁非公平锁线程在获取锁时不按照等待的先后顺序获取锁而是随机获取锁
ReentrantLock lock new ReentrantLock(); //参数默认false非公平锁
ReentrantLock lock new ReentrantLock(true); //公平锁非公平锁在加锁允许先进行CAS操作判断一次公平锁则直接进入acquire()方法
4、可重入锁
可重入锁又名递归锁是指在同一个线程在外层方法获取锁的时候再进入该线程的内层方法会自动获取锁前提锁对象得是同一个不会因为之前已经获取过还没释放而阻塞。ReentrantLock和synchronized都是可重入锁可重入锁可一定层度避免死锁。在实际开发中可重入锁常常 应用于递归操作、调用同一个类中的其他方法、锁嵌套等场景中
public class ReentrantLockRecursiveTest {private final ReentrantLock lock new ReentrantLock();/*** 递归调用5次* param num*/public void recursiveCall(int num) {lock.lock();try {if (num 5) {return;}System.out.println(执行递归调用第num次);recursiveCall(num);} finally {lock.unlock();}}public static void main(String[] args) {ReentrantLockRecursiveTest lockRecursiveTest new ReentrantLockRecursiveTest();lockRecursiveTest.recursiveCall(1);}
}
打印结果
执行递归调用第1次 执行递归调用第2次 执行递归调用第3次 执行递归调用第4次 执行递归调用第5次
5、基于Condition的等待唤醒机制
java.util.concurrent类库中提供Condition类实现线程之间的协调。调用Condition.await()方法使线程等待其它线程调用Condition.signal()或Condition.signalAll()方法唤醒等待的线程
注意调用Condition的await()和signal()方法都必须在lock保护之内
案例基于ReentrantLock和Condition实现一个简单队列
public class ReentrantLockConditionTest {public static void main(String[] args) {Queue queue new Queue(5);// 创建生产者线程new Thread(new Producer(queue)).start();// 创建消费者线程new Thread(new Customer(queue)).start();}}class Queue {private Object[] items;int size 0;int takeIndex;int putIndex;private ReentrantLock lock;public Condition notEmpty;public Condition notFull;public Queue(int capacity) {this.items new Object[capacity];lock new ReentrantLock();notEmpty lock.newCondition();notFull lock.newCondition();}/*** 生产者方法* param value* throws InterruptedException*/public void put(Object value) throws InterruptedException {lock.lock();try {while (size items.length) {// 队列满了 进入等待notFull.await();}items[putIndex] value;if (putIndex items.length) {putIndex 0;}size;notEmpty.signal();// 队列中只要添加一个对象就唤醒消费者线程} finally {System.out.println(producer生产value);lock.unlock();}}/*** 消费者方法* return* throws InterruptedException*/public Object take() throws InterruptedException {lock.lock();try {// 队列空了就让消费者等待while (size 0) {notEmpty.await();}Object value items[takeIndex];items[takeIndex] null;if (takeIndex items.length) {takeIndex 0;}size--;notFull.signal();// 队列中只要消费一个对象就唤醒生产者线程return value;} finally {lock.unlock();}}
}class Producer implements Runnable {private Queue queue;public Producer(Queue queue) {this.queue queue;}Overridepublic void run() {try {while (true) {Thread.sleep(1000);queue.put(new Random().nextInt(1000));}} catch (InterruptedException e) {e.printStackTrace();}}
}class Customer implements Runnable {private Queue queue;public Customer (Queue queue) {this.queue queue;}Overridepublic void run() {try {while (true) {Thread.sleep(2000);System.out.println(consumer消费 queue.take());}} catch (InterruptedException e) {e.printStackTrace();}}}
6、应用场景总结
ReentrantLock具体应用场景如下
解决多线程竞争资源的问题例如多个线程同时对同一个数据库进行写操作可以使用ReentrantLock保证每次只有一个线程能够写入。实现多线程任务的顺序执行例如在一个线程执行完某个任务后再让另一个线程执行任务。实现多线程等待/通知机制例如在某个线程执行完某个任务后通知其他线程继续执行任务。
二、Semaphore
Semaphore信号量是一种用于多线程编程的同步工具用于控制同时访问某个资源的线程数量。 Semaphore维护了一个计数器线程可以通过调用acquire()方法来获取Semaphore中的许可证当计数器为0时调用acquire()的线程将被阻塞直到有其他线程释放许可证线程可以通过调用release()方法来释放Semaphore中的许可证这会使Semaphore中的计数器增加从而允许更多的线程访问共享资源。
1、常用api permits 表示许可证的数量资源数fair 表示公平性如果这个设为 true 的话下次执行的线程会是等待最久的线程acquire() 表示阻塞并获取许可tryAcquire() 方法在没有许可的情况下会立即返回 false要获取许可的线程不会阻塞release() 表示释放许可
2、使用
public class SemaphoreTest {// 定义两个资源数private static Semaphore semaphore new Semaphore(2);private static Executor executor Executors.newFixedThreadPool(10);public static void main(String[] args) {for (int i 0; i 10; i) {executor.execute(() - fluidControl());}}public static void fluidControl2() {try {// acquire()会构建同步等待队列semaphore.acquire();System.out.println(请求服务成功);Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();} finally {semaphore.release();}}public static void fluidControl() {// tryAcquire()直接CAS返回if (!semaphore.tryAcquire()) {System.out.println(请求被流控了);return;}try {System.out.println(请求服务成功);Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();} finally {semaphore.release();}}
}
3、应用场景
以下是一些使用Semaphore的常见场景
限流Semaphore可以用于限制对共享资源的并发访问数量以控制系统的流量。资源池Semaphore可以用于实现资源池以维护一组有限的共享资源。
三、CountDownLatch
CountDownLatch闭锁是一个同步协助类允许一个或多个线程等待直到其他线程完成操作集。 CountDownLatch使用给定的计数值count初始化。await方法会阻塞直到当前的计数值count由于countDown方法的调用达到0count为0之后所有等待的线程都会被释放并且随后对await方法的调用都会立即返回。这是一个一次性现象 —— count不会被重置。
1、常用api CountDownLatch(int)构造方法初始化count数await()等待count减到0后继续往后执行await()等待指定时长count值还没减到0不再等待继续执行countDown()每调用一次count就会减1减到0为止
2、使用
public class CountDownLatchTest {private static int[] values {30, 20, 65, 23, 45};private static int result 0;private static CountDownLatch coming new CountDownLatch(values.length);public static void main(String[] args) throws InterruptedException {for (int i 0; i values.length; i) {int tempI i;new Thread(() - {result values[tempI];System.out.println(Thread.currentThread().getName()线程计算的结果集是result);coming.countDown();}, Thread_i).start();}coming.await();System.out.println(汇总结果集是result);}
}
3、应用场景
以下是使用CountDownLatch的常见场景
并行任务同步CountDownLatch可以用于协调多个并行任务的完成情况确保所有任务都完成后再继续执行下一步操作。多任务汇总CountDownLatch可以用于统计多个线程的完成情况以确定所有线程都已完成工作。资源初始化CountDownLatch可以用于等待资源的初始化完成以便在资源初始化完成后开始使用。
四、CyclicBarrier
CyclicBarrier回环栅栏或循环屏障是 Java 并发库中的一个同步工具通过它可以实现让一组线程等待至某个状态屏障点之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后CyclicBarrier可以被重用。
1、常用api // parties表示屏障拦截的线程数量每个线程调用 await 方法告诉 CyclicBarrier 我已经到达了屏障然后当前线程被阻塞。public CyclicBarrier(int parties)// 用于在线程到达屏障时优先执行 barrierAction方便处理更复杂的业务场景(该线程的执行时机是在到达屏障之后再执行)public CyclicBarrier(int parties, Runnable barrierAction) //指定数量的线程全部调用await()方法时这些线程不再阻塞
// BrokenBarrierException 表示栅栏已经被破坏破坏的原因可能是其中一个线程 await() 时被中断或者超时
public int await() throws InterruptedException, BrokenBarrierException
public int await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException//循环 通过reset()方法可以进行重置
public void reset()
2、使用
public class CyclicBarrierTest {public static void main(String[] args) {ExecutorService executorService Executors.newFixedThreadPool(5);CyclicBarrier cyclicBarrier new CyclicBarrier(5,() - System.out.println(人齐了准备发车));for (int i 0; i 10; i) {final int id i1;executorService.submit(new Runnable() {Overridepublic void run() {try {System.out.println(id号马上就到);int sleepMills ThreadLocalRandom.current().nextInt(2000);Thread.sleep(sleepMills);System.out.println(id 号到了上车);cyclicBarrier.await();} catch (InterruptedException e) {e.printStackTrace();}catch(BrokenBarrierException e){e.printStackTrace();}}});}}
}3、应用场景
以下是一些常见的 CyclicBarrier 应用场景
多线程任务CyclicBarrier 可以用于将复杂的任务分配给多个线程执行并在所有线程完成工作后触发后续操作。数据处理CyclicBarrier 可以用于协调多个线程间的数据处理在所有线程处理完数据后触发后续操作。
4、CyclicBarrier 与 CountDownLatch 区别
CountDownLatch 是一次性的CyclicBarrier 是可循环利用的CountDownLatch 参与的线程的职责是不一样的有的在倒计时有的在等待倒计时结束。CyclicBarrier 参与的线程职责是一样的。
五、Exchanger
Exchanger是一个用于线程间协作的工具类用于两个线程间交换数据。具体交换数据是通过exchange方法来实现的如果一个线程先执行exchange方法那么它会同步等待另一个线程也执行exchange方法这个时候两个线程就都达到了同步点两个线程就可以交换数据。 1、常用api
public V exchange(V x) throws InterruptedException
public V exchange(V x, long timeout, TimeUnit unit) throws InterruptedException, TimeoutException
V exchange(V v)等待另一个线程到达此交换点除非当前线程被中断然后将给定的对象传送给该线程并接收该线程的对象。V exchange(V v, long timeout, TimeUnit unit)等待另一个线程到达此交换点或者当前线程被中断——抛出中断异常又或者是等候超时——抛出超时异常然后将给定的对象传送给该线程并接收该线程的对象。
2、使用
public class ExchangerTest {private static Exchanger exchanger new Exchanger();static String goods 电脑;static String money $4000;public static void main(String[] args) throws InterruptedException {System.out.println(准备交易一手交钱一手交货...);// 卖家new Thread(new Runnable() {Overridepublic void run() {System.out.println(卖家到了已经准备好货 goods);try {String money (String) exchanger.exchange(goods);System.out.println(卖家收到钱 money);} catch (Exception e) {e.printStackTrace();}}}).start();Thread.sleep(3000);// 买家new Thread(new Runnable() {Overridepublic void run() {try {System.out.println(买家到了已经准备好钱 money);String goods (String) exchanger.exchange(money);System.out.println(买家收到货 goods);} catch (Exception e) {e.printStackTrace();}}}).start();}
}
3、应用场景
Exchanger 可以用于各种应用场景具体取决于具体的 Exchanger 实现。常见的场景包括
数据交换在多线程环境中两个线程可以通过 Exchanger 进行数据交换。数据采集在数据采集系统中可以使用 Exchanger 在采集线程和处理线程间进行数据交换。
六、Phaser
Phaser阶段协同器是一个Java实现的并发工具类用于协调多个线程的执行。它提供了一些方便的方法来管理多个阶段的执行可以让程序员灵活地控制线程的执行顺序和阶段性的执行。Phaser可以被视为CyclicBarrier和CountDownLatch的进化版它能够自适应地调整并发线程数可以动态地增加或减少参与线程的数量。所以Phaser特别适合使用在重复执行或者重用的情况。 1、常用api
构造方法
Phaser(): 参与任务数0Phaser(int parties) :指定初始参与任务数Phaser(Phaser parent) :指定parent阶段器 子对象作为一个整体加入parent对象 当子对象中没有参与者时会自动从parent对象解除注册Phaser(Phaser parentint parties) : 集合上面两个方法
增减参与任务数方法
int register() 增加一个任务数返回当前阶段号。int bulkRegister(int parties) 增加指定任务个数返回当前阶段号。int arriveAndDeregister() 减少一个任务数返回当前阶段号。
到达、等待方法
int arrive() 到达(任务完成)返回当前阶段号。int arriveAndAwaitAdvance() 到达后等待其他任务到达返回到达阶段号。int awaitAdvance(int phase) 在指定阶段等待(必须是当前阶段才有效)int awaitAdvanceInterruptibly(int phase) 阶段到达触发动作int awaitAdvanceInterruptiBly(int phaselong timeoutTimeUnit unit)protected boolean onAdvance(int phaseint registeredParties)类似CyclicBarrier的触发命令通过重写该方法来增加阶段到达动作该方法返回true将终结Phaser对象。
2、使用
public class PhaserBatchProcessorTest {public static void main(String[] args) {final Phaser phaser new Phaser() {//重写该方法来增加阶段到达动作Overrideprotected boolean onAdvance(int phase, int registeredParties) {// 参与者数量去除主线程int staffs registeredParties - 1;switch (phase) {case 0:System.out.println(大家都到公司了出发去公园人数 staffs);break;case 1:System.out.println(大家都到公园门口了出发去餐厅人数 staffs);break;case 2:System.out.println(大家都到餐厅了开始用餐人数 staffs);break;}// 判断是否只剩下主线程一个参与者如果是则返回true代表终止return registeredParties 1;}};// 注册主线程 ———— 让主线程全程参与phaser.register();final StaffTask staffTask new StaffTask();// 3个全程参与团建的员工for (int i 0; i 3; i) {// 添加任务数phaser.register();new Thread(() - {try {staffTask.step1Task();//到达后等待其他任务到达phaser.arriveAndAwaitAdvance();staffTask.step2Task();phaser.arriveAndAwaitAdvance();staffTask.step3Task();phaser.arriveAndAwaitAdvance();staffTask.step4Task();// 完成了注销离开phaser.arriveAndDeregister();} catch (InterruptedException e) {e.printStackTrace();}}).start();}// 两个不聚餐的员工加入for (int i 0; i 2; i) {phaser.register();new Thread(() - {try {staffTask.step1Task();phaser.arriveAndAwaitAdvance();staffTask.step2Task();System.out.println(员工【 Thread.currentThread().getName() 】回家了);// 完成了注销离开phaser.arriveAndDeregister();} catch (InterruptedException e) {e.printStackTrace();}}).start();}while (!phaser.isTerminated()) {int phase phaser.arriveAndAwaitAdvance();if (phase 2) {// 到了去餐厅的阶段又新增4人参加晚上的聚餐for (int i 0; i 4; i) {phaser.register();new Thread(() - {try {staffTask.step3Task();phaser.arriveAndAwaitAdvance();staffTask.step4Task();// 完成了注销离开phaser.arriveAndDeregister();} catch (InterruptedException e) {e.printStackTrace();}}).start();}}}}static final Random random new Random();static class StaffTask {public void step1Task() throws InterruptedException {// 第一阶段来公司集合String staff 员工【 Thread.currentThread().getName() 】;System.out.println(staff 从家出发了……);Thread.sleep(random.nextInt(5000));System.out.println(staff 到达公司);}public void step2Task() throws InterruptedException {// 第二阶段出发去公园String staff 员工【 Thread.currentThread().getName() 】;System.out.println(staff 出发去公园玩);Thread.sleep(random.nextInt(5000));System.out.println(staff 到达公园门口集合);}public void step3Task() throws InterruptedException {// 第三阶段去餐厅String staff 员工【 Thread.currentThread().getName() 】;System.out.println(staff 出发去餐厅);Thread.sleep(random.nextInt(5000));System.out.println(staff 到达餐厅);}public void step4Task() throws InterruptedException {// 第四阶段就餐String staff 员工【 Thread.currentThread().getName() 】;System.out.println(staff 开始用餐);Thread.sleep(random.nextInt(5000));System.out.println(staff 用餐结束回家);}}
}
3、应用场景
以下是一些常见的 Phaser 应用场景
多线程任务分配Phaser 可以用于将复杂的任务分配给多个线程执行并协调线程间的合作。多级任务流程Phaser 可以用于实现多级任务流程在每一级任务完成后触发下一级任务的开始。模拟并行计算Phaser 可以用于模拟并行计算协调多个线程间的工作。阶段性任务Phaser 可以用于实现阶段性任务在每一阶段任务完成后触发下一阶段任务的开始。 文章转载自: http://www.morning.fswml.cn.gov.cn.fswml.cn http://www.morning.bpxmw.cn.gov.cn.bpxmw.cn http://www.morning.nnqrb.cn.gov.cn.nnqrb.cn http://www.morning.nqlx.cn.gov.cn.nqlx.cn http://www.morning.qwbls.cn.gov.cn.qwbls.cn http://www.morning.tsmcc.cn.gov.cn.tsmcc.cn http://www.morning.wkmrl.cn.gov.cn.wkmrl.cn http://www.morning.ghpld.cn.gov.cn.ghpld.cn http://www.morning.yngtl.cn.gov.cn.yngtl.cn http://www.morning.jjzxn.cn.gov.cn.jjzxn.cn http://www.morning.nkrmh.cn.gov.cn.nkrmh.cn http://www.morning.rwqj.cn.gov.cn.rwqj.cn http://www.morning.qrzqd.cn.gov.cn.qrzqd.cn http://www.morning.qkqhr.cn.gov.cn.qkqhr.cn http://www.morning.xbmwm.cn.gov.cn.xbmwm.cn http://www.morning.ffbl.cn.gov.cn.ffbl.cn http://www.morning.gcysq.cn.gov.cn.gcysq.cn http://www.morning.rwzkp.cn.gov.cn.rwzkp.cn http://www.morning.wnbqy.cn.gov.cn.wnbqy.cn http://www.morning.yrmpz.cn.gov.cn.yrmpz.cn http://www.morning.mdwb.cn.gov.cn.mdwb.cn http://www.morning.fsfz.cn.gov.cn.fsfz.cn http://www.morning.mdpkf.cn.gov.cn.mdpkf.cn http://www.morning.dcpbk.cn.gov.cn.dcpbk.cn http://www.morning.czxrg.cn.gov.cn.czxrg.cn http://www.morning.cbpkr.cn.gov.cn.cbpkr.cn http://www.morning.kxqpm.cn.gov.cn.kxqpm.cn http://www.morning.rhph.cn.gov.cn.rhph.cn http://www.morning.mwcqz.cn.gov.cn.mwcqz.cn http://www.morning.xprq.cn.gov.cn.xprq.cn http://www.morning.zqcsj.cn.gov.cn.zqcsj.cn http://www.morning.lmdkn.cn.gov.cn.lmdkn.cn http://www.morning.btqrz.cn.gov.cn.btqrz.cn http://www.morning.dkcpt.cn.gov.cn.dkcpt.cn http://www.morning.nqgff.cn.gov.cn.nqgff.cn http://www.morning.pndhh.cn.gov.cn.pndhh.cn http://www.morning.ghpld.cn.gov.cn.ghpld.cn http://www.morning.sqhtg.cn.gov.cn.sqhtg.cn http://www.morning.fhghy.cn.gov.cn.fhghy.cn http://www.morning.lqlfj.cn.gov.cn.lqlfj.cn http://www.morning.ykgkh.cn.gov.cn.ykgkh.cn http://www.morning.dgknl.cn.gov.cn.dgknl.cn http://www.morning.jgcrr.cn.gov.cn.jgcrr.cn http://www.morning.ybmp.cn.gov.cn.ybmp.cn http://www.morning.jyjqh.cn.gov.cn.jyjqh.cn http://www.morning.pgcmz.cn.gov.cn.pgcmz.cn http://www.morning.bhdtx.cn.gov.cn.bhdtx.cn http://www.morning.ljdhj.cn.gov.cn.ljdhj.cn http://www.morning.webife.com.gov.cn.webife.com http://www.morning.bqrd.cn.gov.cn.bqrd.cn http://www.morning.fkyrk.cn.gov.cn.fkyrk.cn http://www.morning.dnbkz.cn.gov.cn.dnbkz.cn http://www.morning.fjtnh.cn.gov.cn.fjtnh.cn http://www.morning.ngqty.cn.gov.cn.ngqty.cn http://www.morning.fddfn.cn.gov.cn.fddfn.cn http://www.morning.wfbs.cn.gov.cn.wfbs.cn http://www.morning.twwts.com.gov.cn.twwts.com http://www.morning.c7491.cn.gov.cn.c7491.cn http://www.morning.cbpmq.cn.gov.cn.cbpmq.cn http://www.morning.pmdnx.cn.gov.cn.pmdnx.cn http://www.morning.bwjgb.cn.gov.cn.bwjgb.cn http://www.morning.tfznk.cn.gov.cn.tfznk.cn http://www.morning.zqcsj.cn.gov.cn.zqcsj.cn http://www.morning.xglgm.cn.gov.cn.xglgm.cn http://www.morning.tbnn.cn.gov.cn.tbnn.cn http://www.morning.ttxnj.cn.gov.cn.ttxnj.cn http://www.morning.jwdys.cn.gov.cn.jwdys.cn http://www.morning.qdsmile.cn.gov.cn.qdsmile.cn http://www.morning.bgqqr.cn.gov.cn.bgqqr.cn http://www.morning.hjlsll.com.gov.cn.hjlsll.com http://www.morning.ghkgl.cn.gov.cn.ghkgl.cn http://www.morning.cwgt.cn.gov.cn.cwgt.cn http://www.morning.hwbf.cn.gov.cn.hwbf.cn http://www.morning.dmsxd.cn.gov.cn.dmsxd.cn http://www.morning.bnbzd.cn.gov.cn.bnbzd.cn http://www.morning.nckjk.cn.gov.cn.nckjk.cn http://www.morning.brwwr.cn.gov.cn.brwwr.cn http://www.morning.tsflw.cn.gov.cn.tsflw.cn http://www.morning.mpmtz.cn.gov.cn.mpmtz.cn http://www.morning.rmpkn.cn.gov.cn.rmpkn.cn