一、什么是线程

  一个应用就是一个进程、一个进程由多个线程组成。一个生产车间比作是一个进程、工人比作是线程。当任务比较多的时候,增加工人可以提高效率,同时成本就是支付费用(机器资源,内存)也会增加。

  1. package com.study.demo;
  2.  
  3. import java.lang.management.ManagementFactory;
  4. import java.lang.management.ThreadInfo;
  5. import java.lang.management.ThreadMXBean;
  6.  
  7. public class ThreadTest {
  8. public static void main(String[] args) {
  9. //java虚拟机的线程管理接口
  10. ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
  11.  
  12. //获取线程信息的方法
  13. ThreadInfo[] threadInfos =
  14. threadMXBean.dumpAllThreads(false,false);
  15. for(ThreadInfo threadInfo:threadInfos){
  16. System.out.println(threadInfo.getThreadId()+":"+threadInfo.getThreadName());
  17. }
  18. }
  19. }

输出:

5:Attach Listener  //获取内存dump,线程dump

4:Signal Dispatcher  //将信号分发给jvm的线程

3:Finalizer  //调用对象的finalizer 方法 进行垃圾回收

2:Reference Handler  //清除Reference

1:main //程序的主入口

为什么要用线程?

1、 充分利用CPU多处理核心;

2、 更快的响应时间

二、启动线程和退出线程

1、创建线程的方法

  1. extends Thread
  2. implements Runnable

启动线程:threadl类的start()

线程完成:

1、run()方法执行完成;

2、抛出一个未处理的异常导致线程的提前结束

  1. package com.study.demo;
  2.  
  3. public class CreateThread {
  4.  
  5. private class TestThread extends Thread{
  6.  
  7. @Override
  8. public void run(){
  9. System.out.println(" Thread");
  10. }
  11. }
  12.  
  13. private class TestRunnable implements Runnable{
  14.  
  15. @Override
  16. public void run() {
  17. System.out.println("Runnable");
  18. }
  19. }
  20.  
  21. private void test(){
  22. Thread t1 = new TestThread();
  23. Thread t2 = new Thread(new TestRunnable());
  24. t1.start();
  25. t2.start();
  26. }
  27.  
  28. public static void main(String[] args) {
  29. new CreateThread().test();
  30. }
  31.  
  32. }

2、取消和中断

  • 不安全的取消
  • 单独使用一个取消标志位
  1. package com.lgstudy.interrupt;
  2.  
  3. /**
  4. * lgs
  5. *
  6. * 使用自定义的取消标志位中断线程(不安全)
  7. */
  8. public class FlagCancel {
  9.  
  10. private static class TestRunable implements Runnable{
  11.  
  12. private volatile boolean on = true;
  13. private long i =0;
  14.  
  15. @Override
  16. public void run() {
  17. while(on){
  18. i++;
  19. //阻塞方法,on不起作用
  20. //wait,sleep,blockingqueue(put,take)
  21. try {
  22. Thread.sleep(20000);
  23. } catch (InterruptedException e) {
  24. e.printStackTrace();
  25. }
  26. }
  27. System.out.println("TestRunable is runing :"+i);
  28. }
  29.  
  30. public void cancel(){
  31. on = false;
  32. }
  33. }
  34.  
  35. }

Stop(),suspend(),resume()是过期的api,很大的副作用,容易导致死锁(suspend():将线程挂起不会释放锁)或者数据不一致(Stop():线程在未处理完数据时就停止)

3、如何安全的终止线程

使用线程的中断 :

interrupt() 中断线程,本质是将线程的中断标志位设为true,其他线程向需要中断的线程打个招呼。是否真正进行中断由线程自己决定。

isInterrupted() 线程检查自己的中断标志位

静态方法Thread.interrupted() 将中断标志位复位为false

由上面的中断机制可知Java里是没有抢占式任务,只有协作式任务。

为何要用中断,线程处于阻塞(如调用了java的sleep,wait等等方法时)的时候,是不会理会我们自己设置的取消标志位的,但是这些阻塞方法都会检查线程的中断标志位。

  1. package com.lgstudy.interrupt;
  2.  
  3. /**
  4. * lgs
  5. *
  6. * 安全的中断线程
  7. */
  8. public class SafeInterrupt implements Runnable {
  9.  
  10. private volatile boolean on = true;
  11. private long i =0;
  12.  
  13. @Override
  14. public void run() {
  15. //阻塞方法wait,sleep,blockingqueue(put,take),on不起作用
  16. //要加上线程的中断才能安全的终止线程
  17. while(on&&!Thread.currentThread().isInterrupted()){
  18. i++;
  19. }
  20. System.out.println("TestRunable is runing :"+i);
  21. }
  22.  
  23. public void cancel(){
  24. on = false;
  25. //在java线程很忙的时候可能不会理会中断,所以定义一个标志位on更好
  26. Thread.currentThread().interrupt();
  27. }
  28. }

4、处理不可中断的阻塞

IO通信 inputstream read/write等阻塞方法,不会理会中断,而关闭底层的套接字socket.close()会抛出socketException

NIO: selector.select()会阻塞,调用selector的wakeup和close方法会抛出ClosedSelectorException

死锁状态不响应中断的请求,这个必须重启程序,检查程序找到死锁的位置修改错误。

  1. package com.lgstudy.interrupt;
  2.  
  3. /**
  4. * lgs
  5. *
  6. * 调用阻塞方法时,如何中断线程
  7. */
  8. public class BlockInterrupt {
  9.  
  10. private static volatile boolean on = true;
  11.  
  12. private static class WhenBlock implements Runnable {
  13.  
  14. @Override
  15. public void run() {
  16. while (on && !Thread.currentThread().isInterrupted()) {
  17. try {
  18. //抛出中断异常的阻塞方法(wait,sleep,blockingqueue(put,take)),抛出异常后,中断标志位改成false
  19. Thread.sleep(100);
  20. } catch (InterruptedException e) {
  21. Thread.currentThread().interrupt();//重新设置一下
  22. //do my work
  23. }
  24. //清理工作结束线程
  25. }
  26. }
  27.  
  28. //外部线程调用方法阻塞
  29. public void cancel() {
  30. on = false;
  31. Thread.currentThread().interrupt();
  32. }
  33.  
  34. }
  35. }

如何让我们的代码既可以响应普通的中断,又可以关闭底层的套接字呢?

覆盖线程的interrupt方法,在处理套接字异常时,再用super.interrupt()自行中断线程。

  1. package com.lgstudy.interrupt;
  2.  
  3. import java.io.IOException;
  4. import java.io.InputStream;
  5. import java.net.Socket;
  6.  
  7. /**
  8. * lgs
  9. *
  10. * 如何覆盖线程的interrupt() 方法
  11. */
  12. public class OverrideInterrupt extends Thread {
  13. private final Socket socket;
  14. private final InputStream in;
  15.  
  16. public OverrideInterrupt(Socket socket, InputStream in) {
  17. this.socket = socket;
  18. this.in = in;
  19. }
  20.  
  21. private void t(){
  22. }
  23.  
  24. @Override
  25. public void interrupt() {
  26. try {
  27. //关闭底层的套接字
  28. socket.close();
  29. } catch (IOException e) {
  30. e.printStackTrace();
  31. //.....
  32. }finally {
  33. //同时中断线程
  34. super.interrupt();
  35. }
  36.  
  37. }
  38. }

5、线程的状态

  • 新创建   线程被创建,但是没有调用start方法
  • 可运行(RUNNABLE)  运行状态,由cpu决定是不是正在运行
  • 被阻塞(BLOCKING)  阻塞,线程被阻塞于锁
  • 等待/计时等待(WAITING) 等待某些条件成熟
  • 被终止  线程执行完毕

6、线程的优先级

成员变量priority控制优先级,范围1-10之间,数字越高优先级越高,缺省为5,创建线程时setPriotity()可以设置优先级,不要指望他发挥作用,因为线程优先级是由操作系统决定的,有的操作系统甚至会忽略jvm的线程优先级。

7、Daemon线程

守护型线程(如GC线程),程序里没有非Daemon线程时,java程序就会退出。一般用不上,也不建议我们平时开发时使用,因为Try/Finally里的代码不一定执行的。

  1. package com.lgstudy;
  2.  
  3. import com.lgstudy.threadstate.SleepUtils;
  4.  
  5. /**
  6. * lgs
  7. *
  8. * 守护线程
  9. */
  10. public class Daemon {
  11. public static void main(String[] args) {
  12. Thread thread = new Thread(new DaemonRunner());
  13. //将线程置为守护线程
  14. thread.setDaemon(true);
  15. thread.start();
  16. }
  17.  
  18. static class DaemonRunner implements Runnable {
  19. @Override
  20. public void run() {
  21. try {
  22. SleepUtils.second(100);
  23. } finally {
  24. System.out.println("DaemonThread finally run.");
  25. }
  26. }
  27. }
  28. }

三、常用方法深入理解

run()和start() 

run就是一个普通的方法,跟其他类的实例方法没有任何区别,他之所以能在线程里面运行时因为调用了start()方法。

Sleep

不会释放锁,当前线程变成了休眠状态,所以我们在用sleep时,要把sleep放在同步代码块的外面。

yield()

不会释放锁,当前线程出让cpu占有权,当前线程变成了可运行状态,下一时刻仍然可能被cpu选中。

wait()和 notify()/notiyfAll()

调用以前,当前线程必须要持有锁,调用了wait() notify()/notiyfAll()会释放锁。

等待通知机制:

线程 A调用了对象O的wait方法进入等待状态,线程 B调用了对象O的notify方法进行唤醒,唤醒的是在对象O上wait的线程(比如线程A)

notify() 唤醒一个线程,唤醒哪一个完全看cpu的心情(谨慎使用)

notiyfAll() 所有在对象O上wait的线程全部唤醒(应该用notiyfAll())

join方法

线程A,执行了thread.join(),线程A等待thread线程终止了以后,A在join后面的语句才会继续执行

  1. package com.lgstudy;
  2.  
  3. /**
  4. * join的使用
  5. */
  6. public class JoinTest {
  7.  
  8. static class CutInLine implements Runnable{
  9.  
  10. private Thread thread;
  11.  
  12. public CutInLine(Thread thread) {
  13. this.thread = thread;
  14. }
  15.  
  16. @Override
  17. public void run() {
  18. try {
  19. //在被插队的线程里,调用一下插队线程的join方法
  20. thread.join();
  21. } catch (InterruptedException e) {
  22. e.printStackTrace();
  23. }
  24. System.out.println(Thread.currentThread().getName()+" will work");
  25. }
  26. }
  27.  
  28. public static void main(String[] args) {
  29. Thread previous = Thread.currentThread();
  30. for(int i=0;i<10;i++){
  31. Thread thread =
  32. new Thread(new CutInLine(previous),String.valueOf(i));
  33. System.out.println(previous.getId()+" cut in the thread:"+thread.getName());
  34. thread.start();
  35. previous = thread;
  36. }
  37.  
  38. }
  39.  
  40. }

四、线程间协作和通信

每个线程有自己栈空间,孤立运行,对我们没有价值。如果多个线程能够相互配合完成工作,这将会带来巨大的价值。

1、volatile和synchronized

多个线程同时访问一个共享的变量的时候,每个线程的工作内存有这个变量的一个拷贝,变量本身还是保存在共享内存中。

volatile修饰字段,对这个变量的访问必须要从共享内存刷新一次。最新的修改写回共享内存。可以保证字段的可见性。绝对不是线程安全的,没有操作的原子性。

适用场景:

1、一个线程写,多个线程读;

2、volatile变量的变化很固定即变化以后都是一个固定的值。

  1. package com.lgstudy.volatiletest;
  2.  
  3. /**
  4. *
  5. * 测试Volatile型变量的操作原子性
  6. */
  7. public class VolatileThread implements Runnable {
  8.  
  9. private volatile int a= 0;
  10.  
  11. @Override
  12. public void run() {
  13. synchronized (this){
  14. a=a+1;
  15. System.out.println(Thread.currentThread().getName()+"----"+a);
  16. try {
  17. Thread.sleep(100);
  18. } catch (InterruptedException e) {
  19. e.printStackTrace();
  20. }
  21. a=a+1;
  22. System.out.println(Thread.currentThread().getName()+"----"+a);
  23.  
  24. }
  25. }
  26. }
  1. package com.lgstudy.volatiletest;
  2.  
  3. public class VolatileTest {
  4. public static void main(String[] args) {
  5. VolatileThread volatileThread = new VolatileThread();
  6.  
  7. Thread t1 = new Thread(volatileThread);
  8. Thread t2 = new Thread(volatileThread);
  9. Thread t3 = new Thread(volatileThread);
  10. Thread t4 = new Thread(volatileThread);
  11. t1.start();
  12. t2.start();
  13. t3.start();
  14. t4.start();
  15. }
  16. }

输出:

Thread-0----1
Thread-0----2
Thread-3----3
Thread-3----4
Thread-2----5
Thread-2----6
Thread-1----7
Thread-1----8

关键字synchronized可以修饰方法或者以同步块的形式来进行使用,它主要确保多个线程在同一个时刻,只能有一个线程处于方法或者同步块中,它保证了线程对变量访问的可见性和排他性,又称为内置锁机制。

Synchronized的类锁和对象锁,本质上是两把锁,类锁实际锁的是每一个类的class对象。对象锁锁的是当前对象实例。

  1. package com.lgstudy.syn;
  2.  
  3. import com.lgstudy.threadstate.SleepUtils;
  4.  
  5. /**
  6. * 类锁和实例锁
  7. */
  8. public class InstanceAndClass {
  9.  
  10. //测试类锁
  11. private static class TestClassSyn extends Thread{
  12. @Override
  13. public void run() {
  14. System.out.println("TestClass is going...");
  15. synClass();
  16. }
  17. }
  18.  
  19. //测试对象锁
  20. private static class TestInstanceSyn extends Thread{
  21. private InstanceAndClass instanceAndClass;
  22.  
  23. public TestInstanceSyn(InstanceAndClass instanceAndClass) {
  24. this.instanceAndClass = instanceAndClass;
  25. }
  26.  
  27. @Override
  28. public void run() {
  29. System.out.println("TestInstance is going..."+instanceAndClass);
  30. instanceAndClass.synInstance();
  31. }
  32.  
  33. }
  34.  
  35. //测试对象锁
  36. private static class TestInstance2Syn implements Runnable{
  37. private InstanceAndClass instanceAndClass;
  38.  
  39. public TestInstance2Syn(InstanceAndClass instanceAndClass) {
  40. this.instanceAndClass = instanceAndClass;
  41. }
  42. @Override
  43. public void run() {
  44. System.out.println("TestInstance2 is going..."+instanceAndClass);
  45. instanceAndClass.synInstance2();
  46. }
  47. }
  48.  
  49. //锁对象的方法
  50. private synchronized void synInstance(){
  51. SleepUtils.second(3);
  52. System.out.println("synInstance is going...");
  53. SleepUtils.second(3);
  54. System.out.println("synInstance ended");
  55. }
  56.  
  57. //锁对象的方法
  58. private synchronized void synInstance2(){
  59. SleepUtils.second(3);
  60. System.out.println("synInstance2 going...");
  61. SleepUtils.second(3);
  62. System.out.println("synInstance2 ended");
  63. }
  64.  
  65. //锁类的方法
  66. private static synchronized void synClass(){
  67. SleepUtils.second(1);
  68. System.out.println("synClass going...");
  69. SleepUtils.second(1);
  70. }
  71.  
  72. public static void main(String[] args) {
  73. InstanceAndClass instanceAndClass = new InstanceAndClass();
  74. Thread t1 = new TestClassSyn();
  75. Thread t2 = new Thread(new TestInstanceSyn(instanceAndClass));
  76. Thread t3 = new Thread(new TestInstance2Syn(instanceAndClass));
  77. t2.start();
  78. t3.start();
  79. SleepUtils.second(1);
  80. t1.start();
  81. }
  82.  
  83. }

2、等待和通知机制

等待方原则:

1、获取对象锁

2、如果条件不满足,调用对象的wait方法,被通知后依然要检查条件是否满足

3、条件满足以后,才能执行相关的业务逻辑

Synchronized(对象){

While(条件不满足){

对象.wait()

}

业务逻辑处理

}

通知方原则:

1、 获得对象的锁;

2、 改变条件;

3、 通知所有等待在对象的线程

Synchronized(对象){

业务逻辑处理,改变条件

对象.notify/notifyAll

}

  1. package com.lgstudy.bq;
  2.  
  3. import java.util.LinkedList;
  4. import java.util.List;
  5.  
  6. /**
  7. *
  8. * 有界阻塞队列/有界缓存队列
  9. */
  10. public class BlockingQueueWN<T> {
  11.  
  12. //当前队列
  13. private List queue = new LinkedList<>();
  14. //队列支持的最大容量
  15. private final int limit;
  16.  
  17. //外部修改队列的容量
  18. public BlockingQueueWN(int limit) {
  19. this.limit = limit;
  20. }
  21.  
  22. //入队
  23. public synchronized void enqueue(T item) throws InterruptedException {
  24. //如果当前队列的容量已经满了的话就要等待
  25. while(this.queue.size()==this.limit){
  26. wait();
  27. }
  28. //如果当前队列的容量为0的话,可以肯定有出队的线程正在等待,需要他可以准备出队了
  29. if (this.queue.size()==0){
  30. notifyAll();
  31. }
  32. //开始入队
  33. this.queue.add(item);
  34. }
  35.  
  36. //出队
  37. public synchronized T dequeue() throws InterruptedException {
  38. //如果当前队列的容量为0的话就等待暂不出队
  39. while(this.queue.size()==0){
  40. wait();
  41. }
  42. //如果当前队列的容量已经满了的话,可以肯定有入队线程正在等待,需要唤醒他可以准备入队了
  43. if (this.queue.size()==this.limit){
  44. notifyAll();
  45. }
  46. //开始出队
  47. return (T)this.queue.remove(0);
  48. }
  49. }

输出;

Pop will pop.....
i=5 will push
i=5 alread pop
Pop will pop.....
i=4 will push
i=4 alread pop
Pop will pop.....
i=3 will push
i=3 alread pop
Pop will pop.....
i=2 will push
i=2 alread pop
Pop will pop.....
i=1 will push
i=1 alread pop
Pop will pop.....

管道输入输出流 使用较少

管道输入输出流用于线程中间的数据传递,传输媒介是内存

PpedOutputStream/PpedInputStream 面向的字节

PipedReader/PipedWriter 面向的是字符

只适合线程间一对一的通信,适用范围较狭窄。

ThreadLocal

本质是个map,map的键就是每个线程对象,值就是每个线程所拥有的值

常用方法:

initialValue()

get()

set()

remove():将当前线程局部变量的值删除,这个方法是JDK 5.0新增的方法。当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。

ThreadLocal拥有的这个变量,在线程之间很独立的,相互之间没有联系。内存占用相对来说比较大。

性能问题

串行化、无锁化、异步化编程是趋势之一,比如node.js,Vert.x。

黄金原则:编码时候不要考虑性能优化的事情,先正确实现业务,发现性能不行,这个时候再来考虑性能优化。

等待超时模式

调用场景:调用一个方法时等待一段时间(一般来说是给定一个时间段),如果该方法能够在给定的时间段之内得到结果,那么将结果立刻返回,反之,超时返回默认结果。

假设等待时间段是T,那么可以推断出在当前时间now+T之后就会超时

等待持续时间:REMAINING=T。

·超时时间:FUTURE=now+T。

  1. public synchronized Object get(long mills) throws InterruptedException {
  2. long future = System.currentTimeMillis() + mills;
  3. long remaining = mills;
  4. // 当超时大于0并且result返回值不满足要求
  5. while ((result == null) && remaining > 0) {
  6. wait(remaining);
  7. remaining = future - System.currentTimeMillis();
  8. }
  9. return result;
  10. }

java并发编程系列一、多线程的更多相关文章

  1. 干货:Java并发编程系列之volatile(二)

    接上一篇<Java并发编程系列之synchronized(一)>,这是第二篇,说的是关于并发编程的volatile元素. Java语言规范第三版中对volatile的定义如下:Java编程 ...

  2. Java并发编程系列-(5) Java并发容器

    5 并发容器 5.1 Hashtable.HashMap.TreeMap.HashSet.LinkedHashMap 在介绍并发容器之前,先分析下普通的容器,以及相应的实现,方便后续的对比. Hash ...

  3. Java并发编程系列-(2) 线程的并发工具类

    2.线程的并发工具类 2.1 Fork-Join JDK 7中引入了fork-join框架,专门来解决计算密集型的任务.可以将一个大任务,拆分成若干个小任务,如下图所示: Fork-Join框架利用了 ...

  4. Java并发编程系列-(1) 并发编程基础

    1.并发编程基础 1.1 基本概念 CPU核心与线程数关系 Java中通过多线程的手段来实现并发,对于单处理器机器上来讲,宏观上的多线程并行执行是通过CPU的调度来实现的,微观上CPU在某个时刻只会运 ...

  5. Java并发编程系列-(7) Java线程安全

    7. 线程安全 7.1 线程安全的定义 如果多线程下使用这个类,不过多线程如何使用和调度这个类,这个类总是表示出正确的行为,这个类就是线程安全的. 类的线程安全表现为: 操作的原子性 内存的可见性 不 ...

  6. Java并发编程系列-(8) JMM和底层实现原理

    8. JMM和底层实现原理 8.1 线程间的通信与同步 线程之间的通信 线程的通信是指线程之间以何种机制来交换信息.在编程中,线程之间的通信机制有两种,共享内存和消息传递. 在共享内存的并发模型里,线 ...

  7. Java并发编程系列-(9) JDK 8/9/10中的并发

    9.1 CompletableFuture CompletableFuture是JDK 8中引入的工具类,实现了Future接口,对以往的FutureTask的功能进行了增强. 手动设置完成状态 Co ...

  8. java并发编程系列原理篇--JDK中的通信工具类Semaphore

    前言 java多线程之间进行通信时,JDK主要提供了以下几种通信工具类.主要有Semaphore.CountDownLatch.CyclicBarrier.exchanger.Phaser这几个通讯类 ...

  9. Java并发编程系列-(4) 显式锁与AQS

    4 显示锁和AQS 4.1 Lock接口 核心方法 Java在java.util.concurrent.locks包中提供了一系列的显示锁类,其中最基础的就是Lock接口,该接口提供了几个常见的锁相关 ...

  10. Java并发编程系列-(3) 原子操作与CAS

    3. 原子操作与CAS 3.1 原子操作 所谓原子操作是指不会被线程调度机制打断的操作:这种操作一旦开始,就一直运行到结束,中间不会有任何context switch,也就是切换到另一个线程. 为了实 ...

随机推荐

  1. 【Code Chef】April Challenge 2019

    Subtree Removal 很显然不可能选择砍掉一对有祖先关系的子树.令$f_i$表示$i$子树的答案,如果$i$不被砍,那就是$a_i + \sum\limits_j f_j$:如果$i$被砍, ...

  2. 洛谷 P2300 合并神犇 解题报告

    P2300 合并神犇 题目背景 loidc来到了NOI的赛场上,他在那里看到了好多神犇. 题目描述 神犇们现在正排成一排在刷题.每个神犇都有一个能力值p[i].loidc认为坐在附近的金牌爷能力参差不 ...

  3. 分数拆分(Fractions Again?!, UVa 10976)

    题目链接:https://vjudge.net/problem/UVA-10976 It is easy to see that for every fraction in the form 1k(k ...

  4. linux动态库与静态库混合连接

      1, 在应用程序需要连接外部库的情况下,linux默认对库的连接是使用动态库,在找不到动态库的情况下再选择静态库.使用方式为: gcc test.cpp -L. -ltestlib 如果当前目录有 ...

  5. P1382 楼房 set用法小结

    这个sb题目,剧毒... STL大法好 首先,我准备用经典的线段树优化扫描线来做.之前的矩形周长把我困了数天导致我胸有成竹. 然后,敲代码半小时,调试半个月......这个,sb,怎么改都是0分+2个 ...

  6. java 线程栈 & java.lang.StackOverflowError

    网上搜索了一下,关于java的线程栈: JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K. JVM的内存,被划分了很多的区域: (来源:http://www.iteye.com/ ...

  7. MATLAB:图像的移动(move函数)

    图像移动涉及到move函数,实现过程如下: close all; %关闭当前所有图形窗口,清空工作空间变量,清除工作空间所有变量 clear all; clc; I=imread('lenna.bmp ...

  8. 将make的输出重定向到文件(转)

    原文:将make的输出重定向到文件 [用法] 1.想要把make输出的全部信息,输出到某个文件中,最常见的办法就是:make xxx > build_output.txt此时默认情况是没有改变2 ...

  9. linux chkconfig添加开机启动服务

    --add:增加所指定的系统服务,让chkconfig指令得以管理它,并同时在系统启动的叙述文件内增加相关数据: --del:删除所指定的系统服务,不再由chkconfig指令管理,并同时在系统启动的 ...

  10. DBA记录-数据库管理员需要掌握的内容

    1.Linux 2.ORACLE/MySQL/SQLSERVER 3.NOSQL 4.环境部署.用户及权限管理.表空间.表.视图.索引.过程.触发器.分区.函数.查询.性能调优.迁移备份.集群.日志分 ...