简介 
看起来是多个任务都在做,其实本质上我们的大脑在同一时间依旧只做了一件时间。 
充分利用道路,加入多个车道
在操作系统中运行的程序就是进程。一个进程可以有多个线程,如视频中同时听声音,看图像,看弹幕等等。
程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。进程则是执行程序的一次执行过程,是一个动态的概念。是系统资源分配的单位。通常一个进程中可以包含若干个线程,一个进程中至少有一个线程(main线程)。线程是CPU调度和执行的单位。 
很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器。如果模拟出来的多线程,即在一个cpu的情况下,在同一时间点,cpu只能执行一个代码,因为切换的很快,所以就有同时执行的错觉。 
每个线程在自己的工作区间内交互,内存控制不当会造成数据不一致 
线程创建 
继承Thread类 
线程不一定立即执行,CPU安排调度 
执行顺序是主线程调用线程之后不一定立即执行,由CPU安排调度。安排调度后是两线程个随机交叉执行  
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 public  class  TestThread01  extends  Thread     @Override      public  void  run ()                    for  (int  i = 0 ; i < 200 ; i++) {             System.out.println("我在看代码------"  + i);         }     }     public  static  void  main (String[] args)                             TestThread01 testThread01 = new  TestThread01();                  testThread01.start();         for  (int  i = 0 ; i < 1000 ; i++) {             System.out.println("我在学习多线程------"  + i);         }     } } 
1.创建lib项目,导入jar包
2.编写代码
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 public  class  TestThread02  extends  Thread      private  String url;     private  String name;     public  TestThread02 (String url, String name)           this .url = url;         this .name = name;     }     @Override      public  void  run ()           WebDownloader1 webDownloader1 = new  WebDownloader1();         webDownloader1.downloader(url, name);         System.out.println("下载了文件名为"  + name);     }     public  static  void  main (String[] args)           TestThread02 t1 = new  TestThread02("https://qiniu.codekylin.cn/github/img/img/%E5%AE%87%E8%88%AA%E5%91%98.png" , "1.png" );         TestThread02 t2 = new  TestThread02("https://qiniu.codekylin.cn/github/img/img/%E5%AE%87%E8%88%AA%E5%91%98.png" , "2.png" );         TestThread02 t3 = new  TestThread02("https://qiniu.codekylin.cn/github/img/img/%E5%AE%87%E8%88%AA%E5%91%98.png" , "3.png" );         t1.start();         t2.start();         t3.start();     } } class  WebDownloader1           public  void  downloader (String url, String name)           try  {             FileUtils.copyURLToFile(new  URL(url), new  File(name));         } catch  (IOException e) {             e.printStackTrace();             System.out.println("IO异常,downloader方法出现问题" );         }     } } 
实现Runnable接口 
执行顺序是主线程调用线程之后不一定立即执行,由CPU安排调度。安排调度后是两个随机交叉执行  
线程延迟增大了问题的发生概率
实现Callable接口 
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 public  class  TestThreadCallable  implements  Callable <Boolean >     private  String url;     private  String name;     public  TestThreadCallable (String url, String name)           this .url = url;         this .name = name;     }     @Override      public  Boolean call ()           WebDownloader1 webDownloader1 = new  WebDownloader1();         webDownloader1.downloader(url, name);         System.out.println("下载了文件名为"  + name);         return  true ;     }     public  static  void  main (String[] args)  throws  ExecutionException, InterruptedException          TestThreadCallable t1 = new  TestThreadCallable("https://qiniu.codekylin.cn/github/img/img/%E5%AE%87%E8%88%AA%E5%91%98.png" , "1.png" );         TestThreadCallable t2 = new  TestThreadCallable("https://qiniu.codekylin.cn/github/img/img/%E5%AE%87%E8%88%AA%E5%91%98.png" , "2.png" );         TestThreadCallable t3 = new  TestThreadCallable("https://qiniu.codekylin.cn/github/img/img/%E5%AE%87%E8%88%AA%E5%91%98.png" , "3.png" );                  ExecutorService es = Executors.newFixedThreadPool(3 );                  Future<Boolean> r1 = es.submit(t1);         Future<Boolean> r2 = es.submit(t2);         Future<Boolean> r3 = es.submit(t3);                  Boolean rs1 = r1.get();         Boolean rs2 = r2.get();         Boolean rs3 = r3.get();                  es.shutdown();     } } class  WebDownloader1           public  void  downloader (String url, String name)           try  {             FileUtils.copyURLToFile(new  URL(url), new  File(name));         } catch  (IOException e) {             e.printStackTrace();             System.out.println("IO异常,downloader方法出现问题" );         }     } } 
线程方法 
方法 
说明 
 
 
setPriority(int newPriority) 
更改线程的优先级 
 
static void sleep(long millis) 
在指定的毫秒数内让当前正在执行的线程休眠 
 
void join() 
等待该线程终止 
 
static void yield() 
暂停当前正在执行的线程对象,并执行其他线程 
 
void interrupt 
中断线程(不使用这个方式) 
 
boolean isAlive() 
测试线程是否处于活动状态 
 
线程停止 
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 public  class  TestStop  implements  Runnable           private  boolean  flag = true ;     @Override      public  void  run ()           int  i = 0 ;         while  (flag) {             System.out.println("run.....Thread"  + i++);         }     }          public  void  stop ()           this .flag = false ;     }     public  static  void  main (String[] args)           TestStop testStop = new  TestStop();         new  Thread(testStop).start();         for  (int  i = 0 ; i < 1000 ; i++) {             System.out.println("main" +i);             if  (i == 900 ) {                 testStop.stop();                 System.out.println("线程已经停止了" );             }         }     } } 
线程休眠 
sleep不会释放锁 
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 public  class  TestSleep2      public  static  void  main (String[] args)  throws  InterruptedException                   tenDown();     }          public  static  void  systemDate ()          Date startTime = new  Date(System.currentTimeMillis());         while  (true ){             try  {                 System.out.println(new  SimpleDateFormat("HH:mm:ss" ).format(startTime));                 Thread.sleep(1000 );                 startTime = new  Date(System.currentTimeMillis());             } catch  (InterruptedException e) {                 e.printStackTrace();             }         }     }          public  static  void  tenDown ()  throws  InterruptedException          int  num = 10 ;         while  (true ){             Thread.sleep(1000 );             System.out.println(num--);             if  (num<=0 ){                 break ;             }         }     } } 
线程礼让 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public  class  TestYield      public  static  void  main (String[] args)           MyYield yield = new  MyYield();         new  Thread(yield,"后裔" ).start();         new  Thread(yield,"嫦娥" ).start();     } } class  MyYield  implements  Runnable     @Override      public  void  run ()           System.out.println(Thread.currentThread().getName()+"线程开始执行" );         Thread.yield();         System.out.println(Thread.currentThread().getName()+"线程停止执行" );     } } 
线程强制执行 
线程状态 
线程是一个动态执行的过程,它也有一个从产生到死亡的过程。
下图显示了一个线程完整的生命周期。
新建状态:
使用 new  关键字和 Thread  类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start()  这个线程。
就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行 run() ,此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。 
同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。 
其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。 
 
死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
 
线程状态观测 
线程优先级 
优先级低只是意味着获得调度的概率低,并不会一定在优先级高之后被CPU调度。 
守护线程 
用户线程定义:平时用到的普通线程均是用户线程,当在Java程序中创建一个线程,它就被称为用户线程 
虚拟机必须确保用户线程执行完毕 
虚拟机不用等待守护线程执行完毕 
线程同步 多个线程操作同一个资源线程不安全 
线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用。 
不安全的买票案例 
同票:每个线程都有自己的工作空间,都与主存交互。将主存中的数据拷贝到工作区之后,还没或者正要进行将对数据的修改返回给主存时,此时的cpu调度了另一个线程对其进行同样的操作。导致拿到了同一张票。线程切换带来的原子性问题 
负票:在票的临界处也就是最后一张票的时候,A线程被CPU调度执行进行买票,A线程延迟100毫秒。在这100毫秒的时间间隔里,线程B被CPU调度执行进行买票。此时的票数还是为1 
没有return出去。然后B线程延迟,A线程延迟过后票数-1此时为0票。B线程延迟过后买票,票数-1此时0-1等于-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 public  class  UnsafeBuyTicket      public  static  void  main (String[] args)           BuyTicket ticket = new  BuyTicket();         new  Thread(ticket, "苦逼的我" ).start();         new  Thread(ticket, "努力的你" ).start();         new  Thread(ticket, "可恶的黄牛" ).start();     } } class  BuyTicket  implements  Runnable           private  int  tickeNums = 10 ;     boolean  flag = true ;     @Override      public  void  run ()                    while  (flag) {             try  {                 buy();             } catch  (InterruptedException e) {                 e.printStackTrace();             }         }     }          private  void  buy ()  throws  InterruptedException                   if  (tickeNums <= 0 ) {             flag = false ;             return ;         }                  Thread.sleep(100 );                  System.out.println(Thread.currentThread().getName() + "拿到"  + tickeNums-- + "票" );     } } 
不安全的银行取钱 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 public  class  UnsafeBank      public  static  void  main (String[] args)           Account account = new  Account(100 , "积蓄" );         Drawing you = new  Drawing(account, 50 , "你" );         Drawing girlFriend = new  Drawing(account, 100 , "女朋友" );         you.start();         girlFriend.start();     } } class  Account     int  money;     String name;     public  Account (int  money, String name)           this .money = money;         this .name = name;     } } class  Drawing  extends  Thread     Account account;          int  drawingMoney;          int  nowMoney;     public  Drawing (Account account,int  drawingMoney,String name)          super (name);         this .account = account;         this .drawingMoney= drawingMoney;     }          @Override      public  void  run ()               if  (account.money-drawingMoney<0 ){                 System.out.println(Thread.currentThread().getName()+"钱不够,取不了" );                 return ;             }             try  {                 Thread.sleep(1000 );             } catch  (InterruptedException e) {                 e.printStackTrace();             }                          account.money = account.money-drawingMoney;                          nowMoney = nowMoney + drawingMoney;             System.out.println(account.name+"余额为:" +account.money);                          System.out.println(this .getName()+"手里的钱:" +nowMoney);         } } 
案例:女朋友取款100,你取款50。银行卡余额100,却出现你两都取钱成功,而余额为负数的结果。
出现负数的解释同买票出现负数原因一样。在对余额进行操作时A线程延迟,此时B线程被CPU调度执行取钱,延迟。A线程取钱成功余额减去对应的数目(假设取钱50:100-50=50),B线程进行取钱成功余额减去对应的数目(假设取钱100:50-100=-50)。 
线程不安全的集合 
集合个数本来应该是10000的,出现了少了1个的原因是。当某个线程正在执行向集合添加元素的时候。同一时间CPU调度执行了另一个线程向集合添加元素,添加成功。此时刚才那个线程也向集合中添加元素,在list的同一位置,旧的元素值被新的覆盖。买票同票问题 
同步方法 
方法里面也有分只读和修改的代码块,这时候使用同步方法会浪费资源 
改造不安全买票,使用synchronized 
同步方法的同步监视器是默认是this 
同步块 
解决不安全的银行取钱案例。首先我们还是使用同步方法
说明我们并没有锁正确。原因是因为同步方法默认锁的是this对象,在这里也就是Drawing对象。而我们操控的是账户对象的值,所以导致并没有起作用。
使用同步块,对account对象进行锁定
还可以对当前账户的余额进行判断,从而优化我们的代码
解决线程不安全的集合。
同步是高开销的操作,因此尽量减少同步的内容。通常没有必要同步整个方法,同步部分代码块即可。
死锁 
多个线程互相抱着对方需要的资源同时又请求对方的资源,然后形成僵持 
程序一直在运行,僵持
如何解决死锁?很简单我们请求对方的资源的同时,放下手里的资源
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 public  class  TestDeadLock      public  static  void  main (String[] args)           MakeUp g1 = new  MakeUp(0 , "灰姑凉" );         MakeUp g2 = new  MakeUp(1 , "白雪公主" );         g1.start();         g2.start();     } } class  Lipstick  } class  Mirror  } class  MakeUp  extends  Thread           static  Lipstick lipstick = new  Lipstick();     static  Mirror mirror = new  Mirror();     int  choice;      String girlName;     MakeUp(int  choice, String girlName) {         this .choice = choice;         this .girlName = girlName;     }     @Override      public  void  run ()           try  {             makeup();         } catch  (InterruptedException e) {             e.printStackTrace();         }     }     private  void  makeup ()  throws  InterruptedException          if  (choice == 0 ) {             synchronized  (lipstick) {                 System.out.println(this .girlName + "获得口红的锁" );                 Thread.sleep(1000 );             }             synchronized  (mirror) {                 System.out.println(this .girlName + "获得镜子的锁" );                 Thread.sleep(1000 );             }         } else  {             synchronized  (mirror) {                 System.out.println(this .girlName + "获得镜子的锁" );                 Thread.sleep(1000 );             }             synchronized  (lipstick) {                 System.out.println(this .girlName + "获得口红的锁" );             }         }     } } 
Lock锁 
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 public  class  TestLock      public  static  void  main (String[] args)           TestLock2 testLock2 = new  TestLock2();         new  Thread(testLock2).start();         new  Thread(testLock2).start();         new  Thread(testLock2).start();     } } class  TestLock2  implements  Runnable      int  ticketNums = 10 ;          private  final  ReentrantLock lock = new  ReentrantLock();     @Override      public  void  run ()           while  (true ) {             try  {                 lock.lock();                 if  (ticketNums > 0 ) {                     try  {                         Thread.sleep(1000 );                     } catch  (InterruptedException e) {                         e.printStackTrace();                     }                     System.out.println(ticketNums--);                 } else  {                     break ;                 }             } finally  {                 lock.unlock();             }         }     } } 
线程协作 
方法名 
作用 
 
 
wait() 
表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁 
 
wait(long timeout) 
指定等待的毫秒数 
 
notify() 
唤醒一个处于等待状态的线程 
 
notifyAll() 
唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度 
 
管程法 
生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据 
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 public  class  TestPC      public  static  void  main (String[] args)           SynContainer container = new  SynContainer();         new  Product(container).start();         new  Consumer(container).start();     } } class  Product  extends  Thread      SynContainer container;     public  Product  (SynContainer container)          this .container=container;     }          @Override      public  void  run ()           for  (int  i = 0 ; i < 100 ; i++) {             container.push(new  Chicken(i));             System.out.println("生产了" +1 +"只鸡" );         }     } } class  Consumer  extends  Thread      SynContainer container;     public  Consumer  (SynContainer container)          this .container=container;     }          @Override      public  void  run ()           for  (int  i = 0 ; i < 100 ; i++) {             Chicken chicken = container.pop();             System.out.println("消费了" +1 +"只鸡" );         }     } } class  Chicken      int  id;     public  Chicken (int  id)           this .id = id;     } } class  SynContainer           Chicken[] chickens = new  Chicken[10 ];          int  count = 0 ;          public  synchronized  void  push (Chicken chicken)                    if  (count == chickens.length) {                          try  {                 this .wait();             } catch  (InterruptedException e) {                 e.printStackTrace();             }         }                  chickens[count] = chicken;         count++;                  this .notifyAll();     }          public  synchronized  Chicken pop ()                   if  (count==0 ){                          try  {                 this .wait();             } catch  (InterruptedException e) {                 e.printStackTrace();             }         }                  count--;         Chicken chicken = chickens[count];                  this .notifyAll();         return  chicken;     } } 
信号灯法 
通过标志位来判断进行操作和等待操作 
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 public  class  TestPC2      public  static  void  main (String[] args)           TV tv = new  TV();         new  Player(tv).start();         new  Watcher(tv).start();     } } class  Player  extends  Thread      TV tv;     public  Player (TV tv)           this .tv = tv;     }     @Override      public  void  run ()           for  (int  i = 0 ; i < 20 ; i++) {             if  (i % 2  == 0 ) {                 this .tv.play("快乐大本营播放中" );             } else  {                 this .tv.play("抖音记录美好生活" );             }         }     } } class  Watcher  extends  Thread     TV tv;     public  Watcher (TV tv)          this .tv=tv;     }     @Override      public  void  run ()           for  (int  i = 0 ; i < 20 ; i++) {             tv.watch();         }     } } class  TV                String voice;     boolean  flag = true ;          public  synchronized  void  play (String voice)           if  (!flag) {                          try  {                 this .wait();             } catch  (InterruptedException e) {                 e.printStackTrace();             }         }         System.out.println("演员表演了:"  + voice);                  this .notifyAll();         this .voice = voice;         this .flag = !this .flag;     }          public  synchronized  void  watch ()           if  (flag) {                          try  {                 this .wait();             } catch  (InterruptedException e) {                 e.printStackTrace();             }         }         System.out.println("观看了"  + voice);                  this .notifyAll();         this .flag = !this .flag;     } } 
线程池 
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 import  java.util.concurrent.ExecutorService;import  java.util.concurrent.Executors;public  class  TestPool      public  static  void  main (String[] args)                             ExecutorService service = Executors.newFixedThreadPool(10 );                  service.execute(new  MyThread());         service.execute(new  MyThread());         service.execute(new  MyThread());         service.execute(new  MyThread());                  service.shutdown();     } } class  MyThread  implements  Runnable     @Override      public  void  run ()               System.out.println(Thread.currentThread().getName());     } }