详解线程池

概念:

什么是线程池:

线程池是用来存储多线程的容器,是一种处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。

线程池使用和不使用的区别:

  1. 因为系统创建线程池的成本很高,会涉及到和操作系统交互,频繁的创建线程和销毁会对消耗系统资源
  2. 线程池会启动一个线程执行这个任务,如果不够可以自动加,执行完线程会进入空闲状态,不会销毁,等待下一次执行(很乖的)

不使用线程池会造成资源浪费

我们创建一个线程执行任务后线程死亡.如果线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。
比如:中午吃完饭将碗洗干净,晚上接着使用 重复利用,可以节省资源

线程状态:

在这里插入图片描述

线程状态具体含义
NEW一个尚未启动的线程的状态。也称之为初始状态、开始状态。线程刚被创建,但是并未启动。还没调用start方法。MyThread t = new MyThread()只有线程对象,没有线程特征。
RUNNABLE当我们调用线程对象的start方法,那么此时线程对象进入了RUNNABLE状态。那么此时才是真正的在JVM进程中创建了一个线程,线程一经启动并不是立即得到执行,线程的运行与否要听令与CPU的调度,那么我们把这个中间状态称之为可执行状态(RUNNABLE)也就是说它具备执行的资格,但是并没有真正的执行起来而是在等待CPU的调度。
BLOCKED当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。
WAITING一个正在等待的线程的状态。也称之为等待状态。造成线程等待的原因有两种,分别是调用Object.wait()、join()方法。处于等待状态的线程,正在等待其他线程去执行一个特定的操作。例如:因为wait()而等待的线程正在等待另一个线程去调用notify()或notifyAll();一个因为join()而等待的线程正在等待另一个线程结束。
TIMED_WAITING一个在限定时间内等待的线程的状态。也称之为限时等待状态。造成线程限时等待状态的原因有三种,分别是:Thread.sleep(long),Object.wait(long)、join(long)。
TERMINATED一个完全运行完成的线程的状态。也称之为终止状态、结束状态
四种线程池:
方法名说明
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程
newFixedThreadPool创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
newScheduledThreadPool创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
线程池常用方法:
方法说明
submit()使用完毕归还线程
shutdown()关闭线程池
newCachedThreadPool:

构造方法

方法名说明
newCachedThreadPool()创建一个线程池,默认为空,最大容量是int最大值
newCachedThreadPool​(ThreadFactory threadFactory)创建一个根据需要创建新线程的线程池,但在它们可用时将重用以前构造的线程,并在需要时使用提供的ThreadFactory创建新线程。
代码演示:
    public static void main(String[] args) throws InterruptedException {/**创建一个默认的线程池对象.池子中默认是空的.默认最多可以容纳int类型的最大值idea会提醒你,手动创建会更好,这里的初始值是最大值,最大多少个线程Executors:创建线程池对象ExecutorService:控制线程池*/ExecutorService es = Executors.newCachedThreadPool();// submit:使用完毕归还线程es.submit(() -> System.out.println(Thread.currentThread().getName() + ": 执行"));/**这里让线程睡眠会导致只有一条线程运行原因:创建的时候没有指定线程,所以会自动创建一条线程,执行完放回线程池,然后睡眠完了发现线程池有刚才闲置的线程就直接用了不睡眠为什么会创建两个:先自动创建,使用完还没有来得及归还,代码就创建下一个线程了但是睡眠时间比较少的话,比如sleep(1),那么还是会创建两个线程*/Thread.sleep(1000);es.submit(() -> System.out.println(Thread.currentThread().getName() + ": 执行"));// 关闭线程池es.shutdown();}
newFixedThreadPool:

代码演示:

    public static void main(String[] args) {// 参数不是初始值而是最大值ExecutorService es = Executors.newFixedThreadPool(10);System.out.println(((ThreadPoolExecutor) es).getPoolSize());// 0es.submit(() -> System.out.println(Thread.currentThread().getName() + "在执行了"));es.submit(() -> System.out.println(Thread.currentThread().getName() + "在执行了"));System.out.println(((ThreadPoolExecutor) es).getPoolSize());// 2es.shutdown();}
ThreadPoolExecutor自定义线程池:

参数详解:

参数一:核心线程数量
参数二:最大线程数量
参数三:空闲线程最大存活时间
参数四:时间单位,对应参数三的
参数五:任务列表(阻塞队列):让任务在队列中等,等到线程有空,再从队列中获取任务执行
参数六:创建线程工厂:按照默认的方式创建线程对象
参数七:任务的拒绝策略:

  1. 什么时候拒绝:提交的任务 > (池子中最大线程数 + 队列容量)
  2. 如何拒绝:看下面的四种拒绝策略

线程类:

public class Runn implements Runnable {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + "执行了");}
}

测试类:

public class Demo05 {public static void main(String[] args) {ThreadPoolExecutor tpe = new ThreadPoolExecutor(2, 5, 2, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());tpe.submit(new Runn());tpe.submit(new Runn());tpe.shutdown();}
}
任务拒绝策略:
方法名说明
ThreadPoolExecutor.AbortPolicy丢弃任务并抛出RejectedExecutionException异常。是默认的拒绝策略,可灵活回收空闲线程,若无可回收,则新建线程
ThreadPoolExecutor.DiscardPolicy丢弃任务,但是不抛出异常 这是不推荐的做法
ThreadPoolExecutor.DiscardOldestPolicy抛弃队列中等待最久的任务 然后把当前任务加入队列中
ThreadPoolExecutor.CallerRunsPolicy调用任务的run()方法绕过线程池直接执行
public class Demo {public static void main(String[] args) {/*ThreadPoolExecutor tpe = new ThreadPoolExecutor(2, 5, 2,TimeUnit.SECONDS, new ArrayBlockingQueue<>(10),Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());*//* ThreadPoolExecutor tpe = new ThreadPoolExecutor(2, 5, 2,TimeUnit.SECONDS, new ArrayBlockingQueue<>(10),Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardPolicy());*/// 如果是最大线程数是3,队列是1,一共是4,创建10个线程,那么使用此拒绝策略,会打印,4个,但是打印的是线程1、2、3、10,10前面的就不要了/* ThreadPoolExecutor tpe = new ThreadPoolExecutor(2, 5, 2,TimeUnit.SECONDS, new ArrayBlockingQueue<>(10),Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy());*/// 任务过多的时候直接缇跳过线程池让main方法执行多出的线程ThreadPoolExecutor tpe = new ThreadPoolExecutor(2, 5, 2,TimeUnit.SECONDS, new ArrayBlockingQueue<>(10),Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());for (int i = 0; i < 16; i++) {tpe.submit(new Runn());}tpe.shutdown();}
}
volatile:

Volatile关键字 : 强制线程每次在使用的时候,都会看一下共享区域最新的,但是不能不能保证原子性

使用场景:加在共享数据上

当A线程修改了共享数据时,B线程没有及时获取到最新的值,如果还在使用原先的值,就会出现问题

  1. 堆内存是唯一的,每一个线程都有自己的线程栈。
  2. 每一个线程在使用堆里面变量的时候,都会先拷贝一份到变量的副本中。
  3. 在线程中,每一次使用是从变量的副本中获取的。

定义循环测试,如果没有获取最新数据就会一直循环

共享数据类:

public class Share {public static  int MONEY =10;
}

定义两个线程类:

public class libai extends Thread {@Overridepublic void run() {while (Share.MONEY = 1000) {}System.out.println("钱发生了变化");}
}
public class hanxin extends Thread {@Overridepublic void run() {try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}Share.MONEY = 900;}
}

测试类:

public class Demo04 {public static void main(String[] args) {hanxin t1 = new hanxin();t1.setName("韩信");t1.start();libai t2 = new libai();t2.setName("李白");t2.start();}
}

解决方案:
在共享数据上加上volatile就可以了

public class Share {public static volatile int MONEY =10;
}
synchronized:

上面的情况用synchronized也是可以解决的
原因是:

  1. synchronized会清空变量副本
  2. 拷贝共享变量最新的值到变量副本中
  3. 如果修改了共享数据,会把修改后的变量副本中的值赋值给共享数据

共享数据类:

public class Share {public static Object lock = new Object();public static volatile int MONEY = 10;
}

两个线程类:

public class hanxin extends Thread {@Overridepublic void run() {while (true) {synchronized (Share.lock) {if (Share.MONEY != 10) {System.out.println("数值发生变化");break;}}}}
}
public class libai extends Thread {@Overridepublic void run() {synchronized (Share.lock) {try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}Share.MONEY = 1000;}}
}

测试类:

public class Demo04 {public static void main(String[] args) {hanxin t1 = new hanxin();t1.setName("韩信");t1.start();libai t2 = new libai();t2.setName("李白");t2.start();}
}
原子性AtomicInteger:
  • 原子性是指在一次操作或者多次操作中,要么所有的操作全部都得到了执行并且不会受到任何因素的干扰而中断,要么所有的操作都不执行,多个操作是一个不可以分割的整体。
  • volatile只能保证线程每次在使用共享数据的值是最新的,但是不能保证原子性
  • AtomicInteger原理 : 自旋锁 + CAS 算法
  • CAS算法:
    • 有3个操作数(内存值V, 旧的预期值A,要修改的值B)
    • 当旧的预期值A == 内存值 此时修改成功,将V改为B
    • 当旧的预期值A!=内存值 此时修改失败,不做任何操作
    • 并重新获取现在的最新值(这个重新获取的动作就是自旋)
  • 自旋:
    • 在修改共享数据的时候,先把原来的旧值记录下来,然后要修改时候如果内存中和旧值的一样证明其他线程没操作内存值,那就修改成功,如果不一样说明其他线程操作了,就失败,会把最新的值从内存获取到旧值,然后再修改,重新获取就是自旋

例:

public class Demo06 implements Runnable {private int count = 0;@Overridepublic void run() {for (int i = 0; i < 100; i++) {/*   1、从共享数据中读取数据到本地线程栈中2、修改本线程栈中的变量副本值3、会把本线程栈中变量副本值赋值给共享数据异常:1、线程A刚修改了本线程栈中的变量副本值为101,还没同步给共享数据2、这时候线程B抢走了CPU的执行权,也修改了本线程栈中的变量副本值为101,然后同步给共享数据3、线程B同步完后,线程A也同步,但是两个线程提交的是同一个值。*/count++;System.out.println(count);}}
}

测试类:

public class Demo07 {public static void main(String[] args) {Demo06 d = new Demo06();for (int i = 0; i < 100; i++) {new Thread(d).start(); // 9999}}
}

lock锁可以解决,但是效率比较低,因为每次要去判断锁、获得锁、释放锁

public class Demo01 {public static void main(String[] args) {Run runn = new Run();for (int i = 0; i < 100; i++) {new Thread(runn).start();}}
}
class Run implements Runnable {private volatile int count = 0;private Object lock = new Object();@Overridepublic void run() {for (int i = 0; i < 100; i++) {synchronized (lock) {count++;System.out.println("已经打印了  " + count + "  次");}}}
}
AtomicInteger方法:

构造方法:

方法名说明
public AtomicInteger()初始化一个默认值为0的原子型Integer
public AtomicInteger(int initialValue)初始化一个指定值的原子型Integer

成员方法:

方法名说明
int get()获取值
int getAndIncrement()以原子方式将当前值加1,注意,这里返回的是自增前的值
int incrementAndGet()以原子方式将当前值加1,注意,这里返回的是自增后的值
int addAndGet(int data)以原子方式将输入的数值与实例中的值(AtomicInteger里的value)相加,并返回结果
int getAndSet(int value)以原子方式设置为newValue的值,并返回旧值

: 。

    public static void main(String[] args) {// int get(): 获取值AtomicInteger ai1 = new AtomicInteger(10);System.out.println(ai1.get()); //10 // int getAndIncrement(): 以原子方式将当前值加1,注意,这里返回的是自增前的值。AtomicInteger ai2 = new AtomicInteger(10);System.out.println(ai2.getAndIncrement());//10System.out.println(ai2.get());//11// int incrementAndGet(): 以原子方式将当前值加1,注意,这里返回的是自增后的值。AtomicInteger ai3 = new AtomicInteger(10);System.out.println(ai3.incrementAndGet());//11// int addAndGet(int data): 以原子方式将输入的数值与实例中的值(AtomicInteger里的value)相加,并返回结果。AtomicInteger ai4 = new AtomicInteger(10);System.out.println(ai4.addAndGet(1)); //11System.out.println(ai4.get()); //11// int getAndSet(int value): 先获得再设置新值。AtomicInteger ai5 = new AtomicInteger(10);System.out.println(ai5.getAndSet(1)); //10System.out.println(ai5);//1}

AtomicInteger解决共享数据问题:

public class Demo09 implements Runnable {AtomicInteger ac = new AtomicInteger(0);@Overridepublic void run() {for (int i = 0; i < 100; i++) {int count = ac.incrementAndGet();System.out.println(count);}}
}

incrementAndGet源码解析:

//先自增,然后获取自增后的结果
public final int incrementAndGet() {//+ 1 自增后的结果//this 就表示当前的atomicInteger(值)//1    自增一次return U.getAndAddInt(this, VALUE, 1) + 1;
}public final int getAndAddInt(Object o, long offset, int delta) {//v 旧值int v;//自旋的过程do {//不断的获取旧值v = getIntVolatile(o, offset);//如果这个方法的返回值为false,那么继续自旋//如果这个方法的返回值为true,那么自旋结束//o 表示的就是内存值//v 旧值//v + delta 修改后的值} while (!weakCompareAndSetInt(o, offset, v, v + delta));//作用:比较内存中的值,旧值是否相等,如果相等就把修改后的值写到内存中,返回true。表示修改成功。//                                 如果不相等,无法把修改后的值写到内存中,返回false。表示修改失败。//如果修改失败,那么继续自旋。return v;
}
synchronized和CAS的区别 :

相同点:

在多线程情况下,都可以保证共享数据的安全性。

不同点:

  • synchronized(悲观锁):总是从最坏的角度出发,认为每次获取数据的时候,别人都有可能修改。所以在每 次操作共享数据之前,都会上锁。

  • cas(乐观锁):是从乐观的角度出发,假设每次获取数据别人都不会修改,所以不会上锁。只不过在修改共享数据的时候,会检查一下,别人有没有修改过这个数据。如果别人修改过,那么我再次获取现在最新的值。如果别人没有修改过,那么我现在直接修改共享数据的值

Hashtable

Hashtable出现的原因 : 在集合类中HashMap是比较常用的集合对象,但是HashMap是线程不安全的(多线程环境下可能会存在问题)。为了保证数据的安全性我们可以使用Hashtable,但是Hashtable的效率低下。
效率低的原因:因为底层是悲观锁,整个表都锁起来,那其他线程在外面等就会变的效率很低

    public static void main(String[] args) throws InterruptedException {Hashtable<String, String> hm = new Hashtable<>(100);Thread t1 = new Thread(() -> {for (int i = 0; i < 25; i++) {hm.put(i + "", i + "");}});Thread t2 = new Thread(() -> {for (int i = 25; i < 51; i++) {hm.put(i + "", i + "");}});t1.start();t2.start();System.out.println("----------------------------");//为了t1和t2能把数据全部添加完毕Thread.sleep(1000);//0-0 1-1 ..... 50- 50for (int i = 0; i < 51; i++) {System.out.println(hm.get(i + ""));}//0 1 2 3 .... 50}
ConcurrentHashMap:

ConcurrentHashMap出现的原因 : 在集合类中HashMap是比较常用的集合对象,但是HashMap是线程不安全的(多线程环境下可能会存在问题)。为了保证数据的安全性我们可以使用Hashtable,但是Hashtable的效率低下。基于以上两个原因我们可以使用JDK1.5以后所提供的ConcurrentHashMap。

在这里插入图片描述

ConcurrentHashMap1.7和1.8区别:

JDK1.7
在这里插入图片描述

JDK1.8

  1. 如果使用空参构造ConcurrentHashMap对象,则什么事情都不做,在第一次添加元素的时候创建哈希表
  2. 计算当前元素应存入的索引
  3. 如果该索引位置为null,则利用cas算法,将本节点添加到数组中
  4. 如果该索引位置不为null,则利用volatile关键字获得当前位置最新结点地址,挂载它下面,变成链表
  5. 当链表的长度大于等于8时,自动转成红黑树
  6. 以链表或者红黑树头结点为锁对象,配合synchronized保证多线程操作集合时数据的安全性

在这里插入图片描述

    public static void main(String[] args) throws InterruptedException {ConcurrentHashMap<String, String> hm = new ConcurrentHashMap<>(100);Thread t1 = new Thread(() -> {for (int i = 0; i < 25; i++) {hm.put(i + "", i + "");}});Thread t2 = new Thread(() -> {for (int i = 25; i < 51; i++) {hm.put(i + "", i + "");}});t1.start();t2.start();System.out.println("----------------------------");//为了t1和t2能把数据全部添加完毕Thread.sleep(1000);//0-0 1-1 ..... 50- 50for (int i = 0; i < 51; i++) {System.out.println(hm.get(i + ""));}//0 1 2 3 .... 50}
CountDownLatch:

使用场景: 让某一条线程等待其他线程执行完毕之后再执行

方法解释
public CountDownLatch(int count)参数传递线程数,表示等待线程数量
public void await()让线程等待
public void countDown()当前线程执行完毕

线程一:

public class Thread1 extends Thread {private CountDownLatch countDownLatch;public Thread1(CountDownLatch countDownLatch) {this.countDownLatch = countDownLatch;}@Overridepublic void run() {// 让线程等待for (int i = 0; i < 10; i++) {System.out.println("线程一在执行第 " + i + " 次");}// 当前线程执行完毕,计数器-1,这时候线程还剩1countDownLatch.countDown();}
}

线程二:

public class Thread2 extends Thread {private CountDownLatch countDownLatch;public Thread2(CountDownLatch countDownLatch) {this.countDownLatch = countDownLatch;}@Overridepublic void run() {// 让线程等待for (int i = 0; i < 10; i++) {System.out.println("线程二在执行第 " + i + " 次");}// 当前线程执行完毕,计数器-1,这个时候计数器为0countDownLatch.countDown();}
}

父类线程:

public class FuThread extends Thread {private CountDownLatch countDownLatch;public FuThread(CountDownLatch countDownLatch) {this.countDownLatch = countDownLatch;}@Overridepublic void run() {//1.等待try {//当计数器变成0的时候,会自动唤醒这里等待的线程。countDownLatch.await();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("父类线程执行完毕");}
}

测试类:

public class Test {public static void main(String[] args) {//1.创建CountDownLatch的对象,需要传递给四个线程。//在底层就定义了一个计数器,此时计数器的值就是2CountDownLatch countDownLatch = new CountDownLatch(2);//2.创建三个线程对象并开启他们。FuThread motherThread = new FuThread(countDownLatch);motherThread.start();Thread1 t1 = new Thread1(countDownLatch);t1.setName("线程一");Thread2 t2 = new Thread2(countDownLatch);t2.setName("线程二");t1.start();t2.start();}
}
Semaphore:

使用场景 :

可以控制访问特定资源的线程数量。
比如有一个路口没有交警、没有红绿灯、会引发交通事故,可以做的和收费站一样发通行证、一次只能进来一辆车、或者两辆车、其他的等车想进来必须等这两个出去一个或者都出去、semaphore就是做这个,就是管理员的身份,也可以理解是有两个锁吧,执行完就释放锁,在执行就等待

实现步骤 :

  1. 需要有人管理这个通道
  2. 当有车进来了,发通行许可证
  3. 当车出去了,收回通行许可证
  4. 如果通行许可证发完了,那么其他车辆只能等着

线程类:

public class Thread1 implements Runnable{//1.获得管理员对象,private Semaphore semaphore = new Semaphore(2);@Overridepublic void run() {//2.获得通行证try {semaphore.acquire();//3.开始行驶System.out.println("获得了通行证开始行驶");Thread.sleep(2000);System.out.println("归还通行证");//4.归还通行证semaphore.release();} catch (InterruptedException e) {e.printStackTrace();}}
}

测试类:

public class Test {public static void main(String[] args) {Thread1  t= new Thread1();for (int i = 0; i < 5; i++) {new Thread(t).start();}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/572415.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

oracle 10g express linux,在Ubuntu下安装Oracle Database 10g Express Edition

Oracle 10g有一款XE版&#xff0c;意为体验版&#xff0c;限制是不支持多CPU和数据库大小不能超过2G(还有其他的什么&#xff0c;不记得了&#xff0c;Oracle官方网站有写)。对于开发的时候调试一下&#xff0c;体验一下还是够用的。关键大小比较适中&#xff0c;安装包200多M。…

oracle硬盘亮黄灯,RH2288H V3服务器硬盘亮黄灯故障处理案例

原标题&#xff1a;RH2288H V3服务器硬盘亮黄灯故障处理案例本文广州诚本将分享RH2288H V3服务器硬盘亮黄灯故障处理案例&#xff0c;希望对大家的工作有所帮助。问题描述某客户新开局的项目&#xff0c;采购一批RH2288H V3的服务器&#xff0c;做RAID时发现其中一台服务器一块…

彻底学会IO流

概述&#xff1a; IO流就是用来处理设备间数据传输问题的.常见的应用: 文件复制; 文件上传; 文件下载IO的数据传输&#xff0c;可以看做是一种数据的流动&#xff0c;按照流动的方向&#xff0c;已内存为参照物&#xff0c;进行读写操作IO可以保存到文件&#xff0c;其实就是内…

一些芯片资料

74hc14d u10 u11 u12 六反相触发器 74HC244 三态八缓冲器 u15 u13 uln2003afwg u16 74HC07 u17 L298N 步进电机驱动芯片 MOS管认知 转载于:https://www.cnblogs.com/legion/p/6908434.html

DP Intro - Tree DP Examples

因为上次比赛sb地把一道树形dp当费用流做了&#xff0c;受了点刺激&#xff0c;用一天时间稍微搞一下树形DP&#xff0c;今后再好好搞一下&#xff09; 基于背包原理的树形DP poj 1947 Rebuilding Roads 题意&#xff1a;给你一棵树,让你求最少剪掉多少条边可以剪出一棵点数为m…

转换流/序列化/反序列化

转换流&#xff1a; 使用转换流可以在一定程度上避免乱码&#xff0c;还可以指定输入输出所使用的字符集 InputStreamReader&#xff1a;是从字节流到字符流的桥梁&#xff0c;父类是Reader OutputStreamWriter&#xff1a;是从字符流到字节流的桥梁&#xff0c;父类是Writer 转…

python+unittest框架整理(一点点学习前辈们的封装思路,一点点成长。。。)

预期框架整理目标&#xff1a; 1.单个用例维护在单个.py文件中可单个执行&#xff0c;也可批量生成组件批量执行 2.对定位参数&#xff0c;定位方法&#xff0c;业务功能脚本&#xff0c;用例脚本&#xff0c;用例批量执行脚本&#xff0c;常用常量进行分层独立&#xff0c;各自…

vs远程编译linux程序,使用Visual Studio 2015远程调试Linux程序

##安装 Visual Studio 2015安装时注意将跨平台移动开发->Visual C移动开发->Viaual C Android 开发的选项勾上##安装PUTTYVisual Studio依赖putty中的plink来连接Linux机器并发送命令##使用首先在Visual Studio中新建一个空项目这里是列表文本接下来将代码导入到这个空项…

都在说反射,反射到底是什么

概念&#xff1a; 什么是反射? 利用反射可以无视修饰符获取类里面所有的属性和方法对于任何对象&#xff0c;都能够调用它的方法和属性&#xff0c;这种动态获取信息以及动态调用对象方法的功能称为Java的反射 反射的应用场景? 常见的有&#xff1a; idea的智能提示、框架等…

LaunchScreen原理

会自动加载LaunchScreen是因为在Target当中,指定了Launch Screen file 它的底层实现其实把LaunchScreen上的东西,生成了一张图片,然后把这张图片设为程序的启动图片.可以进入沙盒当中查看,查看方法,找到应用程序根目录.获取方法: NSLog("%",NSHomeDirectory());打印出…

Mac - 苹果电脑mac系统释放硬盘空间方法汇总

硬盘空间是大家最头痛的一个问题&#xff0c;大家在硬盘空间变小的时候怎么腾空间的呢&#xff1f;下面为大家分享7个mac系统释放空间的高级方法&#xff0c;大家赶紧来收了&#xff01; mac系统释放硬盘空间方法&#xff1a; 方法一&#xff1a;删除Emacs——可以节省出60MB的…

XML语言

XML&#xff1a; XML是可扩展的标记语言 标记语言: 通过标签来描述数据的一门语言(标签有时我们也将其称之为元素) 可扩展&#xff1a;标签的名字是可以自定义的,XML文件是由很多标签组成的,而标签名是可以自定义的 xml学习网站https://www.w3school.com.cn/x.asp 作用&#xf…

xml中的Document和Attribute

1.Document 导入dom4j的步骤 去官网下载 zip 包。http://www.dom4j.org/ 在项目中创建一个文件夹&#xff1a;lib将dom4j-2.1.1.jar文件复制到 lib 文件夹在jar文件上点右键&#xff0c;选择 Add as Library -> 点击OK在类中导包使用 得到Document对象 步骤&#xff1a;…

XPath表达式

什么是XPath XPath&#xff1a;路径表达式 作用&#xff1a;在DOM解析XML时&#xff0c;通过XPath表达让解析更加简单 XPath表达式分类 绝对路径相对路径全文搜索属性查找 什么是Node对象 DOM树中的每个节点就是Node dom4j中与XPath相关的方法 注&#xff1a;使用XPat…

最详细MySQL的安装与介绍Windows

数据库的安装&#xff1a; 打开下载的mysql安装⽂件双击解压缩&#xff0c;运⾏“mysql-5.5.40-win32.msi”。 选择安装类型&#xff0c;有“Typical&#xff08;默认&#xff09;”、“Complete&#xff08;完全&#xff09;”、“Custom&#xff08;⽤户⾃定义&#xff09;”…

读懂基础机器学习算法

本篇内容主要是面向机器学习初学者&#xff0c;介绍常见的机器学习算法&#xff0c;当然&#xff0c;欢迎同行交流。 哲学要回答的基本问题是从哪里来、我是谁、到哪里去&#xff0c;寻找答案的过程或许可以借鉴机器学习的套路&#xff1a;组织数据->挖掘知识->预测未来。…

在c语言中有函数leapyear定义如下,职业教育概论——职业教育的发展历史超星尔雅答案题库...

职业职业展历有时候,文学造诣高的中国诗歌翻译者在用词华丽方面超过莎士比亚。有int型变量x&#xff0c;教育教育i&#xff0c;j&#xff0c;k&#xff0c;则表达式x(i5,j6,k7)的值为( )从数据表student中查询张姓的所有学生&#xff0c;概论正确的SQL语句是( )史超世界上第一个…

tomcat安装启动配置

服务器&#xff1a; 服务器的概念非常的广泛&#xff0c;它可以指代一台特殊的计算机&#xff08;相比普通计算机运行更快、负载更高、价格更贵&#xff09;&#xff0c;也可以指代用于部署网站的应用。我们这里说的服务器&#xff0c;其实是web服务器&#xff0c;或者应用服务…

JS在页面限制checkbox最大复选数

应该是挺简单的代码, 记录一下分享. 首先最直接的想法就是使用循环, 用局部变量记录已选的checkbox, 达到最大值就将余下的checkbox都禁止选择, 例如以下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" ""> <html xmlns"…

c语言 二维数组 文库,c语言二维数组练习题

c语言二维数组练习题 (3页)本资源提供全文预览&#xff0c;点击全文预览即可全文预览,如果喜欢文档就下载吧&#xff0c;查找使用更方便哦&#xff01;19.90 积分完成下列程序代码完成下列程序代码1、 将二维数组(5 行 5 列)的右上半部分置零。 即&#xff1a;1234510000678910…