黑马程序员_Java基础_多线程1

一、多线程(概述)

进程:是一个正在执行中的程序。每一个进程执行都有一个执行的顺序,该顺序就是一个执行路径,或者叫控制单元。

线程:是进程中的一个独立的控制单元。线程在控制着进程的执行。

一个进程中至少有一个线程。

Java VM启动的时候会有一个进程java.exe。该进程中至少有一个线程负责java程序的执行,而且这个线程运行的代码存在于main方法中,该线程称为主线程。

扩展:其实JVM启动不止一个线程,还有负责垃圾回收机制的线程。

二、多线程(创建线程-继承Thread)

通过API查找,Java已经提供了对线程这类事物的描述,就是Thread类。

创建线程的第一种方式:继承Thread类。

1.定义类继承Thread类。

2.复写Thread类中的run方法。

3.调用线程的start方法,该方法有两个作用,第一是启动线程,第二是调用run方法。

 

[java]  view plain copy
  1. /* 
  2. 测试: 
  3. */  
  4. class Demo extends Thread  
  5. {  
  6.     public void run()  
  7.     {  
  8.         for(int i=0;i<60;i++)  
  9.             System.out.println("demo run");  
  10.     }  
  11. }  
  12. class ThreadDemo   
  13. {  
  14.     public static void main(String[] args)   
  15.     {  
  16.         Demo d=new Demo();//创建好一个线程。  
  17.         d.start();  
  18.           
  19.         for(int i=0;i<60;i++)  
  20.             System.out.println("min run");  
  21.     }  
  22. }  


发现运行结果每次都不同,因为多个线程都在获取CPU的执行权,CPU执行到谁,谁就运行。明确一点,在某一时刻,只能有一个程序在运行。CPU在做着快速的切换,以达到看上去是同时运行的效果。

我们可以形象的把多线程的运行行为看作是在互相抢夺CPU的执行权,这就是多线程的一个特性:随机性。谁抢到谁执行,至于执行多长时间,CPU说的算。

三、多线程(创建线程-runstart特点)

为什么要复写run方法?

Thread类用于描述线程。该类就定义了一个功能,用于存储线程要执行的代码,该存储功能就是run方法。也就是说,Thread 类中的run方法用于存储线程要执行的代码。

所以,复写run方法的目的是将自定义的代码存储在run方法中,让线程运行。

start:开启线程,执行run方法。

run:仅仅是对象调用方法。虽然线程创建了,但没执行。

四、多线程(线程练习)

[java]  view plain copy
  1. /* 
  2. 练习:创建两个线程和主线程交替运行。 
  3. */  
  4. class Test extends Thread  
  5. {  
  6.     private String name;  
  7.     Test(String name)  
  8.     {  
  9.         this.name=name;  
  10.     }  
  11.     public void run()  
  12.     {  
  13.         for(int i=0;i<60;i++)  
  14.         {     
  15.             System.out.println(name+" run..."+i);  
  16.         }  
  17.     }  
  18. }  
  19.   
  20. class TreadTest   
  21. {  
  22.     public static void main(String[] args)   
  23.     {  
  24.         Test t1=new Test("one");  
  25.         Test t2=new Test("two");  
  26.         t1.start();  
  27.         t2.start();  
  28.   
  29.         for(int i=0;i<60;i++)  
  30.         {     
  31.             System.out.println("main run..."+i);  
  32.         }  
  33.     }  
  34. }  


五、多线程(线程运行状态)

六、多线程(获取线程对象以及名称)

线程都有自己的默认的名称:Thread-编号 该编号从0开始。通过Thread类的getName方法获取。

static Thread currentThread();//获取当前线程对象。

getName();//获取线程名称

设置线程名称:setName或者构造函数。

七、多线程(售票的例子)

[java]  view plain copy
  1. /* 
  2. 需求:简单的卖票程序 
  3. 多个窗口同时卖票 
  4. */  
  5. class Ticket extends Thread  
  6. {  
  7.     private static int tick=100;  
  8.     public void run()  
  9.     {  
  10.         while(true)  
  11.         {  
  12.             if(tick>0)  
  13.             {  
  14.                 System.out.println(currentThread().getName()+"...sale:"+tick--);  
  15.             }  
  16.         }  
  17.   
  18.     }  
  19. }  
  20. class TicketDemo   
  21. {  
  22.     public static void main(String[] args)   
  23.     {  
  24.         Ticket t1=new Ticket();  
  25.         //Ticket t2=new Ticket();  
  26.         //Ticket t3=new Ticket();  
  27.         //Ticket t4=new Ticket();  
  28.   
  29.         t1.start();  
  30.         t1.start();  
  31.         t1.start();  
  32.         t1.start();  
  33.   
  34.     }  
  35. }  


八、多线程(创建线程-实现Runnable接口)

创建线程的第二种方式:

1.定义类实现Runnable接口。

2.复写Runnable接口中的run方法。(将线程要运行的代码存放在该run方法中)

3.通过Thread类建立线程对象。

4.将Runnable接口的子对象作为实际参数传递给Thread类的构造函数。(为什么要将Runnable接口的子类对象传递给Thread类的构造函数?因为自定义的run方法所属的对象是Runnable接口的子类对象,所以要让线程去指定对象的run方法,就必须明确该run方法所属的对象)

5.调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。

实现方式和继承方式有什么区别?

实现方式好处:避免了单继承的局限性。在定义线程时,建议使用实现方式。

两种方式的区别:

继承Thread类:线程代码存放在Thread子类run方法中。

实现Runnable接口:线程代码存在接口的子类的run方法中。

[java]  view plain copy
  1. /* 
  2. 实现Runnable接口: 
  3. */  
  4. class Ticket implements Runnable  
  5. {  
  6.     private static int tick=100;  
  7.     public void run()  
  8.     {  
  9.         while(true)  
  10.         {  
  11.             if(tick>0)  
  12.             {  
  13.                 System.out.println(Thread.currentThread().getName()+"...sale:"+tick--);  
  14.             }  
  15.         }  
  16.   
  17.     }  
  18. }  
  19. class TicketDemo   
  20. {  
  21.     public static void main(String[] args)   
  22.     {  
  23.         Ticket t=new Ticket();  
  24.         Thread t1=new Thread(t);  
  25.         Thread t2=new Thread(t);  
  26.         Thread t3=new Thread(t);  
  27.         Thread t4=new Thread(t);  
  28.         t1.start();  
  29.         t2.start();  
  30.         t3.start();  
  31.         t4.start();  
  32.   
  33.     }  
  34. }  


九、多线程(线程的安全问题)

情景模拟:

[java]  view plain copy
  1. /* 
  2. 需求:简单的卖票程序 
  3. 多个窗口同时卖票 
  4. */  
  5. class Ticket extends Thread  
  6. {  
  7.     private static int tick=100;  
  8.     public void run()  
  9.     {  
  10.         while(true)  
  11.         {  
  12.             if(tick>0)  
  13.             {  
  14.                 try  
  15.                 {  
  16.                     Thread.sleep(100);  
  17.                 }  
  18.                 catch (Exception e)  
  19.                 {  
  20.                 }  
  21.                   
  22.                 System.out.println(Thread.currentThread().getName()+"...sale:"+tick--);  
  23.             }  
  24.         }  
  25.   
  26.     }  
  27. }  
  28. class TicketDemo   
  29. {  
  30.     public static void main(String[] args)   
  31.     {  
  32.         //Ticket t=new Ticket();  
  33.         Ticket t1=new Ticket();  
  34.         Ticket t2=new Ticket();  
  35.         Ticket t3=new Ticket();  
  36.         Ticket t4=new Ticket();  
  37.         t1.start();  
  38.         t2.start();  
  39.         t3.start();  
  40.         t4.start();  
  41.   
  42.     }  
  43. }  


 

多线程的运行出现了安全问题。

问题的原因:

当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。

解决方法:

对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其它线程不可以参与执行。

Java对于多线程的安全问题提供了同步代码块来解决。

同步代码块:

synchronized(对象)

{

需要被同步的代码

}

[java]  view plain copy
  1. /* 
  2. 需求:简单的卖票程序 
  3. 多个窗口同时卖票 
  4. */  
  5. class Ticket extends Thread  
  6. {  
  7.     private  static int tick=100;  
  8.     Object obj=new Object();  
  9.     public void run()  
  10.     {  
  11.         while(true)  
  12.         {  
  13.             synchronized(obj)  
  14.             {  
  15.                 if(tick>0)  
  16.                 {  
  17.                     try  
  18.                     {  
  19.                         Thread.sleep(10);  
  20.                     }  
  21.                     catch (Exception e)  
  22.                     {  
  23.                     }  
  24.                   
  25.                 System.out.println(Thread.currentThread().getName()+"...sale:"+tick--);  
  26.                 }  
  27.             }  
  28.         }  
  29.   
  30.     }  
  31. }  
  32. class TicketDemo   
  33. {  
  34.     public static void main(String[] args)   
  35.     {  
  36.         //Ticket t=new Ticket();  
  37.         Ticket t1=new Ticket();  
  38.         Ticket t2=new Ticket();  
  39.         Ticket t3=new Ticket();  
  40.         Ticket t4=new Ticket();  
  41.         t1.start();  
  42.         t2.start();  
  43.         t3.start();  
  44.         t4.start();  
  45.   
  46.     }  
  47. }  


十、多线程(多线程-同步代码块)

[java]  view plain copy
  1. synchronized(对象)  
  2. {  
  3.   需要被同步的代码  
  4. }  


对象如同锁,持有锁的线程可以在同步中执行。没有持有锁的线程即使获取CPU的执行权,也进不去,因为没有获取锁。

同步的前提:

1.必须要有两个或两个以上的线程。

2.必须是多个线程使用同一个锁。

必须保证同步中只能有一个线程在运行。

好处:解决多线程的安全问题。

弊端:多个线程需要判断锁,较为消耗资源。

十一、多线程(多线程-同步函数)

[java]  view plain copy
  1. /* 
  2. 需求: 
  3. 银行有一个金库。有两个储户分别存300元,每次存100,存3次。 
  4.  
  5. 目的:该程序是否有安全问题?如果有,怎么解决? 
  6.  
  7. 如何找问题: 
  8. 1.明确哪些代码是多线程代码 
  9. 2.明确共享数据 
  10. 2.明确多线程运行代码中哪些语句是操作共享数据的。 
  11. */  
  12. class Bank  
  13. {  
  14.     private int sum;  
  15.     public synchronized void add(int n)  
  16.     {  
  17.         sum=sum+n;  
  18.         try  
  19.         {  
  20.             Thread.sleep(1000);  
  21.         }  
  22.         catch (Exception e)  
  23.         {  
  24.         }  
  25.         System.out.println("sum="+sum);  
  26.           
  27.     }  
  28. }  
  29. class Cus implements Runnable  
  30. {  
  31.     private Bank b=new Bank();  
  32.     public void run()  
  33.     {  
  34.         for(int i=0;i<3;i++)  
  35.         {  
  36.             b.add(100);  
  37.         }  
  38.     }  
  39. }  
  40. class BankDemo   
  41. {  
  42.     public static void main(String[] args)   
  43.     {  
  44.         Cus c=new Cus();  
  45.         Thread t1=new Thread(c);  
  46.         Thread t2=new Thread(c);  
  47.         t1.start();  
  48.         t2.start();  
  49.   
  50.     }  
  51. }  


十二、多线程(多线程-同步函数的锁是this)

函数需要被对象调用,那么函数都有一个所属对象的引用,就是this

[java]  view plain copy
  1. /* 
  2. 同步函数用的是哪个锁呢? 
  3. 函数需要被对象调用,那么函数都有一个所属对象的引用,就是this。 
  4. 所以,同步函数使用的锁是this。 
  5.  
  6. 通过该程序进行验证。 
  7. 使用两个线程来卖票: 
  8. 一个线程在同步代码块中。 
  9. 一个线程在同步函数中。 
  10. 都在执行卖票动作。 
  11. */  
  12. class Ticket implements Runnable  
  13. {  
  14.     private  static int tick=50;  
  15.     //Object obj=new Object();  
  16.     boolean flag=true;  
  17.     public  void run()  
  18.     {  
  19.         if(flag)  
  20.         {  
  21.             while(true)  
  22.             {  
  23.                 synchronized(this)  
  24.                 {  
  25.                     if(tick>0)  
  26.                     {  
  27.                         try{Thread.sleep(10);}catch (Exception e){}  
  28.                         System.out.println(Thread.currentThread().getName()+"...synchronized:"+tick--);  
  29.                     }  
  30.                 }  
  31.             }  
  32.   
  33.         }  
  34.         else   
  35.             while(true)  
  36.             {  
  37.                 this.show();  
  38.             }  
  39.                   
  40.           
  41.     }  
  42.     public synchronized void show()//this  
  43.     {  
  44.         if(tick>0)  
  45.         {  
  46.             try{Thread.sleep(10);}catch (Exception e){}  
  47.             System.out.println(Thread.currentThread().getName()+"...show:"+tick--);  
  48.         }  
  49.     }  
  50. }  
  51. class TicketDemo   
  52. {  
  53.     public static void main(String[] args)   
  54.     {  
  55.         Ticket t=new Ticket();  
  56.         Thread t1=new Thread(t);  
  57.         Thread t2=new Thread(t);  
  58.       
  59.         t1.start();  
  60.         try{Thread.sleep(10);}catch (Exception e){}  
  61.         t.flag=false;  
  62.         t2.start();  
  63.   
  64.     }  
  65. }  


 

十三、多线程(多线程-静态同步函数的锁是Class对象)

静态的同步方法,使用的锁是该方法所在类的字节码文件对象。类名.class

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


十四、多线程(多线程-单例设计模式-懒汉式)

[java]  view plain copy
  1. /* 
  2. 单例设计模式: 
  3.  
  4. 饿汉式: 
  5. class Single 
  6. { 
  7.     private static final Single s=new Single(); 
  8.     private Single(){} 
  9.     public static Single getInstance() 
  10.     { 
  11.         return s; 
  12.     } 
  13. } 
  14. */  
  15.   
  16.   
  17. //懒汉式:  
  18.   
  19. /* 
  20. 懒汉式和饿汉式有什么不同? 
  21. 懒汉式的特点在于延迟实例的加载。 
  22. 有没有问题? 
  23. 如果多线程访问会出现安全问题。 
  24. 怎么解决? 
  25. 加同步。同步代码块和同步函数都可以解决,但稍微低效。可以通过 
  26. 双重判断解决效率问题。 
  27. 加同步使用的锁是哪个? 
  28. 该类所属的字节码文件对象。类名.class 
  29.  
  30. */  
  31. class Single  
  32. {  
  33.     private static Single s=null;  
  34.     private Single(){};  
  35.     public static Single getInstance()  
  36.     {  
  37.         if(s==null)  
  38.         {  
  39.             synchronized(Single.class)  
  40.             {  
  41.                 if(s==null)  
  42.                 {  
  43.                     s=new Single();  
  44.                 }  
  45.                   
  46.             }  
  47.         }  
  48.                 return s;  
  49.     }  
  50. }  
十五、多线程 ( 多线程 - 死锁 )
[java]  view plain copy
  1. /* 
  2. 死锁: 
  3. */  
  4. class Test implements Runnable  
  5. {  
  6.     private boolean flag;  
  7.     Test(boolean flag)  
  8.     {  
  9.         this.flag=flag;  
  10.     }  
  11.     public void run()  
  12.     {  
  13.         if(flag)  
  14.         {  
  15.             synchronized(MyLook.looka)  
  16.             {  
  17.                 System.out.println("if looka");  
  18.                 synchronized(MyLook.lookb)  
  19.                 {  
  20.                     System.out.println("if lookb");  
  21.                 }  
  22.             }  
  23.         }  
  24.         else  
  25.         {  
  26.             synchronized(MyLook.lookb)  
  27.             {  
  28.                 System.out.println("else lookb");  
  29.                 synchronized(MyLook.looka)  
  30.                 {  
  31.                     System.out.println("else looka");  
  32.                 }  
  33.             }  
  34.         }  
  35.     }  
  36. }  
  37. class MyLook  
  38. {  
  39.     static Object looka=new Object();  
  40.     static Object lookb=new Object();  
  41.   
  42. }  
  43. class DeadLockDemo   
  44. {  
  45.     public static void main(String[] args)   
  46.     {  
  47.         Thread t1=new Thread(new Test(true));  
  48.         Thread t2=new Thread(new Test(false));  
  49.         t1.start();  
  50.         t2.start();  
  51.   
  52.           
  53.   
  54.     }  
  55. }  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值