/images/avatar.png

Java并发编程概览

Java并发编程概览 主要知识 多线程基础核心 → Synchronized实现原理 → Volatile实现原理 → JMM和指令重排 JUC → 原子类与CAS实现原理 → 锁与AQS实现原理→ 并发工具类→ 并发容器→ 阻塞队列→ 线程池 多线程相关概念 线程和进程 进程:是指内存中运行的一个应用程序,每个进程都有自己独立的内存空间;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过 程。 线程:是进程中的一个执行单元,负责当前进程中任务的执行。一个进程在其执行过程中,会产生很多个线程。 进程与线程区别 进程:有独立内存空间,每个进程中的数据空间都是独立的。 线程:多线程之间堆空间与方法区是共享的,但每个线程的栈空间、程序计数器是独立的,线程消耗的资源比进程小的多。 并发与并行 并发(Concurrent):同一时间段,多个任务都在执行 ,单位时间内不⼀定同时执行。 并行(Parallel):单位时间内,多个任务同时执行,单位时间内一定是同时执行。并行上限取决于CPU核数(CPU时间片内50ms) 注意:并发是一种能力,而并行是一种手段。当我们的系统拥有了并发的能力后,代码如果跑在多核CPU上就可以并行运行。所以咱们会说高并发处理,而不会说高并行处理。并行处理是基于硬件CPU的是固定的,而并发处理的能力是可以通过设计编码进行提高的。 线程上下文切换 一个CPU内核,同一时刻只能被一个线程使用。为了提升CPU利用率,CPU采用了时间片算法将CPU时间片轮流分配给多个线程,每个线程分配了一个时间片(几十毫秒/线程),线程在时间片内,使用CPU执行任务。当时间片用完后,线程会被挂起,然后把 CPU 让给其它线程。 CPU切换线程,会把当前线程的执行位置记录下来,用于下次执行时找到准确位置。 线程执行位置的记录与加载过程就叫做上下文切换。 线程执行位置记录在程序计数器 上下文切换过程 挂起当前任务任务,将这个任务在 CPU 中的状态(上下文)存储于内存中的某处。 恢复一个任务,在内存中检索下一个任务的上下文并将在 CPU 的寄存器中恢复。 跳转到程序计数器所指定的位置(即跳转到任务被中断时的代码行)。 线程上下文切换会有什么问题呢? 过多的线程并行执行会导致CPU资源的争抢,产生频繁的上下文切换,常常表现为高并发执行时,RT延长。因此,合理控制上下文切换次数,可以提高多线程应用的运行效率。(也就是说线程并不是越多越好,要合理的控制线程的数量。) 直接消耗:指的是CPU寄存器需要保存和加载,系统调度器的代码需要执行 间接消耗:指的是多核的cache之间得共享数据,间接消耗对于程序的影响要看线程工作区操作数据的大小 线程安全问题 什么是线程安全问题 多个线程同时执行,可能会运行同一行代码,如果程序每次运行结果与单线程执行结果一致,且变量的预期值也一样,就是线程安全的,反之则是线程不安全。 引发线程安全问题的根本原因:多个线程共享变量 如果多个线程对共享变量只有读操作,无写操作,那么此操作是线程安全的 如果多个线程同时执行共享变量的写和读操作,则操作不是线程安全的 解决线程安全问题,Java给出了各种办法 同步机制Synchronized Volatile关键字:内存屏障 原子类:CAS 锁:AQS 并发容器 线程并发三大特性 原子性 分时复用引起:操作系统增加了进程、线程,以分时复用CPU,进而均衡CPU与I/O设备的速度差异导致。 原子性:一个系列指令代码,要么全执行,要么都不执行,执行过程不能被打断 1 2 3 4 x = 10; //语句1: 直接将数值10赋值给x,也就是说线程执行这个语句的会直接将数值10写入到工作内存中 y = x; //语句2: 包含2个操作,它先要去读取x的值,再将x的值写入工作内存,虽然读取x的值以及 将x的值写入工作内存 这2个操作都是原子性操作,但是合起来就不是原子性操作了。 x++; //语句3: x++包括3个操作:读取x的值,进行加1操作,写入新的值。 x = x + 1; //语句4: 同语句3 可见性 为什么会出现不可见问题呢?因为Java内存模型(JMM), CPU缓存引起:CPU增加了缓存,以均衡与内存的速度差异导致。 当多个线程访问同一个变量时,一个线程修改了共享变量的值,其他线程能够立即看到 有序性 重排序引起:由于编译程序指令重排序优化指令执行次序,使得缓存能够得到更加合理地利用导致。 有序性:程序代码按照先后顺序执行 要想多线程程序正确地执行,必须要保证原子性、可见性以及有序性。只要有一个没有被保证,就有可能会导致程序运行不正确。

HashMap详解

实现步骤 HashMap基于哈希散列表,数组+链表/红黑树实现。 通过key的hashCode()方法计算出hashCode。 通过HashMap类中内部hash()方法将第2步中hashCode带入得出hash值。 通过第3步中hash值和HashMap中数组长度做&(位运算)得出在数组中的位置。 当第4步中位置中没有值则直接放入。 当第4步中位置中有值即产生hash冲突问题,此时通过链表(拉链法)来解决hash冲突问题。 如果第6步中第链表大小超过阈值(TREEIFY_THRESHOLD,8),链表转换为红黑树。 在转换为红黑树时,会判断数组长度大于64才转换,否则继续采用扩容策略而不转换。 关键特性 默认初始容量值为16,负载因子为0.75,当size>=threshold( threshold等于“容量负载因子”)时,会发生扩容:newsize = oldsize2,size一定为2的n次幂 hash冲突默认采用单链表存储,当单链表节点个数大于8时且数组长度大于64,会转化为红黑树存储, 当红黑树中节点少于6时,则转化为单链表存储。 扩容针对整个Map,每次扩容时,原来数组中的元素依次重新计算存放位置,并重新插入 当Map中元素总数超过Entry数组的75%,触发扩容操作,为了减少链表长度,元素分配更均匀 HashMap在1.7和1.8之间的变化: 1.7中是先扩容后插入新值的,1.8中是先插值再扩容 1.7中采用数组+链表,1.8采用的是数组+链表/红黑树,即在1.7中链表长度超过一定长度后就改成红黑树存储。 1.7扩容时需要重新计算哈希值和索引位置,1.8并不重新计算哈希值,巧妙地采用和扩容后容量进行&操作来计算新的索引位置。 1.7是采用表头插入法插入链表,1.8采用的是尾部插入法。 在1.7中采用表头插入法,在扩容时会改变链表中元素原本的顺序,以至于在并发场景下导致链表成环的问题;在1.8中采用尾部插入法,在扩容时会保持链表元素原本的顺序,就不会出现链表成环的问题了。 方法(JDK1.8-数组+链表/红黑树) 确定哈希桶数组索引位置 第1步计算hash 在JDK1.8的实现中,优化了高位运算的算法,通过hashCode()的高16位异或低16位实现的:(h = k.hashCode()) ^ (h »> 16),主要是从速度、功效、质量来考虑的。 目的都是在数组很小也能降低hash碰撞。 1 2 3 4 5 6 7 static final int hash(Object key) { int h; // key.hashCode():返回散列值也就是hashcode // ^ :按位异或 // >>>:无符号右移,忽略符号位,空位都以0补齐 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } 第2步计算数组位置

ArrayList详解

ArrayList 简介 ArrayList 的底层是数组队列,相当于动态数组。与 Java 中的数组相比,它的容量能动态增长。在添加大量元素前,应用程序可以使用ensureCapacity操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。 ArrayList 核心源码解读 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 package java.

Java容器概览

概览 容器主要包括Collection和Map 两种,Collection是存储着对象的集合,而Map存储着键值对(两个对象)的映射表。 Collection List 对付顺序的好帮手: 存储的元素是有序的、可重复的。 ArrayList:基于动态数组实现,支持随机访问,适用于频繁的查找工作。 Vector:和ArrayList类似,但它是线程安全的。 LinkedList:基于双向链表实现,只能顺序访问,但是可以快速地在链表中间插入和删除元素。不仅如此,LinkedList还可以用作栈、队列和双向队列。 Arraylist与 LinkedList 区别? 是否保证线程安全: ArrayList和LinkedList都是不同步的,也就是不保证线程安全; 底层数据结构: Arraylist底层使用的是Object数组;LinkedList底层使用的是双向链表数据结构(JDK1.6 之前为循环链表,JDK1.7 取消了循环。) 插入和删除是否受元素位置的影响: ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。 比如:执行add(E e)方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是 O(1)。但是如果要在指定位置 i 插入和删除元素的话(add(int index, E element))时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。 LinkedList 采用链表存储,所以对于add(E e)方法的插入,删除元素时间复杂度不受元素位置的影响,近似 O(1),如果是要在指定位置 i 插入和删除元素的话((add(int index, E element)) 时间复杂度近似为 O(n) ,因为需要先移动到指定位置再插入。 是否支持快速随机访问: LinkedList 不支持高效的随机元素访问,而 ArrayList 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index)方法)。 内存空间占用: ArrayList的空间浪费主要体现在在list列表的结尾会预留一定的容量空间,而 LinkedList 的空间花费则体现在它的每一个元素都需要消耗比 ArrayList 更多的空间(因为要存放直接后继和直接前驱以及数据)。 Set 注重独一无二的性质: 存储的元素是无序的、不可重复的。 HashSet:基于哈希表实现,支持快速查找,但不支持有序性操作。基于 HashMap 实现的,底层采用 HashMap 来保存元素。 LinkedHashSet:具有 HashSet 的查找效率,并且内部使用双向链表维护元素的插入顺序。LinkedHashSet 是 HashSet 的子类,并且其内部是通过 LinkedHashMap 来实现的。 TreeSet:基于红黑树实现((自平衡的排序二叉树)),支持有序性操作,例如根据一个范围查找元素的操作。查找效率不如HashSet,HashSet 查找的时间复杂度为 O(1),TreeSet 则为 O(logN)。 HashSet 如何检查重复 当你把对象加入HashSet时,HashSet 会先计算对象的hashcode值来判断对象加入的位置,同时也会与其他加入的对象的 hashcode 值作比较,如果没有相符的 hashcode,HashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让加入操作成功。

Java IO知识体系详解

IO大纲 IO装饰者模式 以 InputStream 为例 InputStream 是抽象组件; FileInputStream 是 InputStream 的子类,属于具体组件,提供了字节流的输入操作; FilterInputStream 属于抽象装饰者,装饰者用于装饰组件,为组件提供额外的功能。例如 BufferedInputStream 为 FileInputStream 提供缓存的功能。 实例化一个具有缓存功能的字节流对象时,只需要在 FileInputStream 对象上再套一层 BufferedInputStream 对象即可。 1 2 FileInputStream fileInputStream = new FileInputStream(filePath); BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream); IO常见类的使用 磁盘操作 File 类可以用于表示文件和目录的信息,但是它不表示文件的内容。 1 2 3 4 5 6 7 8 9 10 11 12 13 //递归地列出一个目录下所有文件: public static void listAllFiles(File dir) { if (dir == null || !dir.exists()) { return; } if (dir.isFile()) { System.

框架的灵魂-反射

什么是反射 简而言之,通过反射,我们可以在运行时获得程序中每一个类型的成员和成员的信息。 程序中一般的对象的类型都是在编译期就确定下来的,而 Java 反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。 所以我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。 反射的核心是 JVM 在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。 Java 反射主要提供以下功能 在运行时构造任意一个类的对象。 在运行时调用任意一个对象的方法。 在运行时判断任意一个对象所属的类。 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法)。 反射的主要用途 反射最重要的用途就是开发各种通用框架。 很多框架(比如 Spring)都是配置化的(比如通过 XML 文件配置 Bean),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射,运行时动态加载需要加载的对象。 举一个例子,在运用 Struts 2 框架的开发中我们一般会在 struts.xml 里去配置 Action,比如: 1 2 3 4 <action name="login" class="org.ScZyhSoft.test.action.SimpleLoginAction" method="execute"> <result>/shop/shop-index.jsp</result> <result name="error">login.jsp</result> </action> 配置文件与 Action 建立了一种映射关系,当 View 层发出请求时,请求会被 StrutsPrepareAndExecuteFilter 拦截,然后 StrutsPrepareAndExecuteFilter 会去动态地创建 Action 实例。比如我们请求 login.action,那么 StrutsPrepareAndExecuteFilter就会去解析struts.xml文件,检索action中name为login的Action,并根据class属性创建SimpleLoginAction实例,并用invoke方法来调用execute方法,这个过程离不开反射。 对与框架开发人员来说,反射虽小但作用非常大,它是各种容器实现的核心。而对于一般的开发者来说,不深入框架开发则用反射用的就会少一点,不过了解一下框架的底层机制有助于丰富自己的编程思想,也是很有益的。 像Java中的一大利器注解的实现也用到了反射。 为什么你使用 Spring 的时候 ,一个@Component注解就声明了一个类为 Spring Bean 呢?为什么你通过一个 @Value注解就读取到配置文件中的值呢?究竟是怎么起作用的呢? 这些都是因为你可以基于反射分析类,然后获取到类/属性/方法/方法的参数上的注解。你获取到注解之后,就可以做进一步的处理。 反射的基本运用 获得Class对象 使用Class类的forName 静态方法。 1 Class appleClass = Class.

Java特性-泛型

泛型,即参数化类型。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。 那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(类型形参),然后在使用/调用时传入具体的类型(类型实参)。 Java 语言中引入泛型是一个较大的功能增强。不仅语言、类型系统和编译器有了较大的变化,而且类库也进行了大翻修,所以许多重要的类,比如集合框架,都已经成为泛型化的了。这带来了很多好处: 类型安全。 泛型的主要目标是提高 Java 程序的类型安全。通过知道使用泛型定义的变量的类型限制,编译器可以在一个高得多的程度上验证类型假设。 消除强制类型转换。 泛型的一个附带好处是,消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会。 潜在的性能收益。 泛型为较大的优化带来可能。在泛型的初始实现中,编译器将强制类型转换(没有泛型的话,程序员会指定这些强制类型转换)插入生成的字节码中。 注意泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。 常用命名类型参数 K:键,比如映射的键 V:值,比如 List 和 Set 的内容,或者 Map 中的值 E:元素 T:泛型 ?:表示不确定的 java 类型 通配符 Ingeter 是 Number 的一个子类,同时 Generic 与 Generic 实际上是相同的一种基本类型。那么问题来了,在使用 Generic 作为形参的方法中,能否使用Generic 的实例传入呢?在逻辑上类似于 Generic 和 Generic 是否可以看成具有父子关系的泛型类型呢?下面我们通过定义一个方法来验证。 1 2 3 public void show(Generic<Number> obj) { System.out.println("key value is " + obj.getKey()); } 进行如下的调用: 1 2 3 Generic<Integer> genericInteger = new Generic<Integer>(123); show(genericInteger); //error Generic<java.lang.Integer> cannot be applied to Generic<java.

Java世界的入场券-面向对象

面向对象程序设计(英语:Object-oriented programming,缩写:OOP)是种具有对象概念的编程典范,同时也是一种程序开发的抽象方针。它可能包含数据、特性、代码与方法。对象则指的是类(class)的实例。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性,对象里的程序可以访问及经常修改对象相关连的数据。在面向对象程序编程里,计算机程序会被设计成彼此相关的对象。 面向对象就像是一张入场券,掌握了面向对象的思想,就可以在Java世界里尽情遨游,面向对象有三大法宝和七大戒律,并在其指导下萃取出了无数的锦囊妙计和绝世武器,下面我们揭开他们的神秘面纱。 OOP(面向对象编程)的三大法宝 封装 封装,也就是把客观事物封装成抽象的类,并且类可以把自己的属性和方法只让可信的类操作,对不可信的类进行信息隐藏。 继承 继承是指这样一种能力,它可以使用现有的类的所有功能,并在无需重新编写原来类的情况下对这些功能进行扩展。 多态 多态指一个类实例的相同方法在不同情形有不同的表现形式。具体来说就是不同实现类对公共接口有不同的实现方式,但这些操作可以通过相同的方式(公共接口)予以调用。 OOD(面向对象设计)七大戒律 开-闭原则 Open-Close Principle(OCP),即开-闭原则。开,指的是对扩展开放,即要支持方便地扩展;闭,指的是对修改关闭,即要严格限制对已有内容的修改。开-闭原则是最抽象也是最重要的OOD原则。简单工厂模式、工厂方法模式、抽象工厂模式中都提到了如何通过良好的设计遵循开-闭原则。 里氏替换原则 Liskov Substitution Principle(LSP),即里氏替换原则。该原则规定“子类必须能够替换其父类,否则不应当设计为其子类”。换句话说,父类出现的地方,都应该能由其子类代替。所以,子类只能去扩展基类,而不是隐藏或者覆盖基类。 依赖倒置原则 Dependence Inversion Principle(DIP),依赖倒置原则。它讲的是“设计和实现要依赖于抽象而非具体”。一方面抽象化更符合人的思维习惯;另一方面,根据里氏替换原则,可以很容易将原来的抽象替换为扩展后的具体,这样可以很好的支持开-闭原则。 接口隔离原则 Interface Segration Principle(ISP),接口隔离原则,“将大的接口打散成多个小的独立的接口”。由于Java类支持实现多个接口,可以很容易的让类具有多种接口的特征,同时每个类可以选择性地只实现目标接口。 单一职责原则 Single Responsibility Principle(SRP),单一职责原则。它讲的是,不要存在多于一个导致类变更的原因,是高内聚低耦合的一个体现。 迪米特法则/最少知道原则 Law of Demeter or Least Knowledge Principle(LoD or LKP),迪米特法则或最少知道原则。它讲的是“一个对象就尽可能少的去了解其它对象”,从而实现松耦合。如果一个类的职责过多,由于多个职责耦合在了一起,任何一个职责的变更都可能引起其它职责的问题,严重影响了代码的可维护性和可重用性。 合成/聚合复用原则 Composite/Aggregate Reuse Principle(CARP / CRP),合成/聚合复用原则。如果新对象的某些功能在别的已经创建好的对象里面已经实现,那么应当尽量使用别的对象提供的功能,使之成为新对象的一部分,而不要再重新创建。新对象可通过向这些对象的委派达到复用已有功能的效果。简而言之,要尽量使用合成/聚合,而非使用继承。《Java设计模式(九) 桥接模式》中介绍的桥接模式即是对这一原则的典型应用。 行走江湖的锦囊妙计和绝世武器 上面的法宝和戒律是心法,真正行走江湖还需要趁手的兵器和锦囊妙计。 而设计模式就是应用三大法宝和七大戒律下经过反复实践铸造出来锦囊妙计和武器,具体有哪些武器我们暂且不表,毕竟倚天屠龙出世,江湖必将血雨腥风,在这之前我们还需要做好准备工作。

Java位运算

一切的起源:二进制 位:二进制位,简称“位”。是二进制记数系统中表示小于2的整数的符号,一般用1或 0表示,是具有相等概率的两种状态中的一种。二进制位的位数可表示一个机器字的字长,一个二进制位包含的信息量称为一比特(bit)。 1 2 3 4 5 举个栗子: int占4个字节(byte) 1byte = 8bit 换算下来,一个int类型即占32bit int i = 88; 这里的88为十进制,转换为二进制为:1011000,使用完整的32位表示即为:00000000 00000000 00000000 01011000 上文中的00000000 00000000 00000000 01011000即为十进制88转为二进制的 原码 ,与其相关的定义还有 反码 和 补码 关于原码、反码和补码 在计算机内,有符号数有三种表示法:原码、反码以及补码。 原码:就是二进制定点表示法,即最高位为符号位,“0”正负“1”,其余位表示数值的大小。 反码:正数的反码与其原码相同;负数的反码是对正数逐位取反,符号位保持为1。 补码:正数的补码与其原码相同;负数的补码是在其反码的末位加1。 为什么要使用补码 简单来说,就是计算机计算减法时有各种不方便,于是发明了反码,结果发现反码也有缺陷(有两个零存在:“+0”和“-0”),进而发明了补码解决这个问题。 在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理。此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。 有关补码的意义及作用在上面的链接里讨论的非常详尽,我这里就不班门弄斧了,理解就好~ 对原码、反码以及补码有一个初步的认知后,我们接下来再看位运算就会清晰很多。 位运算符的基本运算 操作符 描述 例子(A = 8, B = 9) 按位与& 如果相对应位都是1,则结果为1,否则为0 A&B=8,即1000 按位或 | 如果相对应位都是0,则结果为0,否则为1 A B=9,即1001 按位异或^ 如果相对应位值相同,则结果为0,否则为1 A^B=1,即0001 按位取反~ 按位取反运算符翻转操作数的每一位,即0变成1,1变成0 ~A=7,即0111 左移 « 按位左移运算符。左操作数按位左移右操作数指定的位数 A « 2 = 32,即1000 00 右移 » 按位右移运算符。左操作数按位右移右操作数指定的位数 A » 2 = 2,即0010

MySQL常用命令

登录 mysql -u root -p 远程登录 修改表 1 2 3 1. select user,host from user; 2. update user set host = '%' where user = 'root'; 3. flush privileges; 权限方法 1 2 1. GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '您的数据库密码' WITH GRANT OPTION; 2. flush privileges; 查看日志相关信息 show variables like ’log_%'; 慢查询日志:slow query log show global status like ‘%Slow_queries%’; show variables like ‘%slow_query%’; show variables like ’long_query_time%’;