------- android培训java培训、期待与您交流! ----------

一、多线程的概念

  进程和线程经常会被人混淆,那是因为对它们的概念不明确。就拿我们平时使用的操作系统来说,它是多任务的操作系统,而多线程就是实现多任务的一种方式。
  进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程。比如在Windows系统中,一个运行的exe就是一个进程。
   线程是指进程中的一个执行流程,一个进程中可以运行多个线程。比如平时下载的软件迅雷进程中可以运行很多线程。线程总是属于某个进程,进程中的多个线程共享进程的内存。
   说起多线程,给很多人得印象就是“同时”执行。但是多线程在并发执行的时候,其实是在线程之间快速轮换执行的,只不过轮换的速度很快,所以有“同时”执行的效果。

二、多线程的优缺点

  优点:
  (1)资料利用率更好。
  (2)部分程序设计更加简单。
  (3)程序响应更快。
  (4)简化异步时间的处理(服务器)。

  缺点:
  (1)部分程序设计更复杂。
  (2)增加cpu上下文切换开销。
  (3)增加资源消耗。

三、多线程的创建和使用

  在java中提供了对象线程这类事物的描述,该类是Thread。线程的常用创建和启动方式有两种:

  1.继承Thread类,子类覆盖父类中的run方法,把需要多线程执行的代码存放在run方法中。然后创建子类的对象同时线程也会被创建起来。最后通过子类调用start方法开启线程。

具体如下面代码:

  1. class Demo extends Thread {  //继承Thread类。
  2. public void run() {    //覆盖run方法。
  3. for(int x=0; x<60; x++)
  4. System.out.println("线程1----"+x);
  5. }
  6. }
  7.  
  8. public class ThreadDemo {
  9. public static void main(String[] args) {
  10. Demo d = new Demo();//创建好一个线程。
  11. d.start(); //开启线程并执行该线程的run方法。
  12. //d.run(); //注意,这里仅仅是对象调用方法。而线程创建了,并没有运行。
  13.  
  14. for(int x=0; x<60; x++)
  15. System.out.println("主线程--"+x);
  16. }
  17. }

  2.实现Runnable接口,子类覆盖接口中的run方法,把需要多线程执行的代码存放在run方法中。然后通过Thread创建线程,并将实现了Runnable接口的子类对象作为参数传递给Thread类的构造函数。最后Thread类对象调用Start方法开启线程。

具体如下代码:

  1. class Demo_2 implements Runnable {
  2. private int x = 100;
  3. public void run() {
  4. while(true) {
  5. if(x>0)
  6. // Thread.currentThread().getName()获取当前线程的名称。
  7. System.out.println(Thread.currentThread().getName()+"....x : "+ x--);
  8. }
  9. }
  10. }
  11.  
  12. public class ThreadDemo_2 {
  13. public static void main(String[] args) {
  14. Demo_2 d = new Demo_2();
  15.  
  16. Thread t1 = new Thread(d);//创建了一个线程;
  17. Thread t2 = new Thread(d);//创建了一个线程;
  18. Thread t3 = new Thread(d);//创建了一个线程;
  19. Thread t4 = new Thread(d);//创建了一个线程;
  20. t1.start();
  21. t2.start();
  22. t3.start();
  23. t4.start();
  24. }
  25. }

  以上两种创建方式的区别:
  (1)继承Thread:线程代码存放Thread子类run方法中。
  (2)实现Runnable:线程代码存在接口的子类的run方法,使用起来更加灵活,可以避免单继承局限性,因为java中的对象是单继承多实现的。

  另外通过以上的运行可以发现,运行结果每一次都不同,这是因为线程需要获取cpu的执行权时才能执行。在多个线程中,cpu执行到谁,谁就运行。我们可以把形象的把多个线程在抢夺cpu的资源,这也是多线程随机性的体现。需要明确的一点是,在某一个时刻,只能有一个线程在运行(多核除外)。

四、线程的状态转换

  下面是线程状态转换示意图:

  

  从上图可以看出,一个线程从执行到结束,会经历几种状态,下面对这几种状态进行解释。

  1.创建线程:也可以说叫new状态,就是把线程建立起来。
  2.阻塞状态:当线程start开启后,不一定马上进入运行状态,而是可能先进入阻塞状态,阻塞状态表示程序准备就绪,等待系统的调度。也可以说是具有运行权,但没有执行权。
  3.运行状态:线程接收到系统的调度,执行起来。也就是说线程具有执行权。
  4.冻结状态:当线程处于处于sleep,wait等状态时,即是冻结状态,表示线程还是活的,但是没有运行权。另外当一个线程休眠或者被唤醒结束时,不一定马上进入运行状态,而是可能先进入阻塞状态,等待系统调度,当被调度具有执行权时,再进入运行状态。
  5.消亡状态:就是线程结束。

  当我们对线程各种状态有了基本了解后,设计多线程时才能更加效率准确。

五、多线程的同步

  当多个线程间通讯,也就是多个线程的同时操作同一资源时,如果一个线程正在访问修改一些数据的同时,另一个线程也在修改这些数据,那么前一个线程的修改操作后就得到错误数据,造成安全隐患。

  解决办法:
  当有多条对共享数据进行操作的语句时,要让一个线程都执行完,而在执行过程中,其他线程不可以参与执行,也就是实现同步。

  实现同步的方法:
  java中对于实现同步提供了解决方法。

  1.synchronized同步代码块: 

  使用同步代码块的前提,是具有两个或以上的线程,而且多个线程必须使用同一把锁。当线程持有锁时可以在同步中执行,而没有持有锁的线程即使获取cpu的执行权,也无法执行同步代码块里的代码。同时,也因为同步的特性,当多个线程判断锁时,有较为消耗资源的缺点。以下是同步代码块的应用: 

  1. /*
  2. 多个窗口销售1000张票,票号不重复。
  3. */
  4. class Ticket implements Runnable {
  5. private int tick = 1000;
  6. Object obj = new Object();
  7. public void run() {
  8. while(true) {
  9. synchronized(obj) { //同步代码块,需要传递一个对象参数,可以把对象看成是锁。
  10. //此处两条语句操作tick,多个线程执行时,可能出现安全隐患,需要同步
  11. if(tick>0)
  12. System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
  13. }
  14. }
  15. }
  16. }
  17.  
  18. public class TicketDemo {
  19. public static void main(String[] args) {
  20.  
  21. Ticket t = new Ticket();
  22.  
  23. Thread t1 = new Thread(t);
  24. Thread t2 = new Thread(t);
  25. Thread t3 = new Thread(t);
  26. Thread t4 = new Thread(t);
  27. t1.start();
  28. t2.start();
  29. t3.start();
  30. t4.start();
  31. }
  32. }

从结果可以看出没有错票,实现同步。

  2.synchronized同步函数:

  synchronized同步函数和synchronized同步代码块的功能是一样的,不同的是在同步函数中,synchronized修饰的是函数。

  另外要注意,synchronized同步函数的锁对象有两种情况:
  (1)当synchronized修饰的函数不是静态函数时,那么synchronized对应的锁对象是this,即是调用方法的对象。看以下代码验证:

  1. /*
  2. 非静态同步函数用的是哪一个锁呢?
  3. 非静态函数需要被对象调用。那么函数都有一个所属对象引用。就是this。
  4. 所以非静态同步函数使用的锁是this。
  5.  
  6. 通过该程序进行验证。
  7. 使用两个线程来买票。
  8. 一个线程在同步代码块中。
  9. 一个线程在同步函数中。
  10. 都在执行买票动作。
  11. */
  12. class Ticket implements Runnable {
  13. private int tick = 1000;
  14. Object obj = new Object();
  15. boolean flag = true; //定义标记,不同标记执行不同的代码。
  16. public void run() {
  17. if(flag) {
  18. while(true) {
  19. synchronized(this) {
  20. if(tick>0) {
  21. //让线程停顿,方便验证结果。
  22. try{Thread.sleep(10);}catch(Exception e){}
  23. System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);
  24. }
  25. }
  26. }
  27. }
  28. else
  29. while(true)
  30. show();
  31. }
  32. public synchronized void show(){  //非静态同步代函数
  33. if(tick>0) {
  34. try{Thread.sleep(10);}catch(Exception e){}
  35. System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--);
  36. }
  37. }
  38. }
  39.  
  40. public class ThisLockDemo {
  41. public static void main(String[] args) {
  42. Ticket t = new Ticket();
  43.  
  44. Thread t1 = new Thread(t);
  45. Thread t2 = new Thread(t);
  46. t1.start();
  47. try{Thread.sleep(10);}catch(Exception e){} //这里让线程停顿,方便验证同步。
  48. t.flag = false; //通过修改标记,t2线程执行不同的语句。
  49. t2.start();
  50. }
  51. }

从结果可以看出没有错票,实现同步。

  (2)当synchronized修饰的函数是静态函数时,那么synchronized对应的锁对象是函数所在类的字节码对象(xxx.class),因为静态方法是属于类,不属于对象。看以下代码验证:

  1. /*
  2. 如果同步函数被静态修饰后,使用的锁是什么呢?
  3.  
  4. 通过验证,发现不在是this。因为静态方法中也不可以定义this。
  5. 静态进内存是,内存中没有本类对象,但是一定有该类对应的字节码文件对象。
  6.  
  7. 类名.class 该对象的类型是Class
  8. 静态的同步方法,使用的锁是该方法所在类的字节码文件对象。 类名.class
  9. */
  10. class Ticket implements Runnable {
  11. private static int tick = 1000;
  12. //Object obj = new Object();
  13. boolean flag = true;
  14. public void run() {
  15. if(flag) {
  16. while(true) {
  17. synchronized(Ticket.class) {
  18. if(tick>0) {
  19. try{Thread.sleep(10);}catch(Exception e){}
  20. System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);
  21. }
  22. }
  23. }
  24. }
  25. else
  26. while(true)
  27. show();
  28. }
  29. public static synchronized void show() {  //静态同步代函数
  30. if(tick>0) {
  31. try{Thread.sleep(10);}catch(Exception e){}
  32. System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--);
  33. }
  34. }
  35. }
  36.  
  37. class StaticMethodDemo {
  38. public static void main(String[] args) {
  39. Ticket t = new Ticket();
  40.  
  41. Thread t1 = new Thread(t);
  42. Thread t2 = new Thread(t);
  43. t1.start();
  44. try{Thread.sleep(10);}catch(Exception e){}
  45. t.flag = false;
  46. t2.start();
  47. }
  48. }

从结果可以看出没有错票,实现同步。

  在synchronized同步代码块和同步函数中,可以使用wait(),notify(),notifyAll()来控制线程,这三个方法的使用前提就是必须使用在同步中,而且使用它们的时候要标识它们所属的同步的锁,它们的作用是:
  ①wait():释放cpu执行权,释放锁。也就是说,当线程wait后就处于冻结状态。
  ②notify():唤醒第一个wait的线程。
  ③notify():唤醒全部等待的线程。

  使用这三种方法可以实现等待唤醒机制,以下是使用的实例代码:

  1. /*
  2. 等待唤醒机制。
  3. 也就是常见的生产者消费者问题,举个例子,生产者没生产一个商品,就要被消费者消费。
  4.  
  5. 1,当多个生产者消费者出现时。
  6. 需要让获取执行权的线程判断标记。
  7. 通过while完成。
  8.  
  9. 2,需要将对方的线程唤醒。
  10. 仅仅notify,是不可以的。
  11. 因为有可能出现只唤醒本方,导致所有线程都在等待。
  12. 所以要通过notifyAll形式来完成。
  13.  
  14. 对于多个生产者和消费者。
  15. 为什么要定义while判断标记。
  16. 原因:让被唤醒的线程再一次判断标记。
  17. */
  18.  
  19. class Resource {
  20. private String name;
  21. private int count = 1;
  22. private boolean flag = false;
  23.  
  24. public synchronized void set(String name) {
  25. while(flag)
  26. try{this.wait();}catch(Exception e){}  //当线程执行到这里会进入等待状态,释放锁。
  27. this.name = name+"--"+count++;
  28. //注意下面输出语句name前必须加this,否则会直接访问局部变量中的name。
  29. System.out.println(Thread.currentThread().getName()+"生产者:"+this.name);
  30. flag = true;
  31. this.notifyAll();  //唤醒所有等待的线程。
  32. }
  33.  
  34. public synchronized void out() {
  35. while(!flag)
  36. try{this.wait();}catch(Exception e){}
  37. System.out.println(Thread.currentThread().getName()+"消费者:--"+this.name);
  38. flag = false;
  39. this.notifyAll();
  40. }
  41. }
  42.  
  43. class Producer implements Runnable {
  44. private Resource r;
  45.  
  46. Producer(Resource r) {
  47. this.r = r;
  48. }
  49.  
  50. public void run() {
  51. while(true)
  52. r.set("+商品+");
  53. }
  54. }
  55.  
  56. class Consumer implements Runnable {
  57. private Resource r;
  58. Consumer(Resource r) {
  59. this.r = r;
  60. }
  61.  
  62. public void run() {
  63. while(true)
  64. r.out();
  65. }
  66. }
  67.  
  68. class ProducerConsumerDemo {
  69. public static void main(String[] args) {
  70. Resource r = new Resource();
  71.  
  72. Producer pro = new Producer(r);
  73. Consumer con = new Consumer(r);
  74.  
  75. Thread t1 = new Thread(pro);
  76. Thread t2 = new Thread(pro);
  77. Thread t3 = new Thread(con);
  78. Thread t4 = new Thread(con);
  79.  
  80. t1.start();
  81. t2.start();
  82. t3.start();
  83. t4.start();
  84. }
  85. }

运行结果是不停的生产和消费,但生产和消费的数目总是一样的。说明程序通过标记成功控制每生产一个商品就消费一个,并且实现了多线程的同步。

  同时,等待唤醒机制要避免死锁的出现。所谓死锁就是同步中嵌套同步,然后多线程在执行时相互取得不同的锁后不释放,造成程序一致等待。请看下面例子:

  1. class Test implements Runnable {
  2. private boolean flag;
  3. Test(boolean flag) {
  4. this.flag = flag;
  5. }
  6.  
  7. public void run() {
  8. if(flag) {
  9. while(true) {
  10. synchronized(MyLock.locka) {
  11. System.out.println(Thread.currentThread().getName()+"...if locka ");
  12. synchronized(MyLock.lockb) {
  13. System.out.println(Thread.currentThread().getName()+"..if lockb");
  14. }
  15. }
  16. }
  17. } else {
  18. while(true) {
  19. synchronized(MyLock.lockb) {
  20. System.out.println(Thread.currentThread().getName()+"..else lockb");
  21. synchronized(MyLock.locka) {
  22. System.out.println(Thread.currentThread().getName()+".....else locka");
  23. }
  24. }
  25. }
  26. }
  27. }
  28. }
  29.  
  30. class MyLock {
  31. static Object locka = new Object();
  32. static Object lockb = new Object();
  33. }
  34.  
  35. class DeadLockTest {
  36. public static void main(String[] args) {
  37. Thread t1 = new Thread(new Test(true));
  38. Thread t2 = new Thread(new Test(false));
  39. t1.start();
  40. t2.start();
  41. }
  42. }

运行结果是程序一直等待不动。

  另外需要注意的,wait()和sleep()的区别:
  ①wait()是Object类中的方法;sleep()是Thread类中的方法。
  ②它们两个都可以让线程冻结,wait()需要被唤醒;而sleep在冻结一定时间后会自动唤醒。
  ③wait()释放cpu执行权的同时,会释放锁;而sleep()只释放执行权,不会释放锁。

  (3)在JDk1.5中提供了更加灵活的多线程同步方法:Lock。相应地在使用Lock同步时,也可以使用Condition中的await(),signal();signalAll();来实现多线程等待唤醒机制。以下是具体使用例子:

  1. import java.util.concurrent.locks.*;
  2.  
  3. /*
  4. JDk1.5中提供了多线程升级解决方案。
  5. 将同步隐式锁Syncronized替换成显式Lock操作。
  6. 将Object中的wait,notify,notifyAll,替换成Condition对象。
  7. 该对象可以通过Lock锁进行获取。
  8. 该示例中,实现了本方只唤醒对方的操作。
  9.  
  10. Lock:替换了synchronized
  11. lock;
  12. unlock;
  13. newCondition();
  14.  
  15. Condition:替代了Object中方法wait notify notifyAll
  16. await();
  17. signal();
  18. signalAll();
  19. */
  20. class Resource {
  21. private String name;
  22. private int count = 1;
  23. private boolean flag = false;
  24. private Lock lock = new ReentrantLock(); //创建同步锁。
  25. private Condition condition_pro = lock.newCondition(); //通过Lock建立不同的条件。
  26. private Condition condition_con = lock.newCondition();
  27.  
  28. public void set(String name)throws InterruptedException {
  29. try {
  30. lock.lock();
  31. while (flag)
  32. condition_pro.await();
  33. this.name = name+"--"+count++;
  34. System.out.println(Thread.currentThread().getName()+"生产者:"+this.name);
  35. flag = true;
  36. condition_con.signal(); //实现只唤醒消费者。
  37.  
  38. } finally { //利用finally特性保证最后能释放锁。
  39. lock.unlock();
  40. }
  41. }
  42.  
  43. public void out()throws InterruptedException {
  44. try {
  45. lock.lock();
  46. while(!flag)
  47. condition_con.await();
  48. System.out.println(Thread.currentThread().getName()+"----消费者:"+this.name);
  49. flag = false;
  50. condition_pro.signal(); //实现只唤醒生产者。
  51. } finally { //利用finally特性保证最后能释放锁。
  52. lock.unlock();
  53. }
  54. }
  55. }
  56.  
  57. class Producer implements Runnable {
  58. private Resource r;
  59. Producer(Resource r) {
  60. this.r = r;
  61. }
  62.  
  63. public void run() {
  64. try {
  65. while(true)
  66. r.set("+商品+");
  67. } catch (InterruptedException e){
  68.  
  69. }
  70.  
  71. }
  72. }
  73.  
  74. class Consumer implements Runnable {
  75. private Resource r;
  76. Consumer(Resource r) {
  77. this.r = r;
  78. }
  79.  
  80. public void run() {
  81. try {
  82. while(true)
  83. r.out();
  84. } catch (InterruptedException e) {
  85.  
  86. }
  87. }
  88. }
  89.  
  90. class ProducerConsumerDemo2 {
  91. public static void main(String[] args) {
  92. Resource r = new Resource();
  93.  
  94. Producer pro = new Producer(r);
  95. Consumer con = new Consumer(r);
  96.  
  97. Thread t1 = new Thread(pro);
  98. Thread t2 = new Thread(pro);
  99. Thread t3 = new Thread(con);
  100. Thread t4 = new Thread(con);
  101.  
  102. t1.start();
  103. t2.start();
  104. t3.start();
  105. t4.start();
  106. }
  107. }

运行结果是不停的生产和消费,但生产和消费的数目总是一样的。说明程序通过标记成功控制每生产一个商品就消费一个,并且实现了多线程的同步。

六、多线程的停止

  多线程的stop()已过时,所以想要多线程停止的方法只有通过run方法结束。多线程的代码通常都是摆在循环结构中的,所以只要控制循环,就可以让run方法结束,也就是线程结束。

  但是,如果线程处于冻结状态(等待,睡眠),读不到循环的标记,那么程序就无法停止。所以在没有指定方式让冻结的线程恢复到运行状态中时,可以使用Thread类中的interrupt()方法强行让线程恢复到运行状态中,这样线程就能继续执行读取到循环标记,从而停止线程。以下是例子:

  1. class StopThread implements Runnable {
  2. private boolean flag = true;
  3. public synchronized void run() {
  4. while(flag) {
  5. try {
  6. wait();
  7. }
  8. catch (InterruptedException e) {
  9. //注意这里接收到的InterruptedException是t1.interrupt()和t2.interrupt()抛出的异常。
  10. System.out.println(Thread.currentThread().getName()+".........Exception");
  11. changeFlag(); //最后记住要改标记才能让线程恢复运行后停止。
  12. }
  13.  
  14. System.out.println(Thread.currentThread().getName()+"...run");
  15. }
  16. }
  17.  
  18. public void changeFlag() {
  19. flag = false;
  20. }
  21.  
  22. }
  23.  
  24. class StopThreadDemo {
  25. public static void main(String[] args) {
  26. StopThread st = new StopThread();
  27.  
  28. Thread t1 = new Thread(st);
  29. Thread t2 = new Thread(st);
  30.  
  31. t1.start();
  32. t2.start();
  33.  
  34. int num = 0;
  35. while(true) {
  36. if(num++ == 60) {
  37. // st.changeFlag();
  38. t1.interrupt(); //清除线程中断状态,但是会抛出InterruptedException异常。
  39. t2.interrupt();
  40. break;
  41. }
  42. System.out.println(Thread.currentThread().getName()+"..."+num);
  43. }
  44. System.out.println("over");
  45. }
  46. }

运行结果是程序顺利结束。

七、线程中的其他常见方法

  1.join:
  当A线程执行到了B线程的.join()方法时,A机会等待。等B线程都执行完,A线程才会执行。
  join可以用来临时加入线程执行。

  2.setDaemon(boolean):
  将线程标记为后台程序(守护线程),后台线程和前台线程一样开启,一样抢执行权运行。
  只有在结束时有区别。当前台线程都运行结束后,后台程序会自动结束。

  3.setPriority:
  更改线程优先级别:级别为(1~10),线程默认优先级别为5。
  Thread.MAX_PRIORITY = 10;
  Thread.NORM_PRIORITY = 5;
  Thread.MIN_PRIORITY = 1。

  4.yield:
  暂停正在执行的线程,并执行其他线程。

  以下是它们的使用例子:

  1. class Demo implements Runnable {
  2. public void run() {
  3. for (int x= 0; x<70; x++){
  4. System.out.println(Thread.currentThread().getName()+"..."+x);
  5. Thread.yield();
  6. }
  7. }
  8. }
  9.  
  10. class JoinDemo {
  11. public static void main(String[] args)throws Exception {
  12. Demo d = new Demo();
  13.  
  14. Thread t1 = new Thread(d);
  15. Thread t2 = new Thread(d);
  16. // t1.setDaemon(true); //设置为守护线程,一旦前台所有线程结束,守护线程会自动结束。
  17. // t1.setPriority(Thread.MAX_PRIORITY); //设置线程优先级别,级别越大,线程得到cpu执行权概率就越大。
  18. t1.start();
  19. t1.join();
  20. t2.start();
  21.  
  22. for (int x=0; x<80; x++)
  23. System.out.println("main..."+x);
  24. System.out.println("over");
  25. }
  26. }

运行结果是t1的0线程全部要执行完后,再把执行主线程main执行完,最后才执行t2的1线程。

  多线程总结完毕。

黑马程序员_Java基础:多线程总结的更多相关文章

  1. 黑马程序员_Java基础视频-深入浅出精华版--PPT 文件列表

    \day01\code\第一章_Java概述.ppt;\day01\resource\资料\50道编程题(有精力的同学看看).doc;\day01\resource\资料\Sun_Java程序员认证考 ...

  2. 黑马程序员_Java基础:网络编程总结

    ------- android培训.java培训.期待与您交流! ---------- Java语言是在网络环境下诞生的,它是第一个完全融入网络的语言,虽然不能说它是对支持网络编程做得最好的语言,但是 ...

  3. 黑马程序员_Java基础组成

    Java语言基础组成 2.1关键字 main不是关键字,但被JVM所识别的名称. 关键字的定义和特点 定义:被Java语言赋予了特殊含义的单词. 特点:关键字中所有字母都为小写. 用于定义数据类型的关 ...

  4. 黑马程序员_Java基础:实现多线程对共有数据的同步操作

    ------- android培训.java培训.期待与您交流! ---------- 实现多线程对共有数据的同步操作,主要涉及到多线程和同步. 虽然都是基础,但是这把刀还是要用熟练,等到使用的时候才 ...

  5. 黑马程序员_java基础笔记(04)...多线程

    ——————————ASP.Net+Android+IOS开发..Net培训.期待与您交流!—————————— 多线程.第一重点:创建线程的两种方式.第二重点:同步的所有特性      进程:正在执 ...

  6. 黑马程序员_Java基础:IO流总结

    ------- android培训.java培训.期待与您交流! ---------- IO流在是java中非常重要,也是应用非常频繁的一种技术.初学者要是能把IO技术的学透,java基础也就能更加牢 ...

  7. 黑马程序员_Java基础视频-深入浅出精华版--视频列表

    \day01\avi\01.01_计算机基础(计算机概述).avi; \day01\avi\01.02_计算机基础(计算机硬件和软件概述).avi; \day01\avi\01.03_计算机基础(软件 ...

  8. 课程2:《黑马程序员_Java基础视频-深入浅出精华版》-视频列表-

    \day01\avi\01.01_计算机基础(计算机概述).avi; \day01\avi\01.02_计算机基础(计算机硬件和软件概述).avi; \day01\avi\01.03_计算机基础(软件 ...

  9. 黑马程序员_java基础笔记(08)...GUI,网络编程,正则表达式

    —————————— ASP.Net+Android+IOS开发..Net培训.期待与您交流! —————————— GUI(Graphical User Interface)(图形用户接口):用图形 ...

随机推荐

  1. 谷歌Java编程规范

    Google Java编程风格指南 January 20, 2014 作者:Hawstein 出处:http://hawstein.com/posts/google-java-style.html 声 ...

  2. LDAP客户端

    LDAP客户端通过与服务端关联起来,就可以使用服务端的系统账号登录系统,通过useradd 添加用户是在ldap里是没有显示的,ldap添加用户,在/etc/passwd里也是没有显示的,ldap添加 ...

  3. extern extern “C”用法详解

    1.extern 修饰一个变量,告诉编译器这个变量在其他地方定义,编译器不会给出变量未定义的警告. extern tells the compiler that the variable is def ...

  4. app 要求字体使用楷体,使用字体包

    1,下载字体包     http://www.3987.com/xiazai/6/fonts/36616.html#down 2.  studio中src\main\创建assets\fonts,存放 ...

  5. 足球游戏AI_资料收集

    实况足球中文官网 浅谈足球游戏的人工智能 用遗传算法加强足球游戏的人工智能 足球规则图解 守门员的技巧你知道吗? 教你足球守门员守门技术练习方法和技巧 足球守门员规则 判断点球方向

  6. NGUI 灰化按钮或图标

    在游戏中某些地方可能需要对按钮进行灰化显示,从而表示不能点击!在网上找了一下有些方法是直接用UITexture+灰化shader去做这件事!另外有些方案写的不太清楚,看不懂!不过也基本都是要使用灰化s ...

  7. Docker学习<一>--初体验Windows环境下安装

    背景 今天想试用spring boot与jwt协议的实现,配套就需要使用redis,但redis似乎windows环境版本部署起来不是那么舒心,果断尝试使用docker. 下载 下载地址: 稳定版:h ...

  8. 这有一个flag

    1.并查集[1224] 2.最小生成树?? 3.topsort(好洋气): 4.归并排序[1438]: 5.差分约束系统: 6.A*算法找k短路 7.scanf: 8.搜索[P1198]华容道: 9. ...

  9. Flask 备注一(单元测试,Debugger, Logger)

    Flask 备注一(单元测试,Debugger, Logger) Flask是一个使用python开发Web程序的框架.依赖于Werkzeug提供完整的WSGI支持,以及Jinja2提供templat ...

  10. MVC 之 WebAPI 系列二

    今天,我想在此记录下 WebApi 跨域调用 1. 什么叫跨域: 跨域问题简单理解就是JavaScript同源策略的限制,其根本原因是因为浏览器对于这种请求,所给予的权限是较低的,通常只允许调用本域中 ...