【Java基础总结】多线程
1. 实现多线程的两种方式
//第一种:继承Thread类,重写run()方法
class ThreadTest1 extends Thread{
public void run(){
String threadName = Thread.currentThread().getName();
for(int i=0;i<10;i++){
System.out.println("ThreadTest1 "+threadName+" running ... "+i);
}
}
} //第二种:实现Runnable接口,重写run()方法
class ThreadTest2 implements Runnable{
public void run(){
String threadName = Thread.currentThread().getName();
for(int i=0;i<10;i++){
System.out.println("ThreadTest2 "+threadName+" running ... "+i);
}
}
}
实现方式不同,使用方式也不同
public class Demo1{
public static void main(String[] args){
ThreadTest1 t1 = new ThreadTest1();
ThreadTest1 t2 = new ThreadTest1();
Thread t3 = new Thread(new ThreadTest2());
Thread t4 = new Thread(new ThreadTest2());
t1.start();
t2.start();
t3.start();
t4.start();
}
}
运行结果大致如下:
ThreadTest1 Thread-0 running ... 0
ThreadTest2 Thread-3 running ... 0
ThreadTest2 Thread-2 running ... 0
ThreadTest1 Thread-1 running ... 0
ThreadTest2 Thread-2 running ... 1
ThreadTest2 Thread-3 running ... 1
ThreadTest1 Thread-0 running ... 1
ThreadTest2 Thread-3 running ... 2
ThreadTest2 Thread-2 running ... 2
ThreadTest1 Thread-1 running ... 1
ThreadTest2 Thread-2 running ... 3
ThreadTest2 Thread-3 running ... 3
ThreadTest1 Thread-0 running ... 2
ThreadTest2 Thread-3 running ... 4
ThreadTest2 Thread-2 running ... 4
ThreadTest1 Thread-1 running ... 2
ThreadTest2 Thread-2 running ... 5
ThreadTest2 Thread-3 running ... 5
ThreadTest1 Thread-0 running ... 3
ThreadTest2 Thread-3 running ... 6
ThreadTest2 Thread-2 running ... 6
ThreadTest1 Thread-1 running ... 3
ThreadTest2 Thread-2 running ... 7
ThreadTest2 Thread-3 running ... 7
ThreadTest2 Thread-3 running ... 8
ThreadTest2 Thread-3 running ... 9
ThreadTest1 Thread-0 running ... 4
ThreadTest1 Thread-0 running ... 5
ThreadTest2 Thread-2 running ... 8
ThreadTest2 Thread-2 running ... 9
ThreadTest1 Thread-1 running ... 4
ThreadTest1 Thread-0 running ... 6
ThreadTest1 Thread-0 running ... 7
ThreadTest1 Thread-0 running ... 8
ThreadTest1 Thread-1 running ... 5
ThreadTest1 Thread-0 running ... 9
ThreadTest1 Thread-1 running ... 6
ThreadTest1 Thread-1 running ... 7
ThreadTest1 Thread-1 running ... 8
ThreadTest1 Thread-1 running ... 9
2. 线程共享资源
建议使用 实现Runnable接口,重写run方法 的方式来实现多线程,它有如下优点:
1. 线程和代码分离,多线程间可以共享资源
2. 避免了单继承带来的局限性
3. 多线程之间可以共享资源
tip
Thread.currentThread().getName() 获得当前线程的名称
threadObj.setName() 设置线程名称
案例:售票
class ThreadTest4 implements Runnable{
private int ticket=20;
public void run(){
String threadName = Thread.currentThread().getName();
while(ticket>0){
System.out.println("ThreadTest4 "+threadName+" 售出 "+ticket+" 号票");
ticket--;
}
}
}
使用情况1:
(new Thread(new ThreadTest4(), "窗口a")).start();
(new Thread(new ThreadTest4(), "窗口b")).start();
(new Thread(new ThreadTest4(), "窗口c")).start();
(new Thread(new ThreadTest4(), "窗口d")).start();
运行情况说明A、B、C、D四个窗口也没用共享count这个资源
主线程名称:main
ThreadTest4 窗口a 售出 20 号票
ThreadTest4 窗口a 售出 19 号票
ThreadTest4 窗口b 售出 20 号票
ThreadTest4 窗口b 售出 19 号票
ThreadTest4 窗口b 售出 18 号票
ThreadTest4 窗口b 售出 17 号票
ThreadTest4 窗口b 售出 16 号票
ThreadTest4 窗口b 售出 15 号票
ThreadTest4 窗口a 售出 18 号票
ThreadTest4 窗口b 售出 14 号票
ThreadTest4 窗口d 售出 20 号票
ThreadTest4 窗口c 售出 20 号票
ThreadTest4 窗口d 售出 19 号票
ThreadTest4 窗口b 售出 13 号票
ThreadTest4 窗口a 售出 17 号票
ThreadTest4 窗口b 售出 12 号票
ThreadTest4 窗口d 售出 18 号票
ThreadTest4 窗口c 售出 19 号票
ThreadTest4 窗口d 售出 17 号票
ThreadTest4 窗口b 售出 11 号票
ThreadTest4 窗口a 售出 16 号票
ThreadTest4 窗口b 售出 10 号票
ThreadTest4 窗口d 售出 16 号票
ThreadTest4 窗口c 售出 18 号票
ThreadTest4 窗口d 售出 15 号票
ThreadTest4 窗口b 售出 9 号票
ThreadTest4 窗口a 售出 15 号票
ThreadTest4 窗口b 售出 8 号票
ThreadTest4 窗口d 售出 14 号票
ThreadTest4 窗口c 售出 17 号票
ThreadTest4 窗口d 售出 13 号票
ThreadTest4 窗口b 售出 7 号票
ThreadTest4 窗口a 售出 14 号票
ThreadTest4 窗口b 售出 6 号票
ThreadTest4 窗口d 售出 12 号票
ThreadTest4 窗口c 售出 16 号票
ThreadTest4 窗口d 售出 11 号票
ThreadTest4 窗口b 售出 5 号票
ThreadTest4 窗口a 售出 13 号票
ThreadTest4 窗口b 售出 4 号票
ThreadTest4 窗口d 售出 10 号票
ThreadTest4 窗口c 售出 15 号票
ThreadTest4 窗口d 售出 9 号票
ThreadTest4 窗口b 售出 3 号票
ThreadTest4 窗口a 售出 12 号票
ThreadTest4 窗口b 售出 2 号票
ThreadTest4 窗口d 售出 8 号票
ThreadTest4 窗口c 售出 14 号票
ThreadTest4 窗口d 售出 7 号票
ThreadTest4 窗口b 售出 1 号票
ThreadTest4 窗口a 售出 11 号票
ThreadTest4 窗口a 售出 10 号票
ThreadTest4 窗口d 售出 6 号票
ThreadTest4 窗口c 售出 13 号票
ThreadTest4 窗口d 售出 5 号票
ThreadTest4 窗口a 售出 9 号票
ThreadTest4 窗口d 售出 4 号票
ThreadTest4 窗口c 售出 12 号票
ThreadTest4 窗口d 售出 3 号票
ThreadTest4 窗口a 售出 8 号票
ThreadTest4 窗口d 售出 2 号票
ThreadTest4 窗口c 售出 11 号票
ThreadTest4 窗口d 售出 1 号票
ThreadTest4 窗口a 售出 7 号票
ThreadTest4 窗口c 售出 10 号票
ThreadTest4 窗口a 售出 6 号票
ThreadTest4 窗口a 售出 5 号票
ThreadTest4 窗口a 售出 4 号票
ThreadTest4 窗口a 售出 3 号票
ThreadTest4 窗口a 售出 2 号票
ThreadTest4 窗口a 售出 1 号票
ThreadTest4 窗口c 售出 9 号票
ThreadTest4 窗口c 售出 8 号票
ThreadTest4 窗口c 售出 7 号票
ThreadTest4 窗口c 售出 6 号票
ThreadTest4 窗口c 售出 5 号票
ThreadTest4 窗口c 售出 4 号票
ThreadTest4 窗口c 售出 3 号票
ThreadTest4 窗口c 售出 2 号票
ThreadTest4 窗口c 售出 1 号票
使用情况2:
ThreadTest4 t2 = new ThreadTest4();
(new Thread(t2,"窗口1")).start();
(new Thread(t2,"窗口2")).start();
(new Thread(t2,"窗口3")).start();
(new Thread(t2,"窗口4")).start();
运行情况说明A、B、C、D四个窗口共享count这个资源(但发生了访问冲突)
主线程名称:main
ThreadTest4 窗口1 售出 20 号票
ThreadTest4 窗口2 售出 20 号票
ThreadTest4 窗口2 售出 19 号票
ThreadTest4 窗口2 售出 18 号票
ThreadTest4 窗口2 售出 17 号票
ThreadTest4 窗口2 售出 16 号票
ThreadTest4 窗口2 售出 15 号票
ThreadTest4 窗口2 售出 14 号票
ThreadTest4 窗口2 售出 13 号票
ThreadTest4 窗口2 售出 12 号票
ThreadTest4 窗口2 售出 11 号票
ThreadTest4 窗口2 售出 10 号票
ThreadTest4 窗口2 售出 9 号票
ThreadTest4 窗口2 售出 8 号票
ThreadTest4 窗口2 售出 7 号票
ThreadTest4 窗口3 售出 7 号票
ThreadTest4 窗口1 售出 5 号票
ThreadTest4 窗口2 售出 6 号票
ThreadTest4 窗口1 售出 3 号票
ThreadTest4 窗口4 售出 4 号票
ThreadTest4 窗口3 售出 4 号票
ThreadTest4 窗口1 售出 1 号票
ThreadTest4 窗口2 售出 2 号票
3. 线程同步
多线程中涉及到共享数据时,会出现线程安全问题。就上面的售票案例来说,若没有加 synchronized 关键字,在多个线程同时使用ticket这个共享数据时,会出现同一个ticket被使用两次这样的看似不可能的情况。另外还有一种情况,事实上ticket这个共享数据是类ThreadTest4对象t2中的变量,所以若是在主线程中添加t2.run();语句的话,也是会发生线程安全问题的。
在Java里面,同步锁的概念就是这样的。任何一个Object Reference都可以作为同步锁。我们可以把Object Reference理解为对象在内存分配系统中的内存地址。
class ThreadTest5 implements Runnable{
private int ticket=20;
public void run(){
while(ticket>0){
String threadName = Thread.currentThread().getName();
//同步代码块(越小越好)
synchronized(this){
if(ticket>0){
System.out.println(threadName + " sales ticket "+ticket);
ticket--;
}
}
}
}
}
public class Demo3{
public static void main(String[] args){
ThreadTest5 t = new ThreadTest5();
(new Thread(t, "窗口A")).start();
(new Thread(t, "窗口B")).start();
(new Thread(t, "窗口C")).start();
(new Thread(t, "窗口D")).start();
}
}
运行结果
窗口A sales ticket 20
窗口A sales ticket 19
窗口A sales ticket 18
窗口A sales ticket 17
窗口A sales ticket 16
窗口A sales ticket 15
窗口A sales ticket 14
窗口A sales ticket 13
窗口A sales ticket 12
窗口A sales ticket 11
窗口A sales ticket 10
窗口D sales ticket 9
窗口D sales ticket 8
窗口D sales ticket 7
窗口D sales ticket 6
窗口D sales ticket 5
窗口D sales ticket 4
窗口D sales ticket 3
窗口C sales ticket 2
窗口B sales ticket 1
(1)同步代码块
synchronized(类或对象){
//需要同步的代码段
}
(2)同步函数
(非static的情况)
public synchronized void fun(){
//代码段
}
等价于(调用此同步函数的对象作为此同步函数的同步锁)
public void fun() {
synchronized(this) {
//代码段
}
}
(static的情况)
public static synchronized void fun() {
//代码段
}
静态变量或静态方法加载到内存中时,内存中没有本类对象,但一定有了该类对应的字节码文件(类名.class),该对象的类型是class。静态同步函数使用的同步锁是所在类的字节码文件。
线程同步的前提:
1)2个或2个以上的线程
2)使用同一把锁
保证同步中只有一个线程在运行。
好处:解决线程安全问题
弊端:多个线程需要判断锁,消耗资源
4. 线程通信
经典的生产者和消费者问题
假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中的产品取走消费。如果仓库中没有产品,则生产者可以将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止。如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止。显然,这是一个同步问题,生产者和消费者共享同一资源,并且,生产者和消费者之间彼此依赖,互为条件向前推进

4.1 synchronized和wait、notify、notifyAll
wait() 使得当前线程必须要等待,并释放对锁的拥有权,等到另外一个线程调用notify()或者notifyAll()方法
notify() 会唤醒一个等待当前对象的锁的线程
notifyAll() 唤醒所有一个等待当前对象的锁的线程
一个小比较
当线程调用了wait()方法时,它会释放掉对象的锁。
另一个会导致线程暂停的方法:Thread.sleep(millisecond),它会导致线程睡眠指定的毫秒数,但线程在睡眠的过程中是不会释放掉对象的锁的。
class Repository{
private int count=0; //当前仓库存放商品数量
private int capacity=5; //仓库容量
private String goodsName; //商品名称
public Repository(String goodsName){
this.goodsName = goodsName;
}
public void store(String threadName){
synchronized(this){
while(this.count>=this.capacity){
System.out.println("[" + threadName + "]仓库已达到最大容量 " + this.capacity + " 个 !!");
try{this.wait();}
catch(Exception e){}
}
this.count++;
System.out.println("[" + threadName + "]仓库增加了一个"+this.goodsName+",现有"+this.goodsName+"["+this.count+"] 个");
this.notifyAll();
}
}
public void fetch(String threadName){
synchronized(this){
while(this.count<1){
System.out.println("[" + threadName + "]仓库没有"+this.goodsName+"!!");
try{
this.wait();
}catch(Exception e){
}
}
this.count--;
System.out.println("[" + threadName + "]仓库减少了一个"+this.goodsName+",现有"+this.goodsName+"["+this.count+"] 个");
this.notifyAll();
}
}
}
//生产者
class Producter implements Runnable{
private Repository repository ;
public Producter(Repository repository){
this.repository = repository;
}
public void run(){
String threadName = Thread.currentThread().getName();
while(true){
try{
//sleep 2秒模拟生产过程
Thread.sleep(1000);
}catch(Exception e){
}
//把生产的商品存放到仓库
repository.store(threadName);
}
}
}
//消费者
class Consumer implements Runnable{
private Repository repository ;
public Consumer(Repository repository){
this.repository = repository;
}
public void run(){
String threadName = Thread.currentThread().getName();
while(true){
//将商品从仓库取出来
repository.fetch(threadName);
try{
//sleep 4秒模拟消费过程
Thread.sleep(2000);
}catch(Exception e){
}
}
}
}
public class Thread06{
public static void main(String[] args){
Repository repository = new Repository("馒头");
Producter pro1 = new Producter(repository);
Consumer con1 = new Consumer(repository);
//生产者和消费者各2个
(new Thread(pro1, "生产者A")).start();
(new Thread(pro1, "生产者B")).start();
(new Thread(con1, "消费者1")).start();
(new Thread(con1, "消费者2")).start();
}
}
其中一种运行结果:
[消费者1]仓库没有馒头!!
[消费者2]仓库没有馒头!!
[生产者A]仓库增加了一个馒头,现有馒头[1] 个
[消费者2]仓库减少了一个馒头,现有馒头[0] 个
[消费者1]仓库没有馒头!!
[生产者B]仓库增加了一个馒头,现有馒头[1] 个
[消费者1]仓库减少了一个馒头,现有馒头[0] 个
[生产者A]仓库增加了一个馒头,现有馒头[1] 个
[生产者B]仓库增加了一个馒头,现有馒头[2] 个
[生产者A]仓库增加了一个馒头,现有馒头[3] 个
[消费者2]仓库减少了一个馒头,现有馒头[2] 个
[生产者B]仓库增加了一个馒头,现有馒头[3] 个
[消费者1]仓库减少了一个馒头,现有馒头[2] 个
[生产者A]仓库增加了一个馒头,现有馒头[3] 个
[生产者B]仓库增加了一个馒头,现有馒头[4] 个
[消费者2]仓库减少了一个馒头,现有馒头[3] 个
[生产者A]仓库增加了一个馒头,现有馒头[4] 个
[消费者1]仓库减少了一个馒头,现有馒头[3] 个
[生产者B]仓库增加了一个馒头,现有馒头[4] 个
[生产者A]仓库增加了一个馒头,现有馒头[5] 个
[生产者B]仓库已达到最大容量 5 个 !!
[消费者2]仓库减少了一个馒头,现有馒头[4] 个
[生产者B]仓库增加了一个馒头,现有馒头[5] 个
[生产者A]仓库已达到最大容量 5 个 !!
[消费者1]仓库减少了一个馒头,现有馒头[4] 个
[生产者A]仓库增加了一个馒头,现有馒头[5] 个
[生产者B]仓库已达到最大容量 5 个 !!
[生产者A]仓库已达到最大容量 5 个 !!
(运行结果分析)
1)线程“消费者1”调用repositoy.fetch()方法去仓库取馒头。进入synchronized块,刚一执行while语句,结果 this.count<1 为真,直接就wait()进入等待队列,最后释放了锁
(就绪队列[消费者2,生产者A,生产者B],等待队列[消费者1])
2)线程“消费者2”同“消费者1”的遭遇是相同的(对此,我们深表同情)
(就绪队列[生产者A,生产者B], 等待队列[消费者1,消费者2])
3)线程“生产者A”生产完商品后,调用repository.store()方法把商品存到了仓库中。在store方法里,使用notifyAll方法唤醒了所有在沉睡wait的线程,最后释放了锁
(就绪队列[生产者A,生产者B,消费者1,消费者2],等待队列[])
4)线程“消费者2”从上次wait的地方开始执行(从哪里跌倒,就从哪里爬起来)。同样是while语句,但这次 this.count<1 不为真了,于是乎顺利脱坑,接着执行 this.count--,把仓库仅有的一个馒头给拿走了,走之前还不忘大喊一句“仓库减少了一个馒头,现有馒头[0] 个”。同样是notifyAll方法唤醒所有沉睡wait的线程,释放锁
(就绪队列[生产者A,生产者B,消费者1,消费者2],等待队列[])
5)线程“消费者1”辛辛苦苦抢到了锁,终于能执行syschronized块代码了。和“消费者2”一样,也是从上次wait的地方接着执行,也是while语句,但不同的是 this.count<1 为真,线程“生产者A”生产存放到仓库的仅有的一个馒头被“消费者2”给吃了,可以想象此时的“消费者1”心里是万念俱灰的。啥也别说了,接着沉睡wait吧。(释放了锁)
(就绪队列[生产者A,生产者B,消费者2],等待队列[消费者1])
**** 在接下来的几十个回合中,时而生产者线程夺得仓库锁,称霸武林,时而消费者线程夺得,笑傲江湖,仓库商品也是时增时减,但总体上还是生产者线程抢到的次数多,因为生产者够快,当消费者还在花2秒钟费劲的消化时,生产者早就1秒生产完毕,参与仓库锁的再次争夺了,可见“天下武功,唯快不破” ***** “仓库里没有馒头为什么不通知我?”,线程“消费者2”不满“消费者1”对仓库情况的隐瞒不报,“你要是早告诉我,我也就不用争抢仓库锁,抢到了也没用,里面根本就没有馒头,去了也是wait”。(第1、2步)
“你还有脸说我,你把生产者A生产存放在仓库仅有的一个馒头吃了,你明知道仓库再也没有馒头了,也不告诉我,还让我傻了吧唧抢到仓库锁,去了也白搭”。“消费者1”反驳道,它同样也很委屈。(第4、5步)
“管我什么事,抢仓库锁的有不只你一个,我也去抢了,白费了劲,还没抢到....”,线程“消费者2”道。 “要是仓库没有馒头的时侯,只唤醒那帮生产者就好了”,线程“消费者1”和“消费者2”异口同声的说道。
处理线程通信必须遵循一种原则:对于生产者,在生产者没有生产之前,要通知消费者等待;在生产者生产之后,马上又通知消费者消费;对于消费者,在消费者消费之后,要通知生产者已经消费结束,需要继续生产新的产品以供消费。
4.2 Lock和Condition
Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition 对象。
Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用
5. 停止线程的方法
interrupt() //停止线程
isInterrupt() //判断线程是否停止
6. 守护线程和join方法
守护线程是为其他线程提供便利服务的,当全部的用户线程结束后,守护线程才会随JVM结束工作。 thread.setDaemon(true);
public static void main(String[] args){
Thread t3 = new Thread(test2, "线程t3");
t3.start();
t3.join(); //主线程就此陷入等待,直到t3线程结束。
}
7. 线程优先级和yield方法
线程的优先级从低到高:1-10,优先级高的的优先执行,每个新线程都继承了父线程的优先级,常量:Thread.MIN_PRIORITY 值为1,Thread.MAX_PRIORITY 值为10,Thread.NORM_PRIORITY 值为5
void setPriority(priority) //设置线程优先级
int getPriority() //获取线程优先级
yield() 线程从执行状态变成就绪状态。
【Java基础总结】多线程的更多相关文章
- Java基础技术多线程与并发面试【笔记】
Java基础技术多线程与并发 什么是线程死锁? 死锁是指两个或两个以上的进程(线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去,我们就可以称 ...
- Java基础之多线程篇(线程创建与终止、互斥、通信、本地变量)
线程创建与终止 线程创建 Thread类与Runnable接口的关系 public interface Runnable { public abstract void run(); } public ...
- 关于java基础、多线程、JavaWeb基础、数据库、SSM、Springboot技术汇总
作者 : Stanley 罗昊 本人自行总结,纯手打,有疑问请在评论区留言 [转载请注明出处和署名,谢谢!] 一.java基础 1.多态有哪些体现形式? 重写.重载 2. Overriding的是什么 ...
- Java基础之多线程框架
一.进程与线程的区别 1.定义: 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位. 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比 ...
- Java基础之多线程详细分析
在了解多线程之前,先来了解一下进程与线程之间的关系. 进程和线程: 进程是指在系统中正在执行的一个程序,每个进程之间是独立的. 线程是进程的一个基本执行单元.一个进程要想执行任务,必须得有线程(每1个 ...
- java基础知识 多线程
package org.base.practise9; import org.junit.Test; import java.awt.event.WindowAdapter; import java. ...
- Java基础之多线程
1.进程和线程: 进程:正在进行的程序.每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元. 线程:进程内部的一条执行路径或者一个控制单元. 两者的区别: 一个进程至少有一个线程 ...
- 黑马程序员——【Java基础】——多线程
---------- android培训.java培训.期待与您交流! ---------- 一.概述 (一)进程 正在执行中的程序,每一个进程执行都有一个执行顺序.该顺序是一个执行路径,或者叫一个控 ...
- 黑马程序员——JAVA基础之多线程的线程间通讯等
------- android培训.java培训.期待与您交流! ---------- 线程间通讯: 其实就是多个线程在操作同一个资源,但是动作不同. wait(); 在其他线程调用此对象的notif ...
- 黑马程序员——JAVA基础之多线程的安全问题
------- android培训.java培训.期待与您交流! ---------- 导致多线程出现问题的一个特殊的状态:就绪.具备了执行资格,但是还没有获取资源. 导致安全问题的出现的原因: 1. ...
随机推荐
- kindeditor编辑器微软雅黑样式font-family值变成"
http://www.100cm.cn/article-126-764.html kindeditor编辑器中选中文字, 修改字体(字体名称中带有空格, 例如"Microsoft YaHei ...
- 软件自动化测试 selenium IDE + Firebug + python脚本
按顺序步骤来 一.安装软件 1.1.1 webDriver(就是selenium IDE) 解析:本来这两个东西就合成一个了,但是更新到后来,安装的时候又独立安装的. 安装 Python ...
- Codeforces Round #188 (Div. 1 + Div. 2)
A. Even Odds 奇数个数\(\lfloor \frac{n+1}{2}\rfloor\) B. Strings of Power 从位置0开始,统计heavy个数,若当前为metal,则可以 ...
- java什么叫面向对象?
面向对象(Object-Oriented,简称OO)就是一种常见的程序结构设计方法. 面向对象思想的基础是将相关的数据和方法放在一起,组合成一种新的复合数据类型,然后使用新创建的复合数据类型作为项目的 ...
- 关于Ping和Tracert命令原理详解
本文只是总结了两个常用的网络命令的实现原理和一点使用经验说明.这些东西通常都分布在各种书籍或者文章中的,我勤快那么一点点,总结一下,再加上我的一点理解和使用经验,方便大家了解.这些也是很基础的东西,没 ...
- HDU 1828“Picture”(线段树+扫描线求矩形周长并)
传送门 •参考资料 [1]:算法总结:[线段树+扫描线]&矩形覆盖求面积/周长问题(HDU 1542/HDU 1828) •题意 给你 n 个矩形,求矩形并的周长: •题解1(两次扫描线) 周 ...
- P1044 最大值最小化
题目描述 在印刷术发明之前,复制一本书是一个很困难的工作,工作量很大,而且需要大家的积极配合来抄写一本书,团队合作能力很重要.当时都是通过招募抄写员来进行书本的录入和复制工作的, 假设现在要抄写 \( ...
- PowerShell 通过 WMI 获取系统安装的驱动
本文告诉大家如何通过 WMI 获取用户已经安装的驱动程序 通过下面代码可以获取用户已经安装的驱动程序 Get-WmiObject Win32_SystemDriver | Format-List Ca ...
- Spark MLlib 示例代码阅读
阅读前提:有一定的机器学习基础, 本文重点面向的是应用,至于机器学习的相关复杂理论和优化理论,还是多多看论文,初学者推荐Ng的公开课 /* * Licensed to the Apache Softw ...
- dotnet 使用 MessagePack 序列化对象
和很多序列化库一样,可以通过 MessagePack 序列化和反序列化,和 json 相比这个库提供了二进制的序列化,序列化之后的内容长度比 json 小很多 这个库能序列的内容不多,大多数时候建议使 ...