线程 
概念 
线程是进程中的单个顺序执行流,是一条执行路径。一个进程如果只有一条执行路径,则成为单线程程序;而如果有多条执行路径,则成为多线程程序。
CPU分时调度 
时间片,即CPU分配给各个程序的时间,每个进程被分配一个时间段,称作它的时间片。即该进程允许运行的时间,使各个程序从表面上看是同时进行的。
如果在时间片结束时进程还在运行,则CPU将被剥夺并分配给另一个进程,将当前进程挂起。如果进程在时间片结束前阻塞或结束,则CPU当即进行切换,而不会造成CPU资源浪费。当又切换到之前执行的进程,把现场恢复,继续执行。
在宏观上:我们可以同时打开多个应用程序,每个程序并行,同时运行。
在微观上:由于只有一个CPU,一次只能处理程序要求的一部分,如何处理公平,一种方法就是引入时间片,每个程序轮流执行。多核提高了并发能力。
单核CPU和多核CPU(了解): 
单核CPU同时只能干一件事情,当启动多个程序时,CPU快速切换轮流处理每个程序,但如果CPU不够强劲,同时排队等待处理的任务太多,就会感觉系统会有延时、反应慢、卡顿等,甚至某一个程序在处理时出现错误,无法响应了,会造成后面排队的其他任务只能等待。
多核CPU是在基板上集成多个单核CPU+任务分配系统,两个核心同时处理两份任务,相比单核执行速度更快,有利于同时运行多个程序,不容易造成卡顿,更流畅!
Java程序的运行过程: 
通过eclipse(java命令)运行一个Java程序,java命令会启动JVM(java虚拟机),等于启动了一个应用程序,也就是启动了一个进程。 
该进程会自动启动一个“主线程”,然后主线程去调用某个类的 main 方法,所以 main 方法运行在主线程中。在此之前的所有程序代码都是按照顺序依次调用的,都是单线程的。 
 
如果希望程序中实现多段程序代码交替执行的效果,则需要创建多线程程序。
为什么要使用多线程?
单线程程序执行时都只有一条顺序执行流,如果程序执行某行代码时遇到了阻塞(比如:抛异常),那么程序的运行将会停滞在这一行,其他代码将会无法执行!
 
这就像去银行办理业务,只有1个业务窗口(单线程),所有的客户都需要在一个窗口排队办理业务,如果业务员在为某一个客户办理业务时,花费了很长时间,那么将会导致后面的客户等待很长时间,这样处理业务的效率也是非常低的。
 
但如果银行为了提高效率,同时开放了5个窗口(多线程),客户可以分布在这5个窗口分别办理业务,即使某一个窗口在为个别客户办理业务时花费了很长时间,但不影响其他窗口办理业务的进度。
 
 
多线程理解起来其实非常简单:
单线程的程序只有一个顺序执行流。 
多线程的程序则可以包括多个顺序执行流,多个顺序流之间互不干扰。 
 
[并行]和[并发]的区别: (这是两个概念) 
并行执行指在同一时刻,有多条指令在多个处理器上同时执行; 
并发执行指在同一时刻只能有一条指令执行,但多个进程指令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果。 
 
多线程的特性 
随机性 
多线程的程序在执行时,在某一时间点具体执行哪个线程是不确定的,可以确定的是某一个时间点只有一个线程在执行(单核CPU)。
虽然我们感觉这些线程像是在同时运行,实际上是因为CPU在快速切换轮流执行这些线程,由于切换速度是纳秒级别的,所以我们感觉不到。
如何实现多线程 
由于线程是依赖进程存在的,因此首先需要创建一个进程,但进程是操作系统创建的,而Java程序是不能直接调用系统功能的。但Java可以去调用C或C++写好的程序去创建进程从而实现多线程程序。
在Java中要想实现多线程操作有两种方式,一种是继承Thread类,另一种是实现Runnable接口。接下来针对这两种创建多线程的方式分别进行讲解。
实现多线程1: 继承Thread类 
Thread类概述 
Thread类是在java.lang包中定义的类
JavaSE规范中规定,一个类只要继承了Thread类,此类就是多线程的子类。
在Thread子类中,必须重写该类的run()方法,此方法为线程的主体。
通过继承Thread类创建线程 
接下来通过案例演示<通过继承Thread类的方式实现多线程>
下面创建线程,并测试主线程和创建的新线程(子线程)交替执行的效果
代码实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package  thread; public  class  ThreadDemo1  {    public  static  void  main (String[] args)  {         MyThread1  t1  =  new  MyThread1 ();         MyThread2  t2  =  new  MyThread2 ();                  t1.start();         t2.start();     } } class  MyThread1  extends  Thread {         @Override      public  void  run ()  {         for (int  i=0 ;i<1000 ;i++){             System.out.println("你是谁啊?" );         }     } } class  MyThread2  extends  Thread {         @Override      public  void  run ()  {         for  (int  i  =  0 ; i < 1000 ; i++) {             System.out.println("开门,我是查水表的!" );         }     } } 
 
从上面的运行结果可以看出,main方法(主线程)和run方法(子线程)中的两个for循环中的输出语句交替执行了,说明通过集成Thread类实现了多线程。
(如果没有测试出主线程和子线程交替执行的效果,可以多测试几次!)
实现多线程2: 实现Runnable接口 
Runnable接口概述 
通过继承Thread类实现了多线程,但是这种方式有一定的局限性。因为Java中只支持单继承,一个类一旦继承了某个父类就无法再继承Thread类,例如猫类Cat继承了动物类Animal,就无法通过继承Thread类实现多线程。
为了克服这种弊端,在Thread类中提供了两个构造方法:
public Thread(Runnable target)
public Thread(Runnable target,String name)
这两个构造方法都可以接收Runnable的子类实例对象,这样创建的线程将调用实现了Runnable接口类中的run()方法作为运行代码,而不需要调用Thread类的run()方法,所以就可以依靠Runnable接口的实现类启动多线程。
通过实现Runnable接口实现多线程 
接下来通过案例演示<通过实现Runnable接口的方式实现多线程>
下面创建线程,并测试主线程和创建的新线程(子线程)交替执行的效果
代码实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package  thread; public  class  ThreadDemo2  {    public  static  void  main (String[] args)  {                  MyRunnable1  r1  =  new  MyRunnable1 ();         MyRunnable2  r2  =  new  MyRunnable2 ();                  Thread  t1  =  new  Thread (r1);         Thread  t2  =  new  Thread (r2);                  t1.start();         t2.start();     } } class  MyRunnable1  implements  Runnable {    @Override      public  void  run ()  {         for  (int  i  =  0 ; i < 1000 ; i++) {             System.out.println("hello~大姐姐~~" );         }     } } class  MyRunnable2  implements  Runnable {    @Override      public  void  run ()  {         for  (int  i  =  0 ; i < 1000 ; i++) {             System.out.println("来了~老六~~" );         }     } } 
 
从上面的运行结果可以看出,main方法(主线程)和run方法(子线程)中的两个for循环中的输出语句交替执行了,说明实现Runnable接口同样也实现了多线程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package  thread; public  class  ThreadDemo3  {    public  static  void  main (String[] args)  {                  Thread  t1  =  new  Thread () {             @Override              public  void  run ()  {                 for  (int  i  =  0 ; i < 1000 ; i++) {                     System.out.println("who are you~~" );                 }             }         };         t1.start();                  Runnable  r1  =  () -> {             for  (int  i  =  0 ; i < 1000 ; i++) {                 System.out.println("i'm 老六~" );             }         };         Thread  t2  =  new  Thread (r1);         t2.start();     } } 
 
Thread的常用方法和总结 
CurrentThreadDemo 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package  thread; public  class  CurrentThreadDemo  {    public  static  void  main (String[] args)  {         Thread  main  =  Thread.currentThread();         doSome();                           System.out.println("主线程:" +main);         new  Thread ("无敌霸王鸡" ){             @Override              public  void  run ()  {                 Thread  t  =  Thread.currentThread();                 System.out.println(t);                 doSome();             }         }.start();     }     public  static  void  doSome () {         Thread  t  =  Thread.currentThread();         System.out.println("运行doSome方法的线程:" +t);     } } 
 
ThreadInfoDemo 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package  thread; public  class  ThreadInfoDemo  {    public  static  void  main (String[] args)  {         Thread  main  =  Thread.currentThread();         String  name  =  main.getName();         System.out.println("名字:" +name);         long  id  =  main.getId();         System.out.println("id:" +id);         int  priority  =  main.getPriority();         System.out.println("优先级:" +priority);          boolean  alive  =  main.isAlive();         boolean  daemon  =  main.isDaemon();         boolean  interrupted  =  main.isInterrupted();         System.out.println("是否活着:" +alive);         System.out.println("是否为守护线程:" +daemon);         System.out.println("是否被中断:" +interrupted);     } } 
 
PriorityDemo 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package  thread; public  class  PriorityDemo  {    public  static  void  main (String[] args)  {         Thread  min  =  new  Thread (){             public  void  run ()  {                 for  (int  i  =  0 ; i < 10000 ; i++) {                     System.out.println("皇帝来找min啦" );                 }             }         };         Thread  norm  =  new  Thread (){             public  void  run ()  {                 for  (int  i  =  0 ; i < 10000 ; i++) {                     System.out.println("皇帝来找norm啦" );                 }             }         };         Thread  max  =  new  Thread (){             public  void  run ()  {                 for  (int  i  =  0 ; i < 10000 ; i++) {                     System.out.println("皇帝来找max啦" );                 }             }         };                  min.setPriority(Thread.MIN_PRIORITY);         max.setPriority(10 );         min.start();         norm.start();         max.start();     } } 
 
进程 
什么是进程 
进程是操作系统中运行得到一个任务(一个应用程序运行在一个进程中).
进程(process)是一块包含了某些资源的内存区域.操作同利用进程把它的工作划分为一些功能单元.
进程中所包含的一个或者多个执行单元称为线程(thread).进程拥有一个私有的虚拟地址空间,该空间仅能被他所包含的线程访问
线程只能归属于一个进程并且他只能访问该进程所拥有的资源.当操作系统创建一个进程后,该进程会自动申请一个主线程或者首要线程的线程
线程状态 
概述  
新建状态(New):当一个线程对象被创建后,线程就处于新建状态。在新建状态中的线程对象从严格意义上看还只是一个普通的对象,还不是一个独立的线程,不会被线程调度程序调度。新建状态是线程生命周期的第一个状态。 
 
例如: Thread t = new MyThread();
就绪状态(Runnable):处于新建状态中的线程被调用start()方法就会进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,但并不是说执行了start()方法线程就会立即执行; 
 
另外,在等待/阻塞状态中的线程,被解除等待和阻塞后将不直接进入运行状态,而是首先进入就绪状态
运行状态(Running):处于就绪状态中的线程一旦被系统选中,使线程获取了 CPU 时间,就会进入运行状态。 
线程在运行状态下随时都可能被调度程序调度回就绪状态。在运行状态下还可以让线程进入到等待/阻塞状态。 
在通常的单核CPU中,在同一时刻只有一个线程处于运行状态。在多核的CPU中,就可能两个线程或更多的线程同时处于运行状态,这也是多核CPU运行速度快的原因。 
 
注:就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,必须先处于就绪状态中;
阻塞状态(Blocked):根据阻塞产生的原因不同,阻塞状态又可以分为三种 
a)等待阻塞:运行状态中的线程执行wait()方法,使当前线程进入到等待阻塞状态; 
b)锁阻塞:线程在获取synchronized同步锁失败(因为锁被其它线程所占用),线程会进入同步阻塞状态; 
c)其他阻塞:通过调用线程的sleep(),suspend(), join(), 或发出了I/O请求时等,线程会进入到阻塞状态。当sleep()睡眠结束、调用resume(),?join()等待的线程终止或者超时、或I/O处理完毕时,线程重新转入就绪状态。
 
死亡状态(Dead):当线程中的run方法执行结束后,或者程序发生异常终止运行后,线程会进入死亡状态。处于死亡状态的线程不能再使用 start 方法启动线程。
 
 
SleepDemo 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 package  thread; import  javafx.scene.transform.Scale; import  java.util.Scanner; public  class  SleepDemo  {    public  static  void  main (String[] args)  {         System.out.println("程序开始了!" );                  Scanner  scanner  =  new  Scanner (System.in);         System.out.println("请输入一个数字:" );         try  {             for  (int  num  =  scanner.nextInt(); num > 0 ; num--) {                 System.out.println(num);                 Thread.sleep(1000 );             }         } catch  (InterruptedException e) {             e.printStackTrace();         }          System.out.println("程序结束了!" );     } } **SleepDemo2** ```JAVA package  thread; public  class  SleepDemo2  {    public  static  void  main (String[] args)  {         Thread  lin  =  new  Thread (){             @Override              public  void  run ()  {                 System.out.println("林:刚美完容~睡一会吧~~" );                 try  {                     Thread.sleep(500000000 );                     System.out.println("林:睡醒了!" );                 } catch  (InterruptedException e) {                     System.out.println("林:干嘛呢!干嘛呢!都破了相了!" );                 }              }         };         Thread  huang  =  new  Thread (){             @Override              public  void  run ()  {                 System.out.println("黄:大锤80,小锤40,开始砸墙!" );                 for  (int  i  =  0 ; i < 5 ; i++) {                     System.out.println("黄:80!" );                     try  {                         Thread.sleep(1000 );                     } catch  (InterruptedException e) {                         e.printStackTrace();                     }                 }                 System.out.println("咣当!" );                 System.out.println("黄:大哥!搞定!" );                 lin.interrupt();             }         };         lin.start();         huang.start();     } } 
 
DaemonThreadDemo 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package  thread; public  class  DaemonThreadDemo  {    public  static  void  main (String[] args)  {         Thread  rose  =  new  Thread (){             @Override              public  void  run ()  {                 for  (int  i  =  0 ; i < 5 ; i++) {                     System.out.println("rose:let me go!" );                     try  {                         Thread.sleep(1000 );                     } catch  (InterruptedException e) {                         e.printStackTrace();                     }                 }                 System.out.println("rose:aaaaaaaaaa~~~" );                 System.out.println("扑通!!" );             }         };         Thread  jack  =  new  Thread (){             @Override              public  void  run ()  {                 while  (true ){                     System.out.println("jack:you jump! i jump!" );                     try  {                         Thread.sleep(1000 );                     } catch  (InterruptedException e) {                         e.printStackTrace();                     }                 }             }         };         rose.start();         jack.setDaemon(true );         jack.start();         while  (true );     } } 
 
线程的同步和异步 
JoinDemo 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 package  thread; public  class  JoinDemo  {    static  boolean  isFinish  =  false ;     public  static  void  main (String[] args)  {         Thread  download  =  new  Thread () {             @Override              public  void  run ()  {                 System.out.println("down:开始下载图片..." );                 for  (int  i  =  0 ; i <= 100 ; i++) {                     System.out.println("down:"  + i + "%" );                     try  {                         Thread.sleep(50 );                     } catch  (InterruptedException e) {                         e.printStackTrace();                     }                 }                 System.out.println("down:图片下载完毕!!!" );                 isFinish = true ;              }         };         Thread  show  =  new  Thread (){             @Override              public  void  run ()  {                 System.out.println("show:开始显示文字..." );                 try  {                     Thread.sleep(2000 );                 } catch  (InterruptedException e) {                     e.printStackTrace();                 }                 System.out.println("show:显示文字完毕!!!" );                                  try  {                                          download.join();                 } catch  (InterruptedException e) {                     e.printStackTrace();                 }                 System.out.println("show:开始显示图片..." );                 if  (!isFinish){                     throw  new  RuntimeException ("图片加载失败!!!" );                 }                 System.out.println("show:图片显示完毕!!!" );             }         };         download.start();         show.start();     } } 
 
同步锁 
同步方法 
同步方法概述 
除了可以将需要的代码设置成同步代码块以外,也可以使用synchronized关键字将一个方法修饰成同步方法,它能实现和同步代码块同样的功能。
具体语法格式如下:
1 2 3 权限修饰符 synchronized  返回值类型/void  方法名([参数1 ,...]){   需要同步的代码; } 
 
被synchronized修饰的方法在某一时刻只允许一个线程访问,访问该方法的其他线程都会发生阻塞,直到当前线程访问完毕后,其他线程才有机会执行该方法。
需要注意的是,同步方法的锁是当前调用该方法的对象,也就是this指向的对象。
同步方法的使用 
下面使用同步方法解决上面的多线程售票程序中的线程安全问题
需要注意的是:
将有可能发生线程安全问题的方法使用synchronized修饰,同一时间只允许一个线程进入同步方法中
 
synchronized方法的锁对象是当前调用该方法的对象,也就是this指向的对象。
 
 
如果当前方法是非静态方法,this表示的是调用当前方法的对象
如果当前方法的静态方法,this表示的是当前类。
示例1: SyncDemo1 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package  thread; public  class  SyncDemo1  {    public  static  void  main (String[] args)  {         Table  table  =  new  Table ();         Thread  t1  =  new  Thread ("小红" ){             @Override              public  void  run ()  {                 while  (true ){                     int  bean  =  table.getBean();                     Thread.yield ();                     System.out.println(getName()+"抢了一颗豆子,还剩:" +bean);                 }             }         };         Thread  t2  =  new  Thread ("小蓝" ){             @Override              public  void  run ()  {                 while  (true ){                     int  bean  =  table.getBean();                     Thread.yield ();                     System.out.println(getName()+"抢了一颗豆子,还剩:" +bean);                 }             }         };         t1.start();         t2.start();     } } class  Table {    private  int  beans  =  20 ;           public  synchronized  int  getBean () {         if  (beans <= 0 ){             throw  new  RuntimeException ("没有豆子了!!!" );         }         Thread.yield ();         return  beans--;     } } 
 
同步代码块 
同步代码块概述 
同步是指多个操作在同一个时间段内只能有一个线程进行,其他线程要等待此线程完成之后才可以继续执行。
Java为线程的同步操作提供了synchronized关键字,使用该关键字修饰的代码块被称为同步代码块。
其语法格式如下:
1 2 3 synchronized ( 同步对象 ){  需要同步的代码; } 
 
注意: 在使用同步代码块时必须指定一个需要同步的对象,也称为锁对象,这里的锁对象可以是任意对象。但多个线程必须使用同一个锁对象。
同步代码块的使用 
下面使用同步代码块解决上面的多线程售票程序中的线程安全问题
需要注意的是:
将有可能发生线程安全问题的代码包含在同步代码块中,同一时间只允许一个线程进入同步代码块中
 
synchronized代码块中的锁对象可以是任意对象,但必须只能是一个锁。
 
 
若使用this作为锁对象,需保证多个线程执行时,this指向的是同一个对象
**示例1: **
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package  thread; public  class  SyncDemo2  {    public  static  void  main (String[] args)  {         Shop  shop1  =  new  Shop ();         Shop  shop2  =  new  Shop ();         Thread  t1  =  new  Thread ("康荐文" ){             public  void  run ()  {                  shop1.buy();             }         };         Thread  t2  =  new  Thread ("薛亚杰" ){             public  void  run ()  {                 shop2.buy();             }         };         t1.start();         t2.start();     } } class  Shop {         public  void  buy () {         Thread  t  =  Thread.currentThread();         try  {             System.out.println(t.getName()+":正在挑衣服..." );             Thread.sleep(5000 );                          synchronized  (this ){                 System.out.println(t.getName()+":正在试衣服..." );                 Thread.sleep(5000 );             }             System.out.println(t.getName()+":正在结账离开..." );         } catch  (InterruptedException e) {         }     } } 
 
静态同步方法 
示例1: SyncDemo3 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package  thread; public  class  SyncDemo3  {    public  static  void  main (String[] args)  {         Boo  b1  =  new  Boo ();         Boo  b2  =  new  Boo ();         Thread  t1  =  new  Thread (){             @Override              public  void  run ()  {                 b1.doSome();             }         };         Thread  t2  =  new  Thread (){             @Override              public  void  run ()  {                 b2.doSome();             }         };         t1.start();         t2.start();     } } class  Boo  {              public  static  void  doSome () {         synchronized  (Boo.class){             Thread  t  =  Thread.currentThread();             System.out.println(t.getName()+":正在执行doSome方法..." );             try  {                 Thread.sleep(5000 );             } catch  (InterruptedException e) {                 e.printStackTrace();             }             System.out.println(t.getName()+":执行doSome方法完毕!!!" );         }     } } 
 
互斥锁 
示例:SyncDemo4 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package  thread; public  class  SyncDemo4  {    public  static  void  main (String[] args)  {         Foo  foo  =  new  Foo ();         Thread  t1  =  new  Thread (){             @Override              public  void  run ()  {                 foo.methodA();             }         };         Thread  t2  =  new  Thread (){             @Override              public  void  run ()  {                 foo.methodB();             }         };         t1.start();         t2.start();     } } class  Foo  {    public  synchronized  void  methodA () {         Thread  t  =  Thread.currentThread();         System.out.println(t.getName()+":正在执行A方法..." );         try  {             Thread.sleep(5000 );         } catch  (InterruptedException e) {             e.printStackTrace();         }         System.out.println(t.getName()+":执行A方法完毕..." );     }     public  synchronized  void  methodB () {         Thread  t  =  Thread.currentThread();         System.out.println(t.getName()+":正在执行B方法..." );         try  {             Thread.sleep(5000 );         } catch  (InterruptedException e) {             e.printStackTrace();         }         System.out.println(t.getName()+":执行B方法完毕..." );     } } 
 
死锁 
示例:DeadLockDemo 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 package  thread; public  class  DeadLockDemo  {    static  Object  chopsticks  =  new  Object ();     static  Object  spoon  =  new  Object ();     public  static  void  main (String[] args)  {         Thread  bf  =  new  Thread () {             @Override              public  void  run ()  {                 try  {                     System.out.println("北方人准备吃饭..." );                     synchronized  (chopsticks) {                         System.out.println("北方人拿筷子,开始吃饭" );                         Thread.sleep(5000 );                         System.out.println("北方人吃完了饭,去拿勺" );                         synchronized  (spoon){                             System.out.println("北方人拿勺,开始喝汤" );                             Thread.sleep(5000 );                             System.out.println("北方人喝完了汤" );                         }                         System.out.println("北方人放下勺" );                     }                     System.out.println("北方人放下筷子,吃饭完毕!" );                 } catch  (InterruptedException e) {                     e.printStackTrace();                 }             }         };         Thread  nf  =  new  Thread () {             @Override              public  void  run ()  {                 try  {                     System.out.println("南方人准备吃饭..." );                     synchronized  (spoon) {                         System.out.println("南方人拿勺,开始喝汤" );                         Thread.sleep(5000 );                         System.out.println("南方人喝完了汤,去拿筷子" );                         synchronized  (chopsticks){                             System.out.println("南方人拿起筷子,开始吃饭" );                             Thread.sleep(5000 );                             System.out.println("南方人吃完了饭" );                         }                         System.out.println("南方人放下筷子" );                     }                     System.out.println("南方人放下勺,吃饭完毕!" );                 } catch  (InterruptedException e) {                     e.printStackTrace();                 }             }         };         nf.start();         bf.start();     } } **DeadLockDemo1** ```JAVA package  thread; public  class  DeadLockDemo1  {    static  Object  chopsticks  =  new  Object ();     static  Object  spoon  =  new  Object ();      public  static  void  main (String[] args)  {         Thread  bf  =  new  Thread () {             @Override              public  void  run ()  {                 try  {                     System.out.println("北方人准备吃饭..." );                     synchronized  (chopsticks) {                         System.out.println("北方人拿筷子,开始吃饭" );                         Thread.sleep(5000 );                         System.out.println("北方人吃完了饭" );                     }                     System.out.println("北方人放下筷子,去拿勺" );                     synchronized  (spoon) {                         System.out.println("北方人拿勺,开始喝汤" );                         Thread.sleep(5000 );                         System.out.println("北方人喝完了汤" );                     }                     System.out.println("北方人放下勺" );                     System.out.println("吃饭完毕!" );                 } catch  (InterruptedException e) {                     e.printStackTrace();                 }             }         };         Thread  nf  =  new  Thread () {             @Override              public  void  run ()  {                 try  {                     System.out.println("南方人准备吃饭..." );                     synchronized  (spoon) {                         System.out.println("南方人拿勺,开始喝汤" );                         Thread.sleep(5000 );                         System.out.println("南方人喝完了汤" );                     }                     System.out.println("南方人放下勺,去拿筷子" );                     synchronized  (chopsticks) {                         System.out.println("南方人拿起筷子,开始吃饭" );                         Thread.sleep(5000 );                         System.out.println("南方人吃完了饭" );                     }                     System.out.println("南方人放下筷子" );                      System.out.println("吃饭完毕!" );                 } catch  (InterruptedException e) {                     e.printStackTrace();                 }             }         };         nf.start();         bf.start();     } }