Java并发编程--基础进阶高级完整笔记。

这都不知道是第几次刷狂神的JUC并发编程了,从第一次的迷茫到现在比较清晰,算是个大进步了,之前JUC笔记不见了,重新做一套笔记。

参考链接:https://www.bilibili.com/video/BV1B7411L7tE






目录

1.多线程--基础内容

1.Thread状态

​ 6种:新建、运行、阻塞、等待、超时等待、结束(可点击Thread查看源代码)

public enum State {
NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED;
}
2.Synchronized
  • 非公平锁
  • 可重入锁,

​ Synchronized:是非公平锁(不能保证线程获得锁的顺序,即线程不会依次排队去获取资源,而是争抢,但是结果一定是正确的),是可重入锁(已获得一个锁,可以再获得锁且不会造成死锁,比如synchronized内部可以再写个synchronized函数)

    /**
* Author: HuYuQiao
* Description: Synchronized实现方式(修饰函数即可)
*/
class TicketSync{
public int number = 50;
//synchronized本质是队列,锁
public synchronized void sale(){
if(number > 0) {
System.out.println(Thread.currentThread().getName() + "获得了第" + number-- +"票");
}
}
}
3.Lock锁
  • 可重入锁

  • 公平还是不公平锁可以设置(默认不公平锁)

        /**
    * Creates an instance of {@code ReentrantLock} with the
    * given fairness policy.
    *
    * @param fair {@code true} if this lock should use a fair ordering policy
    */
    public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
    }

    Lock:加锁之后必须解锁,,否则其他线程就获取不到了,所以用try-catch-finally包起来。

    /**
* Author: HuYuQiao
* Description: Lock实现方式(加锁、解锁)
*/
class TicketLock{
Lock lock = new ReentrantLock();
public int number = 50;
public void sale(){
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "获得了第" + number-- +"票"); } catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
4.总结

​ 在不加锁情况下,多线程会争抢,导致输出顺序、计算结果都会不一致(上面例子结果如果一样是因为只有3个线程,for循环即出错,因为number--这个函数本身不是线程安全的),所以就引入了锁的概念,synchronized,lock保证了输出顺序、计算结果的一致性。

虚假唤醒:在synchronized.wait与lock.condition.await唤醒线程时,是从await代码之后开始运行,所以为了保证能唤醒线程,需要用while语句将代码包含起来。

​ 完整代码

package com.empirefree.springboot;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import sun.security.krb5.internal.Ticket; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; /**
* @program: springboot
* @description: 多线程
* @author: huyuqiao
* @create: 2021/06/26 14:26
*/ @RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class ThreadTest { /**
* Author: HuYuQiao
* Description: Synchronized实现方式(修饰函数即可)
*/
class TicketSync{
public int number = 50;
//synchronized本质是队列,锁
public synchronized void sale(){
System.out.println(Thread.currentThread().getName() + "获得了第" + number-- +"票"); }
} /**
* Author: HuYuQiao
* Description: Lock实现方式(加锁、解锁)
*/
class TicketLock{
Lock lock = new ReentrantLock();
public int number = 50;
public void sale(){
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "获得了第" + number-- +"票"); } catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
} @Test
public void testThread() {
// TicketSync ticket = new TicketSync();
TicketLock ticket = new TicketLock();
new Thread( () ->{
for (int i = 0; i < 50; i++) {
ticket.sale();
} },"ThreadA").start();
new Thread(()->{
for (int i = 0; i < 50; i++) {
ticket.sale();
}
},"ThreadB").start();
new Thread(()->{
for (int i = 0; i < 50; i++) {
ticket.sale();
}
},"ThreadC").start(); for (int i = 0; i < 500; i++) {
new Thread(() -> {
ticket.sale();
}).start();
}
}
}

2.八锁现象(synchronized、static)

即synchronized、static修饰的函数,执行顺序、输出结果。

结果表明:

​ 1.synchronized修饰的函数:会锁住对象(可以看成锁对象中某个方法),看起来代码会依次执行,而没有锁的方法即不受影响,一来就先执行

​ 2.static synchronized修饰的函数:会锁住类.class(可以不同对象访问的都是同一个函数),所以2个对象访问自己的函数依然还是顺序执行.

​ 3.一个有static,一个没有static:即一个锁类.class,另一个锁对象,不管是同一个对象还是不同对象,就都不需要等待了,不会顺序执行。

1.synchronized

​ 修饰函数会保证同一对象依次顺序执行()

class Phone{
//synchronized
public synchronized void sendSms() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sendSms");
}
public synchronized void call() {
System.out.println("call");
} public void playGame(){
System.out.println("playGame");
}
} public static void main(String[] args) {
//Thread--代码执行顺序问题
Phone phone = new Phone();
new Thread(phone::sendSms, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(phone::call, "B").start();
new Thread(phone::playGame, "C").start();
}
2.static synchronized
class PhoneStatic{
//static synchronized
public static synchronized void sendSmsStatic() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sendSmsStatic");
}
public static synchronized void callStatic() {
System.out.println("callStatic");
}
} public static void main(String[] args) {
PhoneStatic phoneStatic = new PhoneStatic();
PhoneStatic phoneStatic2 = new PhoneStatic();
new Thread(() ->{
phoneStatic2.sendSmsStatic();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() ->{
phoneStatic2.callStatic();
}, "B").start();
}

3.Java集合--安全性

        //集合安全性--list,set,map都非线程安全
List<String> list = new Vector<>();
List<String> list = Collections.synchronizedList(new ArrayList<>());
List<String> list = new CopyOnWriteArrayList<>(); Map<String, String> objectObjectHashMap = new ConcurrentHashMap<>();
Map<Object, Object> objectObjectHashMap1 = Collections.synchronizedMap(new HashMap<>());
//set底层就是map:无论hashset还是linkedhashset
Set<String> set = Collections.synchronizedSet(new LinkedHashSet<>());
Set<String> set = new CopyOnWriteArraySet<>();

4.高并发--辅助类

​ 学习链接:https://www.cnblogs.com/meditation5201314/p/14395972.html

1.countdownLatch

Countdownlatch:减一操作,直到为0再继续向下执行

package Kuangshen.JUC.Thread;

import java.util.concurrent.CountDownLatch;

public class countDownLatch {
public static void main(String[] args) throws InterruptedException { final CountDownLatch countDownLatch = new CountDownLatch(5); for (int i = 0; i < 5; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "get out");
countDownLatch.countDown();
}, String.valueOf(i)).start();
}
countDownLatch.await(); //等待上述执行完毕再向下执行
System.out.println("close door");
}
}
2.cyclicbarrier

Cyclicbarrier:+1操作,对于每个线程都自动+1并等待,累计到规定值再向下执行,

package Kuangshen.JUC.Thread;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier; /**
* @author :Empirefree
* @description:TODO
* @date :2021/2/10 10:56
*/
public class cyclicbarrier {
public static void main(String[] args) throws BrokenBarrierException, InterruptedException {
//CyclicBarrier里面是容量 + runnable
final CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () ->{
System.out.println("不断增加到7即向后执行,与countdownlatch相反");
}
); /*对于指派的局部变量,lambda只能捕获一次 ,故而需定义成final(int内部定义就是final),而且线程中,
不能对局部变量进行修改,如需要修改,需定义成原子类atomic
*/
for (int i = 0; i < 7; i++) {
int finalI = i;
new Thread(() ->{
System.out.println(finalI);
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
} }).start();
}
} }
3.semaphore

semaphore:对于规定的值,多个线程只规定有指定的值能获取,每次获取都需要最终释放,保证一定能互斥执行

package Kuangshen.JUC.Thread;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit; /**
* @author :Empirefree
* @description:TODO
* @date :2021/2/10 15:24
*/
public class semaphore {
public static void main(String[] args) {
final Semaphore semaphore = new Semaphore(3); for (int i = 0; i < 60; i++) {
new Thread(() -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "抢到车位");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "离开车位"); } catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}).start();
}
}
}

5.读写锁(ReadWriteLock)

​ ReadWriteLock也是多线程下的一种加锁方式,下面列出ReadWriteLock和synchronized对多线程下,保证读完之后在写的实现方式

//读写锁 :写只有一个线程写,写完毕后 可以多个线程读
MyCache myCache = new MyCache();
int num = 6;
for (int i = 1; i < num; i++) {
int finalI = i;
new Thread(()->{
myCache.write(String.valueOf(finalI),String.valueOf(finalI)); },String.valueOf(i)).start();
}
for (int i = 1; i < num; i++) {
int finalI = i;
new Thread(()->{
myCache.read(String.valueOf(finalI));
},String.valueOf(i)).start();
} class MyCache{
private volatile Map<String,String> map = new HashMap<>();
private ReadWriteLock lock = new ReentrantReadWriteLock();
//存,写
public void write(String key,String value){
lock.writeLock().lock(); //写锁
try { System.out.println(Thread.currentThread().getName()+"线程开始写入");
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"线程开始写入ok");
} catch (Exception e){
e.printStackTrace();
} finally {
lock.writeLock().unlock();
}
}
//取,读
public void read(String key){
lock.readLock().lock(); //读锁
try { System.out.println(Thread.currentThread().getName()+"线程开始读取");
map.get(key);
System.out.println(Thread.currentThread().getName()+"线程读取ok");
} catch (Exception e){
e.printStackTrace();
} finally {
lock.readLock().unlock();
}
} //存,写
public synchronized void writeSync(String key,String value){
try {
System.out.println(Thread.currentThread().getName()+"线程开始写入");
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"线程开始写入ok");
} catch (Exception e){
e.printStackTrace();
}
}
//取,读
public void readSync(String key){
try {
System.out.println(Thread.currentThread().getName()+"线程开始读取");
map.get(key);
System.out.println(Thread.currentThread().getName()+"线程读取ok");
} catch (Exception e){
e.printStackTrace();
}
}
}

6.线程池

1.集合--队列(阻塞队列、同步队列)

  • 阻塞队列:blockingQueue(超时等待--抛弃,所以最后size=1)

            //阻塞队列
    ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(1);
    arrayBlockingQueue.offer("a", 2, TimeUnit.SECONDS);
    arrayBlockingQueue.offer("a", 2, TimeUnit.SECONDS);
    System.out.println("超时等待==" + arrayBlockingQueue.size());
  • 同步队列:synchronizeQueue(类似于生产者消费者)

            //同步队列
    SynchronousQueue<String> synchronousQueue = new SynchronousQueue<>();
    new Thread(()->{
    try {
    System.out.println(Thread.currentThread().getName()+"put 01");
    synchronousQueue.put("1");
    System.out.println(Thread.currentThread().getName()+"put 02");
    synchronousQueue.put("2");
    System.out.println(Thread.currentThread().getName()+"put 03");
    synchronousQueue.put("3");
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }).start();
    new Thread(()->{
    try {
    System.out.println(Thread.currentThread().getName()+"take"+synchronousQueue.take());
    System.out.println(Thread.currentThread().getName()+"take"+synchronousQueue.take());
    System.out.println(Thread.currentThread().getName()+"take"+synchronousQueue.take());
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }).start();
2.线程池基本概念(三大方法、七大参数、四种拒绝策略)
  • 三大方法:newSingleThreadExecutro(单个线程),newFixedThreadPool(固定大小线程池),newCachedThreadPool(可伸缩)

     ExecutorService threadPool = Executors.newSingleThreadExecutor();
    ExecutorService threadPool2 = Executors.newFixedThreadPool(5);
    ExecutorService threadPool3 = Executors.newCachedThreadPool();
  • 七大参数:

    ThreadPoolExecutor(int corePoolSize,  //核心线程池大小
    int maximumPoolSize, //最大的线程池大小(当阻塞队列满了就会打开)
    long keepAliveTime, //(空闲线程最大存活时间:即最大线程池中有空闲线程超过这个时间就会释放线程,避免资源浪费)
    TimeUnit unit, //超时单位
    BlockingQueue<Runnable> workQueue, //阻塞队列
    ThreadFactory threadFactory, //线程工厂 创建线程的 一般不用动
    RejectedExecutionHandler handler //拒绝策略
  • 四种拒绝策略:

    new ThreadPoolExecutor.AbortPolicy: // 该 拒绝策略为:银行满了,还有人进来,不处理这个人的,并抛出异常
    new ThreadPoolExecutor.CallerRunsPolicy(): // //该拒绝策略为:哪来的去哪里 main线程进行处理
    new ThreadPoolExecutor.DiscardPolicy(): //该拒绝策略为:队列满了,丢掉异常,不会抛出异常。
    new ThreadPoolExecutor.DiscardOldestPolicy(): //该拒绝策略为:队列满了,尝试去和最早的进程竞争,不会抛出异常

7.Stream(4个函数式接口、Lambda、异步回调)

1.函数式接口

​ 学习链接:https://www.cnblogs.com/meditation5201314/p/13693089.html

2.Lambda表达式

​ 学习链接:https://www.cnblogs.com/meditation5201314/p/13651755.html

3.异步回调
        //异步回调--无返回值
CompletableFuture<Void> future = CompletableFuture.runAsync(() ->{
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "....");
});
System.out.println("begin");
System.out.println(future.get());
System.out.println("end"); //异步回调--有返回值
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() ->{
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 3;
});
System.out.println("begin");
System.out.println(future2.get());
System.out.println(future2.whenComplete((result, error) ->{
System.out.println("返回结果:" + result);
System.out.println("错误结果" + error);
}).exceptionally(throwable -> {
System.out.println(throwable.getMessage());
return 502;
}).get());
System.out.println("end");

8.单例模式

1.饿汉模式(程序一启动就new,十分占内存)
/**
* @program: untitled
* @description: 单例模式
* @author: huyuqiao
* @create: 2021/06/27 14:44
*/ public class SingleModel { /*
* 可能会浪费空间
* */
private byte[] data1 = new byte[1024*1024];
private byte[] data2 = new byte[1024*1024];
private byte[] data3 = new byte[1024*1024];
private byte[] data4 = new byte[1024*1024];
private static final SingleModel hugrySingle = new SingleModel();
private SingleModel(){ }
public static SingleModel getInstance(){
return hugrySingle;
}
}
2.懒汉模式(DCL模式:双重检测,需要的时候才new)

1.第一层if没有加锁,所以会有多个线程到达if

2.如果内部new对象指令重排,就会导致有些线程认为lazyManModel有对象,所以会直接返回lazyManModel(实际为null)

3.所以需要加上valitile防止指令重排

/**
* @program: untitled
* @description: 懒汉式
* @author: huyuqiao
* @create: 2021/06/27 15:06
*/ public class LazyManModel {
private volatile static LazyManModel lazyManModel;
private LazyManModel(){
System.out.println(Thread.currentThread().getName() + "...");
} //DCL懒汉式:双重检测锁--实现效果,只有为空的才null,否则不用null,所以需要2重if判断。
public static LazyManModel getInstance(){
if (lazyManModel == null){
synchronized (LazyManModel.class){
if (lazyManModel == null){
lazyManModel = new LazyManModel();
}
}
}
return lazyManModel;
} public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() ->{
LazyManModel.getInstance();
}).start();
}
}
}

9.Volatile和Atomic

​ 学习笔记:https://www.cnblogs.com/meditation5201314/p/13707590.html

10.Java中锁

1.公平锁(FIFO):Lock锁可以自定义,synchronized
2.非公平锁(允许插队):Lock锁可以自定义
3.可重入锁(获取一个锁再获取其他锁不会造成死锁):lock锁和synchronized
4.自旋锁:得不到就一直等待(Atomic.getAndIncrement底层就是自旋锁)
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock; /**
* @program: untitled
* @description: spinlock
* @author: huyuqiao
* @create: 2021/06/27 15:40
*/ public class SpinLockTest {
public static void main(String[] args) throws InterruptedException { //使用CAS实现自旋锁
SpinlockDemo spinlockDemo=new SpinlockDemo();
new Thread(()->{
spinlockDemo.myLock();
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
e.printStackTrace();
} finally {
spinlockDemo.myUnlock();
}
},"t1").start(); TimeUnit.SECONDS.sleep(1); new Thread(()->{
spinlockDemo.myLock();
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
e.printStackTrace();
} finally {
spinlockDemo.myUnlock();
}
},"t2").start();
}
} class SpinlockDemo { // 默认
// int 0
//thread null
AtomicReference<Thread> atomicReference=new AtomicReference<>(); //加锁
public void myLock(){
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+"===> mylock"); //自旋锁--为空则返回true,否则返回false
while (!atomicReference.compareAndSet(null,thread)){
System.out.println(Thread.currentThread().getName()+" ==> .自旋中~");
}
} //解锁
public void myUnlock(){
Thread thread=Thread.currentThread();
System.out.println(thread.getName()+"===> myUnlock");
atomicReference.compareAndSet(thread,null);
} }
5.死锁命令排查
jps -l
jstack 进程号

Java并发编程--基础进阶高级(完结)的更多相关文章

  1. Java并发编程基础

    Java并发编程基础 1. 并发 1.1. 什么是并发? 并发是一种能并行运行多个程序或并行运行一个程序中多个部分的能力.如果程序中一个耗时的任务能以异步或并行的方式运行,那么整个程序的吞吐量和可交互 ...

  2. 并发-Java并发编程基础

    Java并发编程基础 并发 在计算机科学中,并发是指将一个程序,算法划分为若干个逻辑组成部分,这些部分可以以任何顺序进行执行,但与最终顺序执行的结果一致.并发可以在多核操作系统上显著的提高程序运行速度 ...

  3. Java并发编程基础-线程安全问题及JMM(volatile)

    什么情况下应该使用多线程 : 线程出现的目的是什么?解决进程中多任务的实时性问题?其实简单来说,也就是解决“阻塞”的问题,阻塞的意思就是程序运行到某个函数或过程后等待某些事件发生而暂时停止 CPU 占 ...

  4. java并发编程基础概念

    本次内容主要讲进程和线程.CPU核心数和线程数.CPU时间片轮转机制.上下文切换,并行和并发的基本概念以及并发编程的好处和注意事项,为java并发编程打下扎实基础. 1.什么是进程和线程 1.1 进程 ...

  5. 多线程(一)java并发编程基础知识

    线程的应用 如何应用多线程 在 Java 中,有多种方式来实现多线程.继承 Thread 类.实现 Runnable 接口.使用 ExecutorService.Callable.Future 实现带 ...

  6. Java并发编程基础三板斧之Semaphore

    引言 最近可以进行个税申报了,还没有申报的同学可以赶紧去试试哦.不过我反正是从上午到下午一直都没有成功的进行申报,一进行申报 就返回"当前访问人数过多,请稍后再试".为什么有些人就 ...

  7. Java并发编程基础之volatile

    首先简单介绍一下volatile的应用,volatile作为Java多线程中轻量级的同步措施,保证了多线程环境中“共享变量”的可见性.这里的可见性简单而言可以理解为当一个线程修改了一个共享变量的时候, ...

  8. java并发编程基础——线程的创建

    一.基础概念 1.进程和线程 进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程.(进程是资源分配的最小单位) 线程:同一类线程共享代码和数据 ...

  9. Java并发编程基础-ReentrantLock的机制

    同步锁: 我们知道,锁是用来控制多个线程访问共享资源的方式,一般来说,一个锁能够防止多个线程同时访问共享资源,在Lock接口出现之前,Java应用程序只能依靠synchronized关键字来实现同步锁 ...

随机推荐

  1. Introduction to x265 Rate Control Algorithm

    The rate control in x265 is the same as x264's implementation, which is mostly empirical. It include ...

  2. CRM应用中可能发生的问题

    CRM系统是公认的提升企业竞争力的强大工具.它既是以客户为中心的思想,又是一种企业管理方案.当然,它还是一种管理软件.在国外,CRM使企业运营得风生水起,但在我国的企业应用中,还是有着很高的失败率和使 ...

  3. 快速熟悉windows操作

    快捷键 win + E : 打开我的电脑 Ctrl+Shift+Esc:打开资源管理器 Alt +F4 :关闭当前窗口 Win + R:打开命令窗口 DOS 命令 打开CMD 的方式 Win+R:输入 ...

  4. [bug] redis-cli连接时出现Could not connect to Redis at 127.0.0.1:6379: Connection refused

    参考 https://www.geek-share.com/detail/2684728161.html

  5. sed 's/AA/BB/' file # 将文件中的AA替换成BB,只替换一行中第一次出现的AA,替换后的结果输出到屏幕 sed 's/AA/BB/g' file # 将文件中的所有AA都替换成BB,替换后的结果输出到屏幕

    生信人的自我修养:Linux命令速查手册 简佐义 ​ 四川大学 生物信息学硕士 科学求真 赢 10 万奖金 · 院士面对面 209 人赞同了该文章 许多人做生物信息学,要么不重视Linux,要么不知道 ...

  6. linux基本命令随笔

    cd(change directory) 不跟参数,直接进入当前登录用户的家目录 . 代表当前目录 .. 代表上级目录 ls(list) -l 显示文件详细信息 -a 显示隐藏文件 -S 以文件大小排 ...

  7. 云计算OpenStack核心组件---glance镜像服务(6)

    一.glance介绍: Glance是Openstack项目中负责镜像管理的模块,其功能包括虚拟机镜像的查找.注册和检索等. Glance提供Restful API可以查询虚拟机镜像的metadata ...

  8. hard way for code

    奋斗吧骚年:https://learncodethehardway.org/ 有关linuxz命令的URL:man.linuxde.net

  9. 11.11 ntsysv:管理开机服务

    ntsysv命令提供了一种基于文本界面的菜单操作方式,以设置不同运行级别下的系统服务启动状态.   -back    在交互界面里,显示Back按钮,而非Cancel按钮 -level    指定运行 ...

  10. Jlink固件更新

    故障:JLINK上的指示灯也不亮,无驱动等 文中所提工具和固件链接: 工具:http://pan.baidu.com/s/1c2z8nao 固件:http://pan.baidu.com/s/1jIB ...