黑马程序员系列第一篇 多线程(1)

 

(前言:本篇文章主要依据毕向东老师的课程视频整理而成,如要详细学习,请观看毕老师视频  百度网盘链接地址:http://pan.baidu.com/s/1sjQRHDz

目录:一、线程的两种创建方式        二、线程的五种状态、线程操作中常用的方法        三、多线程安全  四、使用同步的弊端         五、单例设计模式中的多线程(面试重点)

一、线程的两种创建方式

      1、实现Runnable接口(主流)

          代码示例:

 1 public class ThreadTest{
 2     
 3     public static void main(String[] args) throws InterruptedException{
 4       ThreadImple ti=new ThreadImple();
 5       Thread thread=new Thread(ti);
 6       
 7       thread.start();
 8 }
 9 }
10  class ThreadImple implements Runnable {
11 
12     @Override
13     public void run() {
14         for(int i=0;i<60;i++)
15         System.out.println("thread implements runnable..."+i);
16     }
17 }

      2、继承Thread类

     

 1 public class ThreadTest{
 2     
 3     public static void main(String[] args) throws InterruptedException{
 4       ThreadExtends ti=new ThreadExtends();
 5    
 6         ti.start();
 7 }
 8 }
 9  class ThreadExtends extends Thread {
10 
11     public void run() {
12         for(int i=0;i<60;i++)
13         System.out.println("extend thread class..."+i);
14     }
15 }

      3、两种创建方式的区别

            实现方式的运行代码存放在接口子类的run方法中;实现方式避免了单继承的局限性,建议使用

        继承方式的运行代码存放在Thread子类的run方法中

   

二、线程的五种状态    线程操作中常用的方法

 

三、多线程安全

        问题:一个线程操作多条语句来共享数据时,一个线程对多条语句只执行了一部分,另一个线程便参与进来执行了,导致共享数据出错。

        如何寻找问题:  1、明确哪些代码示多线程运行代码     2、明确共享数据     3、明确多线程运行代码中哪些语句是操作共享数据的。

  解决办法:同步代码块:

             Synchronized( 锁 ){

                  需要被同步的代码;

             }

                       同步函数:在函数前加synchronized修饰符。

  使用同步要满足三个前提:1、必须有两个或两个以上线程  2、必须是多个线程使用同一个锁

  弊端:每次执行时都要判断锁,浪费资源。

  普通同步函数的锁是this ,静态函数的锁是其所在文件字节码对象(.class)

      简单模拟售票代码:

 1 /*
 2 程序一共创建三个线程模拟三个售票窗口   ticket=100将票号设为1-100  我们希望票号不重复
 3 遗憾的是结果会出现票号为负数、相同票号的问题      也就是我们要讲到的线程不安全问题
 4 */
 5 public class SaleTicketUnsafe {
 6 
 7     public static void main(String[] args) {
 8 
 9         Ticket ticket=new Ticket();
10         
11         new Thread(ticket).start();
12         new Thread(ticket).start();
13         new Thread(ticket).start();
14         
15     }
16 
17 }
18 class Ticket implements Runnable{
19   
20    private int ticket=100;
21    public void run(){
22     while(true){
23         if(ticket>0){
24             try {
25                 Thread.sleep(10);//线程出错概率不是很大,所以要让线程休眠sleep()来将问题暴露出来
26             } catch (InterruptedException e) {
27                 // TODO Auto-generated catch block
28                 e.printStackTrace();
29             }
30         System.out.println(Thread.currentThread().getName()+"售票口出售---车票号:"+ticket--);
31         }
32     }
33         
34    }
35 }

     使用同步代码块的方式改进后的程序

   

 1 /*同步代码块的方式解决了问题*/
 2 public class SaleTicketSafe {
 3 
 4     public static void main(String[] args) {
 5         Tickets ticket=new Tickets();
 6         
 7         new Thread(ticket).start();
 8         new Thread(ticket).start();
 9         new Thread(ticket).start();
10     }
11 
12 }
13 class Tickets implements Runnable{
14     
15     private int ticket=100;
16     Object obj=new Object();
17     @Override
18     public void run() {
19         while(true){
20             synchronized(obj){//同步代码块
21             if(ticket>0){
22                 try {
23                     Thread.sleep(10);
24                     } catch (InterruptedException e) {
25                     // TODO Auto-generated catch block
26                     e.printStackTrace();
27                 }
28             System.out.println(Thread.currentThread().getName()+"售票口出售---车票号:"+ticket--);
29             }    
30             }
31         }
32     }
33     
34 }

四、使用同步会出现死锁情况(面试时会要求写一个死锁程序)

      问题:多线程各自持有不同的锁没释放,而又彼此需要对方的锁。

    原因:同步中嵌套同步,而它们的锁却不同

 一个简单的死锁程序:

 1 public class DieLock {
 2     
 3   public static void main(String[] args){
 4     //开启两个线程测试
 5     Thread t1=new Thread(new Test(true));
 6     t1.start();
 7     Thread t2=new Thread(new Test(false));    
 8     t2.start();    
 9    }
10 }
11 class Test implements Runnable{
12     boolean flag;
13     public Test(boolean f){
14         this.flag=f;
15     }
16     @Override
17     public void run() {
18         if(flag){
19             while(true){
20             synchronized(Lock.lockb){
21                 System.out.println("if locka");
22                 synchronized(Lock.locka){
23                     System.out.println("if lockb");    
24                 }
25             }
26             }
27         }else{
28             while(true){
29             synchronized(Lock.locka){
30                 System.out.println("else locka");
31                 synchronized(Lock.lockb){
32                     System.out.println("else lockb");    
33                 }
34             }
35             }
36         }        
37 }
38     //单独定义两个不同的锁
39 static class Lock{
40      static Object locka=new Object();
41      static Object lockb=new Object();
42 }        
43 }

五、单例设计模式中的多线程(面试重点)

       饿汉式、懒汉式(是线程不安全的,需要同步)

       将懒汉式同步有两种方式:

        1、下面代码中在getInstance方法前用synchronized修饰,通过同步函数来解决,每次都要同步比较消耗资源   

        2、就是注释中用同步块的方式,用双重判断(s==null)只需同步一次,避免多消耗资源,推荐使用

 1 public class Single{
 2     public static void main(String[] args){
 3         
 4         Singlel s=Singlel.getInstance();
 5         
 6     }
 7 }
 8 //懒汉式
 9  class Singlel extends Single{
10 
11      private Singlel(){}
12     private static Singlel s=null;
13     
14     public static synchronized Singlel getInstance(){
15         if(s==null)
16           s=new Singlel();
17 //        if(s==null){
18 //            synchronized(Singlel.class){    方法二、双重判断 ,提高效率
19 //                if(s==null)
20 //                    s=new Singlel();
21 //            }
22 //        }
23         return s;
24     }
25 }
26  //饿汉式
27  class Singlee extends Single{
28      private  Singlee(){}
29     private static Singlee s=new Singlee();
30     
31     public static Singlee getInstance(){
32         return s;
33     }
34     
35 }

       

       初学者难免错误,欢迎评判指教,持续更正ing...........

原文地址:https://www.cnblogs.com/blueFlowers/p/4948332.html