黑马程序员-多线程学习笔记



期待与您交流! ----------

----------------------------------------线程的概念----------------------------------------

线程的概念:

  1.我们之前的程序都是"单线程"程序:代码一行一行执行,后面的代码总要等到前面的代码执行完毕,才能获得执行;如果前面的代码很复杂,或者被阻塞,后边的代码就不会被执行得到;

  2.我们可以将一些代码作为一个"独立"的线程去执行,这样在主程序,将线程启动后,这个线程中的代码就会和主程序的代码"并行"执行。

 

 

要了解"线程"先要了解"进程":

 1.进程:

    1).什么是进程:进程是操作系统的概念,它就是指在操作系统中运行的某个程序;每个程序对于操作系统来说都是一个独立的进程,由操作系统管理,分配内存、CPU执行时间.....

    2).什么是多进程:是指操作系统可以同时管理多个应用程序的同时运行;

    3).多进程的意义:可以充分利用CPU资源;可以使用户有更好的操作体验,可以同时运行多个程序;

 2.线程:

    1).什么是线程:线程是指在一个"主进程"中,可以使某段代码以独立于主进程的方式运行。线程中的代码,与主进程中的代码同时抢占操作系统资源;

    2).什么是多线程:多线程是指,一个主进程可以启动多个线程,去独立运行。

    3).多线程的意义:多线程也可以充分利用CPU资源;可以使我们的几段代码"同时"运行,提高我们的代码效率;

 3.并行和并发:

   1).并行:是指多个线程在"某个时间段内"同时运行;

   2).并发:是指多个线程在"某个时间点上"同时的访问同一资源;

 

----------------------------------------多线程的实现方式----------------------------------------

多线程程序实现的方式1:

 1.Java中一个线程使用"Thread"类表示;

 2.实现线程的方式:

    1).定义一个线程类,继承自Thread;

    2).重写run()方法;将要在此线程中执行的代码写到这里;

    3).启动线程:

        a.实例化一个自定义线程类的对象;

        b.调用对象的start()方法启动线程;

 3.注意:

   1).启动线程,一定要使用start()方法。调用run()不会产生编译错误,但这只是简单的方法调用,不是启动线程;

   2).不能多次的调用start()方法;  

线程实现方式1代码   收藏代码
  1. public class MyThread extends Thread {//自定义一个线程  
  2.     @Override  
  3.     public void run() {//重写run方法  
  4.         for (int i = 0; i < 100; i++) {  
  5.             System.out.println("i = " + i);  
  6.         }  
  7.     }  
  8. }  

 

影音先锋电影 http://www.iskdy.com/  

 

测试类代码   收藏代码
  1. public class Demo {  
  2.     public static void main(String[] args) {  
  3.         MyThread t = new MyThread();  
  4.         t.start();  
  5.     //  t.run();//这不是启动线程,这只是简单的方法调用;  
  6.         for(int k = 0;k < 100 ; k++){  
  7.             System.out.println("k = " + k);  
  8.         }  
  9.   
  10. }  

 

 

多线程程序实现的方式2:

  1.自定义实现Runnable接口;

  2.重写run()方法;

  3.启动线程:

      1).实例化我们自定义类的对象;

      2).实例化一个Thread对象,将我们的自定义对象作为参数传递给Thread的构造方法;

      3).调用Thread对象的start()方法启动线程

线程实现方式2代码   收藏代码
  1. public class MyRunnable implements Runnable {// 实现Runnable接口自定义线程  
  2.   
  3.     @Override  
  4.     public void run() {// 复写run方法  
  5.         for (int i = 0; i < 100; i++) {  
  6.             System.out.println("i = " + i);  
  7.         }  
  8.     }  
  9. }  

 

测试代码代码   收藏代码
  1. public class Demo {  
  2.     public static void main(String[] args) {  
  3.         MyRunnable myRun = new MyRunnable();  
  4.         Thread t = new Thread(myRun);  
  5.         t.start();// 启动县城关  
  6.         for (int k = 0; k < 100; k++) {  
  7.             System.out.println("k = " + k);  
  8.         }  
  9.     }  
  10. }  

 JDK5实现线程的方式3:

  1.自定义类,实现Callable接口;

  2.重写:call()方法;

  3.使用"线程池"的方式去启动线程;

线程实现方式3代码   收藏代码
  1. public class MyCallable implements Callable{  
  2.   
  3.     @Override  
  4.     public Object call() throws Exception {  
  5.         for(int i = 0; i < 100 ; i++){  
  6.             System.out.println("i = " + i);  
  7.         }  
  8.         return null;   
  9.     }  
  10. }  

 

测试类代码   收藏代码
  1. public class Demo {  
  2.     public static void main(String[] args) {  
  3.         ExecutorService service = Executors.newFixedThreadPool(2);//线程池  
  4.         MyCallable myCall = new MyCallable();  
  5.         service.submit(myCall);//使用线程池的方法启动线程  
  6.         service.shutdown();  
  7.           
  8.         for(int j = 0;j < 100; j++){  
  9.             System.out.println("j = " + j);  
  10.         }  
  11.     }  
  12. }  

---------------------------------------------多线程的方法-------------------------------------------------------------

获取和设置线程对象名称:

  1.一个线程类,可以实例化多个对象,每个对象都可以以单独的线程去执行;

  2.每个线程,都有一个默认的名称,格式:"Thread-索引":

  3.设置线程名称:setName(String name):

     获取线程名称:getName();

设置线程名称代码   收藏代码
  1. public class Demo {  
  2.     public static void main(String[] args) {  
  3.         MyThread t1 = new MyThread();  
  4.         MyThread t2 = new MyThread();  
  5.         MyThread t3 = new MyThread();  
  6.           
  7.         t1.setName("章子怡");//设置线程名称  
  8.         t2.setName("汪峰");//设置线程名称  
  9.         t3.setName("撒贝宁");//设置线程名称  
  10.           
  11.         t1.start();  
  12.         t2.start();  
  13.         t3.start();  
  14.     }  
  15. }  

 线程优先级:

  1.Java中的线程优先级:从低到高:1--10

  2.默认的优先级是:5

  3.设置优先级:

setPriority(int p):设置优先级:一定要在1--10的范围内,否则抛出异常;

getPriority():获取线程的优先级;

  4.注意:

     1).较高的优先级,只代表有机会先被执行,但仍由操作系统管理,仍然有很多的不确定性;

     2).所以,大家不要利用"优先级"的技术,去试图向让某个线程先执行完毕;

     3).如果线程内,有较少的代码,执行逻辑很简单,那么优先级的效果就不会明显;

线程优先级设定代码   收藏代码
  1. public class Demo {  
  2.     public static void main(String[] args) {  
  3.         MyThread t1 = new MyThread();  
  4.         MyThread t2 = new MyThread();  
  5.         MyThread t3 = new MyThread();  
  6.         MyThread t4 = new MyThread();  
  7.         MyThread t5 = new MyThread();  
  8.         MyThread t6 = new MyThread();  
  9.           
  10.         //设置优先级  
  11.     //  t1.setPriority(100);//一定要在1--10的范围内,否则抛出异常;  
  12.         t1.setPriority(1);  
  13.         t2.setPriority(10);//最高优先级  
  14.         t3.setPriority(1);  
  15.         t4.setPriority(1);  
  16.         t5.setPriority(1);  
  17.         t6.setPriority(1);  
  18.           
  19.         //设置线程名称  
  20.         t1.setName("线程1");  
  21.         t2.setName("线程2");  
  22.         t3.setName("线程3");  
  23.         t4.setName("线程4");  
  24.         t5.setName("线程5");  
  25.         t6.setName("线程6");  
  26.           
  27.         //启动线程  
  28.         t1.start();  
  29.         t2.start();  
  30.         t3.start();  
  31.         t4.start();  
  32.         t5.start();  
  33.         t6.start();  
  34.               
  35.     }  
  36. }  

 

 

线程的休眠:

  1.public static void sleep(long millis):在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。

 

线程休眠代码   收藏代码
  1. public class MyThread extends Thread{  
  2.     @Override  
  3.     public void run() {  
  4.         for(int i = 0; i < 10 ;i++){  
  5.             Date date = new Date();  
  6.             SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
  7.             String str = sdf.format(date);  
  8.             System.out.println(str);  
  9.             //休息一秒  
  10.             try {  
  11.                 Thread.sleep(1000);//休眠1秒  
  12.             } catch (InterruptedException e) {  
  13.                 e.printStackTrace();  
  14.             }  
  15.         }  
  16.     }  
  17. }  

 线程的礼让:

  1.public static void yield():暂停当前正在执行的线程对象,并执行其他线程。(退回到"就绪"状态)

  注意:礼让后,很可能会被操作系统再次分配执行,所以,不能利用这个技术试图让某个线程最后执行完毕

线程礼让代码   收藏代码
  1. public class MyThread extends Thread{  
  2.     public void run() {  
  3.         for(int i = 0;i < 1000 ; i++){  
  4.             if(this.getName().equals("汪峰")){  
  5.                 Thread.yield();//礼让:退回到"就绪"状态,有可能会被操作系统再次分配执行;  
  6.             }  
  7.             System.out.println(this.getName() + " i = " + i);  
  8.         }  
  9.     };  
  10. }  

守护线程:

  1.public final void setDaemon(boolean on):将该线程标记为守护线程或用户线程。

  2.默认情况下,当主进程开出一个线程后,都会等待线程结束后,主进程才会结束;这个就是非守护线程;

  3.我们可以将线程设为"守护线程":当主进程完毕时,开出的所有守护线程也跟着结束;但是不会立即结束,会有个小缓冲;

守护线程代码   收藏代码
  1. public class Demo {  
  2.     public static void main(String[] args) {  
  3.         MyThread t1 = new MyThread();  
  4.         t1.setName("小兵");  
  5.         //将线程设为守护线程  
  6.         t1.setDaemon(true);  
  7.         t1.start();  
  8.           
  9.         for(int i = 0; i < 100 ;i++){  
  10.             System.out.println("将军正在杀敌...i = " + i);  
  11.         }  
  12.         System.out.println("将军杀敌完毕,打道回府!!");  
  13.     }  
  14. }  

 线程的中断:

  public final void stop():

  public void interrupt():基于在线程的内部,当处于以下三种阻塞状态时,才可以促使其停止:

   1.Object--wait():

   2.Thread--join():

   3.Thread--sleep():

线程中断代码   收藏代码
  1. public class MyThread extends Thread {  
  2.     @Override  
  3.     public void run() {  
  4.         for(int i = 0;i < 10 ; i++){  
  5.             System.out.println("我不停:i = " + i);  
  6.             try {  
  7.                 Thread.sleep(1000);  
  8.             } catch (InterruptedException e) {  
  9.                 System.out.println("线程出现异常了,可能是外部试图停掉我,那么好吧,拜拜......");  
  10.                 return;  
  11.               
  12.             }  
  13.         }  
  14.     }  
  15. }  

 

测试类代码   收藏代码
  1. public class Demo {  
  2.     public static void main(String[] args) {  
  3.         MyThread t1 = new MyThread();  
  4.         t1.start();  
  5.         // 主进程等待3秒钟,如果线程不结束,这里给它干掉  
  6.         System.out.println("主进程等待3秒...");  
  7.         try {  
  8.             Thread.sleep(1000 * 3);  
  9.         } catch (InterruptedException e) {  
  10.             e.printStackTrace();  
  11.         }  
  12.         System.out.println("主进程醒来,干掉线程......");  
  13.   
  14.         // t1.stop();//过时,不用  
  15.         // 线程中断,使其抛出异常  
  16.         t1.interrupt();  
  17.     }  
  18. }  

 使用同步解决并发访问的问题:

  1.在共享资源上(一般是一些方法)使用关键字:synchronized

  2.作用:当一个线程访问时,其它线程全部列队等待;这种机制保证了这个方法在同一时刻只能被一个线程访问;

  3.线程同步的方式:

     1).同步代码块:

synchronized语法:

 

  synchronized(被锁的对象){

  //同步代码


伦理片 http://www.dotdy.com/

 注:被锁的对象:当一个线程访问此段代码时,会将这个对象中所有的"同步代码块"和"同步方法"加锁,

     也就意味着,一个线程访问一段同步代码块,其它线程不能访问"被锁对象"的其它"同步代码块"和"同步方法";

 

     2).同步方法:

     可以在方法声明时,添加关键字:synchronized,表示这个方法内部的所有代码都是同步的。

 

线程组:

 1.我们可以将多个线程,进行分组管理;好处:可以对组内的线程进行统一操作;

 2.所有的线程,默认都属于"主线程组";

 3.我们可以为线程进行分组;

Java代码   收藏代码
  1. public class MyThread extends Thread {  
  2.     public MyThread() {  
  3.         System.out.println("实例化一个我的对象,很耗时哟,需要5秒钟......");  
  4.         for (int i = 0; i < 5; i++) {  
  5.             System.out.println(i + 1);  
  6.             try {  
  7.                 Thread.sleep(1000);  
  8.             } catch (InterruptedException e) {  
  9.                 e.printStackTrace();  
  10.             }  
  11.         }  
  12.     }  
  13.   
  14.     @Override  
  15.     public void run() {  
  16.         System.out.println("线程打印:run()......");  
  17.     }  
  18. }  
测试代码代码   收藏代码
  1. public class Demo {  
  2.     public static void main(String[] args) {  
  3.         MyThread t1 = new MyThread();  
  4.         MyThread t2 = new MyThread();  
  5.   
  6.         ThreadGroup grp1 = t1.getThreadGroup();  
  7.         ThreadGroup grp2 = t2.getThreadGroup();  
  8.   
  9.         System.out.println("线程1的所在的组:" + grp1.getName());  
  10.         System.out.println("线程2的所在的组:" + grp2.getName());  
  11.   
  12.         // 为线程分组  
  13.         MyThread t3 = new MyThread();  
  14.         MyThread t4 = new MyThread();  
  15.   
  16.         // 实例化一个"线程组"对象  
  17.         ThreadGroup group = new ThreadGroup("我的第一小队");  
  18.   
  19.         // 跟线程组关联  
  20.         Thread th1 = new Thread(group, t3);  
  21.         Thread th2 = new Thread(group, t4);  
  22.   
  23.         th1.start();  
  24.         th2.start();  
  25.         // 主进程睡眠3秒钟  
  26.         try {  
  27.             System.out.println("主进程睡眠3秒......");  
  28.             Thread.sleep(1000 * 3);  
  29.         } catch (InterruptedException e) {  
  30.             // TODO Auto-generated catch block  
  31.             e.printStackTrace();  
  32.         }  
  33.   
  34.         System.out.println("主进程醒来,一次停止组内所有线程......");  
  35.         // 可以一次停止组内的所有线程  
  36.         group.interrupt();  
  37.     }  
  38. }  

 线程池:

1.当使用继承Thread的方式实现线程,如果想反复的使用此线程,不能多次调用start();可以多次实例化此类的对象,然后再启动线程。

2.如果线程的启动,或者构造比较耗时,那么就大大的影响效率;

3.JDK5之后,提供了一个"线程池",这个"线程池"可以缓存一些"线程对象",如果需要再次使用时,无需重复构造,直接从"池"中取出线程对象使用即可;

 4.JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法

       public static ExecutorService newCachedThreadPool():创建一个可根据需要创建新线程的线程池

       public static ExecutorService newFixedThreadPool(int nThreads):创建一个可重用固定线程数的线程池        public static ExecutorService newSingleThreadExecutor():创建一个使用单个worker线程的ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。它提供了如下方法:

    Future<?> submit(Runnable task)

    <T> Future<T> submit(Callable<T> task)

1.获取一个线程池对象:

ExecutorService service = Executors.newFixedThreadPool(2);

2.调用线程池的submit()方法执行线程:

3.接收返回值:Future<Integer> result = service.submit(new MyCallable());

Java代码   收藏代码
  1. public class Demo {  
  2.     public static void main(String[] args) {  
  3.         /* 
  4.          * MyThread t1 = new MyThread();//需要5秒 t1.start(); 
  5.          *  
  6.          * System.out.println("主进程等待2秒......"); try { Thread.sleep(1000 * 2); } 
  7.          * catch (InterruptedException e) { e.printStackTrace(); } 
  8.          *  
  9.          * System.out.println("再次启动MyThread"); t1 = new MyThread();//再次需要5秒 
  10.          * t1.start(); 
  11.          */  
  12.   
  13.         // 获取一个"线程池"对象  
  14.         ExecutorService service = Executors.newFixedThreadPool(2);  
  15.         MyThread t3 = new MyThread();// 需要5秒  
  16.         // 执行线程t3  
  17.         service.submit(t3);  
  18.   
  19.         System.out.println("主进程休息2秒钟......");  
  20.         try {  
  21.             Thread.sleep(1000 * 2);  
  22.         } catch (InterruptedException e) {  
  23.             e.printStackTrace();  
  24.         }  
  25.         System.out.println("主进程再次启动线程......");  
  26.         service.submit(t3);// 无需再次构造  
  27.   
  28.         // 将线程池停止  
  29.         service.shutdown();  
  30.     }  
  31. }  
Java代码   收藏代码
  1. public class MyThread extends Thread {  
  2.     public MyThread() {  
  3.         System.out.println("实例化一个我的对象,很耗时哟,需要5秒钟......");  
  4.         for (int i = 0; i < 5; i++) {  
  5.             System.out.println(i + 1);  
  6.             try {  
  7.                 Thread.sleep(1000);  
  8.             } catch (InterruptedException e) {  
  9.                 e.printStackTrace();  
  10.             }  
  11.         }  
  12.     }  
  13.   
  14.     @Override  
  15.     public void run() {  
  16.         System.out.println("线程打印:run()......");  
  17.     }  
  18. }  

 匿名内部类的方式实现线程:

1.new Thread(){Thread匿名子类};

2.new Thread(new Runnable(){}){};

3.new Thread(new Runnable(){}){Thread匿名子类};

Java代码   收藏代码
  1. public class Demo {  
  2.     public static void main(String[] args) {  
  3.         new Thread() {  
  4.             // 重写run()方法  
  5.             @Override  
  6.             public void run() {  
  7.                 System.out.println("a");  
  8.             }  
  9.         }.start();  
  10.   
  11.         new Thread(new Runnable() {  
  12.             @Override  
  13.             public void run() {  
  14.                 System.out.println("b");  
  15.   
  16.             }  
  17.   
  18.         }) {  
  19.         }.start();  
  20.   
  21.         new Thread(new Runnable() {  
  22.             @Override  
  23.             public void run() {  
  24.                 System.out.println("c");  
  25.   
  26.             }  
  27.   
  28.         }) {  
  29.             // 重写run()方法  
  30.             @Override  
  31.             public void run() {  
  32.                 System.out.println("d");  
  33.             }  
  34.         }.start();  
  35.     }  
  36. }  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值