Java——多线程使用详解

多线程:
  • 多线程就是同时执行多个应用程序,需要硬件的支持
  • 同时执行:不是某个时间段同时,cpu切换的比较快,所有用户会感觉是在同时运行
并发与并行:
  • 并行(parallel):指在同一时刻,有多条指令在多个处理器上同时执行。并行必须借助于多核cpu实现

  • 并发(concurrency):指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,通过cpu时间片轮转使多个进程快速交替的执行。

程序、线程、进程:

程序:是指编译好的可执行文件,程序启动的时候,进程作为支撑
进程:是正在运行的程序(比如360杀毒软件),进程可以产生多线程

独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位
动态性:进程的实质是程序的一次执行过程,进程是动态产生,动态消亡的
并发性:任何进程都可以同其他进程一起并发执行
进程基本的状态有5种:分别为初始态、就绪态、运行态、挂起态、终止态其中初始态为进程准备阶段,常与就绪态结合来看。

线程:是程序正在做的事情,线程是进程的单个控制流(比如360的杀毒,扫描木马)

单线程:一个进程如果只有一条执行路径,则称为单线程程序
多线程:一个进程如果有多条执行路径,则称为多线程程序

进程与线程的区别:

  • 进程:有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程。
  • 线程:堆空间是共享的,栈空间是独立的,线程消耗的资源比进程小的多。

多线程同时执行原理:

比如我们同时运行qq和微信,其实不是同时运行的而是CPU在多个线程间快速切换,造成"同时"执行的假象

多线程的好处:

  • 可以"同时"执行多个任务
  • 可以提高资源的利用率(CPU/网络)

线程越多越好吗:

1.创建和销毁线程需要消耗CPU和内存资源
2.线程太多,CPU需要在大量的线程间切换,造成资源的浪费

进程与并发:
在使用进程实现并发时会出现以下问题:

  1. 系统开销比较大,占用资源比较多,开启进程数量比较少。
  2. 在unix/linux系统下,还会产生孤儿进程僵尸进程。正常情况下,子进程是通过父进程fork创建的,子进程再创建新的进程。并且父进程永远无法预测子进程 到底什么时候结束。 当一个 进程完成它的工作终止之后,它的父进程需要调用系统调用取得子进程的终止状态。

孤儿进程:

父进程比子进程先结束,子进程成为孤儿进程,子进程的父进程成为init进程,称为init进程领养孤儿进程。

僵尸进程:

僵尸进程: 进程终止,父进程尚未回收,子进程残留资源(PCB)存放于内核中,变成僵尸(Zombie)进程。

Windows&Linux进程:

Windows下的进程和Linux下的进程是不一样的,它比较懒惰,从来不执行任何东西,只是为线程提供执行环境。然后由线程负责执行包含在进程的地址空间中的代码。当创建一个进程的时候,操作系统会自动创建这个进程的第一个线程,成为主线程。

创建线程:

实现多线程有三种方法:

  1. 继承Thread类
  2. 实现Runnable接口
  3. 实现Callable接口

开启线程

方法名说明
void run()在线程开启后,此方法将被调用执行
void start()使此线程开始执行,Java虚拟机会调用run方法()

为什么要重写run()方法?

因为run()是用来封装被线程执行的代码

run()方法和start()方法的区别?

  • run():封装线程执行的代码,直接调用,相当于普通方法的调用,并没有开启线程
  • start():启动线程;然后由JVM调用此线程的run()方法
继承Thread:

实现步骤:

  • 定义一个类MyThread继承Thread类
  • 在MyThread类中重写run()方法
  • 创建MyThread类的对象
  • 启动线程

多线程类:

public class ThreadDemo extends Thread {// run是用来封装被线程执行的代码的,一定要重写@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println("线程启动" + i);}}
}

测试类:

public class TestThread {public static void main(String[] args) {// 创建线程对象ThreadDemo thread1 = new ThreadDemo();ThreadDemo thread2 = new ThreadDemo();// 开启线程thread1.start();thread2.start();}
}
实现Runnable:

实现步骤:

  • 定义一个类MyRunnable实现Runnable接口
  • 在MyRunnable类中重写run()方法
  • 创建MyRunnable类的对象
  • 创建Thread类的对象,把MyRunnable对象作为构造方法的参数
  • 启动线程

对象类:

public class RunnableDemo implements Runnable{@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println("线程启动" + i);}}
}

测试类:

public class TestRunnable {public static void main(String[] args) {// 创建参数对象RunnableDemo r1 = new RunnableDemo();RunnableDemo r2 = new RunnableDemo();// 创建线程Thread thread1 = new Thread(r1);Thread thread2 = new Thread(r2);// 开启线程thread1.start();thread2.start();}
}
实现Callable接口:

实现步骤:

  • 定义一个类MyCallable实现Callable接口
  • 在MyCallable类中重写call()方法
  • 创建MyCallable类的对象
  • 创建Future的实现类FutureTask对象,把MyCallable对象作为构造方法的参数
  • 创建Thread类的对象,把FutureTask对象作为构造方法的参数
  • 启动线程
  • 再调用get方法,就可以获取线程结束之后的结果。

构造方法:

方法名说明
Thread(Runnable target)分配一个新的Thread对象
Thread(Runnable target, String name)分配一个新的Thread对象

对象类:

public class CallDemo implements Callable<String> {@Overridepublic String call() throws Exception {for (int i = 0; i < 10; i++) {System.out.println("线程执行" + i);}// 返回值表示运行完以后的结果return "执行完毕";}
}

测试类:

public class TestCall {public static void main(String[] args) throws ExecutionException, InterruptedException {// 创建对象,线程要执行的代码CallDemo call = new CallDemo();// 获取线程执行完毕的结果,可以作为参数传给ThreadFutureTask<String> sft = new FutureTask<>(call);Thread thread = new Thread(sft);thread.start();// get:获取线程运行后的结果,如果在线程没开启前就获取get方法会一直等待,所以get方法要写在start后面System.out.println(sft.get());  }
}
三种实现方式对比:

实现Runnable、Callable接口

好处: 扩展性强,实现该接口的同时还可以继承其他的类
缺点: 编程相对复杂,不能直接使用Thread类中的方法

继承Thread类

好处: 编程比较简单,可以直接使用Thread类中的方法
缺点: 可以扩展性较差,不能再继承其他的类

获取与设置线程名称:
方法名说明
void setName(String name)将此线程的名称更改为等于参数name
String getName()返回此线程的名称
Thread currentThread()返回对当前正在执行的线程对象的引用

对象类:

public class ThreadDemo extends Thread {// 要写构造,否则不能传线程名称public ThreadDemo() {}public ThreadDemo(String name) {super(name);}@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(getName() + i);// 获取当前线程对象}// 线程没有设置名字的话,返回的是默认的名字,每个线程都是有默认名字的System.out.println("当前执行的线程是:" + Thread.currentThread().getName());}
}

测试类:

public class Test {public static void main(String[] args) {ThreadDemo t1 = new ThreadDemo();t1.start();t1.setName("线程一");// 通过构造设置,对象要创建有参构造ThreadDemo t2 = new ThreadDemo("线程二:");t2.start();// 如果是通过接口创建线程那么是不能用getname的,所以currentThread就可以代替了System.out.println(Thread.currentThread().getName());}
}
线程睡眠:
方法名说明
static void sleep(long millis)使当前正在执行的线程停留(暂停执行)指定的毫秒数

对象类:

public class DemoRunnable implements Runnable {@Overridepublic void run() {for (int i = 0; i < 10; i++) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + i);}}
}

测试类:

public class Test {public static void main(String[] args) {DemoRunnable dr = new DemoRunnable();Thread thread = new Thread(dr);thread.start();}
}
线程优先级:
  1. 分时调度:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU的时间
  2. 抢占式调度:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些
  • Java使用的是抢占式调度模型,线程优先级高只是枪战CPU的几率更大,不代表一定优先执行
方法名说明
final int getPriority()返回此线程的优先级
final void setPriority(int newPriority)更改此线程的优先级线程默认优先级是5;线程优先级的范围是:1-10
public class Test {public static void main(String[] args) {DemoRunnable dr = new DemoRunnable();Thread thread = new Thread(dr);thread.start();// final int getPriority()	返回此线程的优先级System.out.println(thread.getPriority());// final void setPriority(int newPriority)	更改此线程的优先级线程默认优先级是5;线程优先级的范围是:1-10thread.setPriority(10);System.out.println(thread.getPriority());}
}
守护线程:
方法名说明
void setDaemon(boolean on)将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出

线程一:

public class Thread1 extends Thread {@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(getName() + " :" + i);}}
}

线程二:

public class Thread2 extends Thread {@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(getName() + " :" + i);}}
}

测试类:

public class Test {public static void main(String[] args) {// 创建线程Thread1 t1 = new Thread1();Thread2 t2 = new Thread2();// 设置线程名字t1.setName("我女朋友");t2.setName("我");// 设置我女朋友为守护线程,我要挂了我女朋友也不能继续活,这就是家庭地位// 因为此时守护线程还有执行权,cpu执行很快,所以守护线程不是马上停止的,要把执行权走完t1.setDaemon(true);t1.start();t2.start();}
}
线程安全:

卖票案例分析线程安全:

重复票:定义三个线程去卖100张票,三个线程sleep后,第一个线程醒来拿到CPU执行权,此时将票减1,为99,刚减完线程二醒了。线程二拿到CPU执行权,此时将票减1,这时候是从99减1,为98,那这时候线程一也要变成98,因为执行的是同一个线程对象。刚减完线程三醒了。线程二拿到CPU执行权,此时将票减1,这时候是从98减1,为97…这时候就会出现重复票数
负数票:此时票数为1,线程1、2、3开始sleep,线程1醒来后,拿到CPU执行权,做减1操作,此时票数为0,线程1被销毁。线程2醒过来,然后拿到CPU执行权,做减1操作,此时票数为-1,线程2被销毁。线程3醒过来拿到CPU执行权,做减1操作,此时票数为-2,线程3被销毁。所以就会出现负数票问题

安全问题出现的条件

  1. 是多线程环境
  2. 有共享数据
  3. 有多条语句操作共享数据

如何解决多线程安全问题呢?

把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行,Java提供了同步代码块的方式来解决,
例:两个人同时上厕所,但是就一个马桶,于是用户A进入后,用户B只能在外面等,只有用户A出来,用户B才能进去。

同步代码块:
    synchronized(任意对象){  多条语句操作共享数据的代码  }// synchronized(任意对象):默认情况是打开的,只要有一个线程进去执行代码,锁就会关闭,当线程执行完出来,锁才会自动打开

同步代码块的好处和弊端:

好处:解决了多线程的数据安全问题
弊端:当线程很多时,每个线程都会去判断同步上的锁,很耗费资源,会降低程序的运行效率

对象类:

public class SellTicket implements Runnable {// 定义总票数private static int tickets = 100;// 创建一个任意的对象private final Object object = new Object();@Overridepublic void run() {while (true) {synchronized (object) {if (tickets > 0) {try {// 进来先睡一会Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");tickets--;} else {break;}}}}
}

测试类:

public class SellTicketDemo {public static void main(String[] args) {// 创建对象SellTicket st = new SellTicket();// 创建线程并设置名字,多线程同一个对象Thread t1 = new Thread(st, "窗口一");Thread t2 = new Thread(st, "窗口二");// 启动线程t1.start();t2.start();}
}
同步方法:

同步方法:锁对象是:this

修饰符  synchronized 返回值类型 方法名(方法参数) {方法体;}

对象类:

public class SellTicket2 implements Runnable {// 定义总票数private static int tickets = 100;// 创建一个任意的对象private final Object object = new Object();@Overridepublic void run() {while (true) {// 同步方法// 判断当前线程,是就调用方法,然后判断票数是不是0,不是就继续循环if ("窗口一".equals(Thread.currentThread().getName())) {boolean result = synchronizedMethod();if (result) {break;}}// 同步代码块if ("窗口二".equals(Thread.currentThread().getName())) {// 因为同步方法的锁是this,所以代码块也要是this才能都用一把锁synchronized (this) {if (tickets > 0) {try {// 进来先睡一会Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");tickets--;} else {break;}}}}}private synchronized boolean synchronizedMethod() {if (tickets > 0) {try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");tickets--;// 不是最后一张就继续循环return false;} else {// 最后一张就返回true,停止return true;}}
}

测试类:

public class SellTicketDemo {public static void main(String[] args) {SellTicket2 st = new SellTicket2();Thread t1 = new Thread(st, "窗口一");Thread t2 = new Thread(st, "窗口二");t1.start();t2.start();}
}

静态同步方法:锁对象是:类名.class

修饰符 static synchronized 返回值类型 方法名(方法参数) {方法体;}

静态方法在方法前加static,this换成类名.class就行了

Lock锁:

为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
Lock是接口不能直接实例化,可以用它的实现类ReentrantLock来实例化

ReentrantLock构造方法

方法名说明
ReentrantLock()创建一个ReentrantLock的实例

加锁解锁方法

方法名说明
void lock()获得锁
void unlock()释放锁

对象类:

public class Ticket implements Runnable {// 票的数量private int ticket = 100;// 创建Lock锁private ReentrantLock lock = new ReentrantLock();@Overridepublic void run() {while (true) {try {// 获得锁lock.lock();if (ticket == 0) {// 卖完了break;} else {Thread.sleep(30);ticket--;System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket + "张票");}} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}}
}

测试类:

    public static void main(String[] args) {Ticket ticket = new Ticket();Thread t1 = new Thread(ticket, "线程一");Thread t2 = new Thread(ticket, "线程二");// 设置优先级t1.setPriority(10);t2.setPriority(5);t1.start();t2.start();}
死锁:

线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行

死锁产生的条件:

1.有多个线程
2.有多把锁
3.有同步代码块嵌套

解决办法:

干掉其中某个条件

public class Demo05 {public static void main(String[] args) {MyRunnable mr = new MyRunnable();new Thread(mr).start();new Thread(mr).start();}
}class MyRunnable implements Runnable {Object objA = new Object();Object objB = new Object();/*嵌套1 objA嵌套1 objB嵌套2 objB嵌套1 objA*/@Overridepublic void run() {synchronized (objA) {System.out.println("嵌套1 objA");synchronized (objB) {// t2, objA, 拿不到B锁,等待System.out.println("嵌套1 objB");}}synchronized (objB) {System.out.println("嵌套2 objB");synchronized (objA) {// t1 , objB, 拿不到A锁,等待System.out.println("嵌套2 objA");}}}
}
生产者&消费者:

生产和消费主要是包含了两类线程:

生产和消费也称为等待唤醒机制
生产者线程用于生产数据
消费者线程用于消费数据

实现:

为了解耦生产者和消费者的关系,通常会采用共享的数据区域,就像是一个仓库
生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为
消费者只需要从共享数据区中去获取数据,并不需要关心生产者的行为

Object类的等待和唤醒方法

方法名说明
void wait()导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法
void notify()唤醒正在等待对象监视器的单个线程
void notifyAll()唤醒正在等待对象监视器的所有线程

对象类:

public class Desk {// 定义一个标记判断食物的状态,为false表示没有食物,true代表有食物private boolean flag;// 食物的个数private int count;// 锁对象private final Object lock = new Object();public Desk() {}public Desk(boolean flag, int count) {this.flag = flag;this.count = count;}public boolean isFlag() {return flag;}public void setFlag(boolean flag) {this.flag = flag;}public int getCount() {return count;}public void setCount(int count) {this.count = count;}public Object getLock() {return lock;}@Overridepublic String toString() {return "Desk{" +"flag=" + flag +", count=" + count +", lock=" + lock +'}';}
}

生产者:

public class Cooker extends Thread {private Desk desk;public Cooker(Desk desk) {this.desk = desk;}@Overridepublic void run() {while (true) {synchronized (desk.getLock()) {if (desk.getCount() == 0) {break;} else if (!desk.isFlag()) {System.out.println("生产者正在生产食物");// 有食物就改状态让消费者去消费desk.setFlag(true);desk.getLock().notifyAll();} else {// 没有食物就线程等待唤醒try {desk.getLock().wait();} catch (InterruptedException e) {e.printStackTrace();}}}}}
}

消费者:

public class Foodie extends Thread {private Desk desk;public Foodie(Desk desk) {this.desk = desk;}@Overridepublic void run() {while (true) {synchronized (desk.getLock()) {if (desk.getCount() == 0) {break;// 布尔类型变量的getset是is开头的} else if (desk.isFlag()) {System.out.println("消费者消费了一个食物");// 消费完毕,将标记设为false,当为false时可以让生产者去生产desk.setFlag(false);// 唤醒等待中的所有线程desk.getLock().notifyAll();// 消费一个减少一个desk.setCount(desk.getCount() - 1);} else {try {// 线程等待唤醒desk.getLock().wait();} catch (InterruptedException e) {e.printStackTrace();}}}}}
}

测试类:

public class Demo {public static void main(String[] args) {Desk desk = new Desk(false, 10);Foodie foodie = new Foodie(desk);Cooker cooker = new Cooker(desk);foodie.start();cooker.start();}
}
阻塞队列:

在这里插入图片描述

常见BlockingQueue:

ArrayBlockingQueue:底层是数组,有界
LinkedBlockingQueue:底层是链表,无界.但不是真正的无界,最大为int的最大值

BlockingQueue的核心方法:

方法名说明
put(anObject):将参数放入队列,如果放不进去会阻塞
take()取出第一个数据,取不到会阻塞

阻塞队列等待&唤醒:

阻塞队列底层是有自动加锁的,但是运行起来,可能打印出的是存两个打印两个,这个是控制台打印的问题。

生产者者线程

public class Cooker extends Thread {private ArrayBlockingQueue<String> bd;public Cooker(ArrayBlockingQueue<String> bd) {this.bd = bd;}@Overridepublic void run() {while (true) {try {bd.put("汉堡包");System.out.println("厨师放入一个汉堡包");} catch (InterruptedException e) {e.printStackTrace();}}}
}

消费者线程

    private ArrayBlockingQueue<String> bd;public Foodie(ArrayBlockingQueue<String> bd) {this.bd = bd;}@Overridepublic void run() {while (true) {try {String take = bd.take();System.out.println("吃货将" + take + "拿出来吃了");} catch (InterruptedException e) {e.printStackTrace();}}}

测试类:

  public class Demo {public static void main(String[] args) {// 创建阻塞队列对象,容量为1ArrayBlockingQueue<String> bd = new ArrayBlockingQueue<>(1);Foodie f = new Foodie(bd);Cooker c = new Cooker(bd);f.start();c.start();}}

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

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

相关文章

php边框的颜色 怎么设置,网页中table表格如何修改边框颜色

table表格是网页制作中以前较常使用的一种布局方式&#xff0c;但随着DIVCSS的兴起&#xff0c;table表格已辉煌不在。但它在我们制作网页中也是时有用到。table表格也可以使用DIV一样随意的设置它的边框颜色。下面介绍一下二种方法。方法一&#xff1a;使用CSS代码控制边框颜色…

看懂线程安全

目录&#xff1a; 线程安全线程同步同步代码块同步方法Lock锁线程状态图sleep睡眠等待和唤醒 1. 线程安全 如果有多个线程在同时运行&#xff0c;而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样 的&#xff0c;而且其他的变量的值也和预期的是…

oracle数据库生产,从安装系统到oracle数据库生产环境(centos6.8)搭建

大纲服务器安装JDK配置Oracle数据库安装配置1.安装服务器服务器的安装参考这篇教程关于安装中可能出现的几个问题&#xff0c;在这里进行解决&#xff1a;1、使用u盘安装完后&#xff0c;系统进入GRUB引导&#xff0c;无法启动&#xff1a;解决办法&#xff1a;出现上述状况的原…

详解线程池

概念&#xff1a; 什么是线程池: 线程池是用来存储多线程的容器&#xff0c;是一种处理形式&#xff0c;处理过程中将任务添加到队列&#xff0c;然后在创建线程后自动启动这些任务。 线程池使用和不使用的区别&#xff1a; 因为系统创建线程池的成本很高&#xff0c;会涉及到…

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;组织数据->挖掘知识->预测未来。…