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基础总结】多线程的更多相关文章

  1. Java基础技术多线程与并发面试【笔记】

    Java基础技术多线程与并发 什么是线程死锁? ​死锁是指两个或两个以上的进程(线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去,我们就可以称 ...

  2. Java基础之多线程篇(线程创建与终止、互斥、通信、本地变量)

    线程创建与终止 线程创建 Thread类与Runnable接口的关系 public interface Runnable { public abstract void run(); } public ...

  3. 关于java基础、多线程、JavaWeb基础、数据库、SSM、Springboot技术汇总

    作者 : Stanley 罗昊 本人自行总结,纯手打,有疑问请在评论区留言 [转载请注明出处和署名,谢谢!] 一.java基础 1.多态有哪些体现形式? 重写.重载 2. Overriding的是什么 ...

  4. Java基础之多线程框架

    一.进程与线程的区别 1.定义: 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位. 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比 ...

  5. Java基础之多线程详细分析

    在了解多线程之前,先来了解一下进程与线程之间的关系. 进程和线程: 进程是指在系统中正在执行的一个程序,每个进程之间是独立的. 线程是进程的一个基本执行单元.一个进程要想执行任务,必须得有线程(每1个 ...

  6. java基础知识 多线程

    package org.base.practise9; import org.junit.Test; import java.awt.event.WindowAdapter; import java. ...

  7. Java基础之多线程

    1.进程和线程: 进程:正在进行的程序.每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元. 线程:进程内部的一条执行路径或者一个控制单元. 两者的区别: 一个进程至少有一个线程 ...

  8. 黑马程序员——【Java基础】——多线程

    ---------- android培训.java培训.期待与您交流! ---------- 一.概述 (一)进程 正在执行中的程序,每一个进程执行都有一个执行顺序.该顺序是一个执行路径,或者叫一个控 ...

  9. 黑马程序员——JAVA基础之多线程的线程间通讯等

    ------- android培训.java培训.期待与您交流! ---------- 线程间通讯: 其实就是多个线程在操作同一个资源,但是动作不同. wait(); 在其他线程调用此对象的notif ...

  10. 黑马程序员——JAVA基础之多线程的安全问题

    ------- android培训.java培训.期待与您交流! ---------- 导致多线程出现问题的一个特殊的状态:就绪.具备了执行资格,但是还没有获取资源. 导致安全问题的出现的原因: 1. ...

随机推荐

  1. html中让多个li标签横排显示

    1.我们可以通过为ul标签下所有li标签设定样式“display:inline-block”的方式,让多个li标签横排显示 2.除了以上方法,我们还可以所有li标签使用float(浮动)的方式,让多个 ...

  2. Python--day23--类的命名空间

    当创建一个对象时,就会在内存中分出一块新的空间存放这个对象的属性,这块空间也叫类的命名空间.里面存放着类对象指针可以找到类.

  3. HDU 1072

    题意:给你一个迷宫,2代表你当前的位置,0代表墙,1代表可走的路,3代表出口,4代表的是炸弹的重置点,一开始炸弹的倒计时设置为6,每走一步时间减少1,倒计时到0的时候走到3或者4都不可以,问走出迷宫的 ...

  4. LA 5031 Graph and Queries —— Treap名次树

    离线做法,逆序执行操作,那么原本的删除边的操作变为加入边的操作,用名次树维护每一个连通分量的名次,加边操作即是连通分量合并操作,每次将结点数小的子树向结点数大的子树合并,那么单次合并复杂度O(n1lo ...

  5. mpvue的坑,持续更新-.-

    mpvue... 坑 怎么说呢,去github看一下,发现还是有很多问题没有解决... 不支持filter 亲,到现在还没有支持filter哦.只能用替代方法了,用computed或者渲染前先处理数据 ...

  6. H3C IP地址动态获取过程

  7. Linux 内核总线属性

    几乎 Linux 驱动模型中的每一层都提供一个添加属性的接口, 并且总线层不例外. bus_attribute 类型定义在 <linux/device.h> 如下: struct bus_ ...

  8. Educational Codeforces Round 64部分题解

    Educational Codeforces Round 64部分题解 A 题目大意:给定三角形(高等于低的等腰),正方形,圆,在满足其高,边长,半径最大(保证在上一个图形的内部)的前提下. 判断交点 ...

  9. CSU 2323 疯狂的企鹅II (中位数的性质)

    Description 继在鹅厂工作的DJ训练完鹅厂的企鹅们之后,DJ发明了一个新游戏.该游戏在nxn的棋盘上进行,其中恰好有n个企鹅,企鹅向四个方向之一移动一格算作一步.DJ希望用最少的总步数把这些 ...

  10. 小白学 Python 爬虫(29):Selenium 获取某大型电商网站商品信息

    人生苦短,我用 Python 前文传送门: 小白学 Python 爬虫(1):开篇 小白学 Python 爬虫(2):前置准备(一)基本类库的安装 小白学 Python 爬虫(3):前置准备(二)Li ...