线程、进程、多线程
进程:进程是程序的一次执行过程,是一个动态的概念,是系统资源分配的单位
线程:通常在一个进程中可以包含若干个线程,一个进程中至少有一个线程,不然没有存在的意义,线程是CPU调度和执行的单位
多线程:真正的多线程是有多个CUP,同时执行,如果在只有一个CPU的情况下,同一时间只能执行一个代码,因为切换速度很快,造成了同时执行的假象
线程就是独立的执行路径
在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程、gc线程
main()称为主线程,为系统入口,用于执行整个程序
在一个线程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不可人为干预的
对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制
线程会带来额外的开销,如CPU调度时间(排队时间),并发控制开销
每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
线程的实现 方式一:继承Thread类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class Test extends Thread { @Override public void run () { for (int i=0 ; i<100 ; i++) { System.out.println("我在吃饭-------------------" ); } } public static void main (String[] args) throws IOException { Test test = new Test(); test.start(); for (int i=0 ; i<1000 ;i++) { System.out.println("我在睡觉" ); } } }
方式二:继承Runnable接口实现(常用) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class Test implements Runnable { @Override public void run () { for (int i=0 ; i<100 ; i++) { System.out.println("我在吃饭-------------------" ); } } public static void main (String[] args) throws IOException { Test test = new Test(); new Thread(test).start(); for (int i=0 ; i<1000 ;i++) { System.out.println("我在睡觉" ); } } }
方式三:继承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 public class Test implements Callable <Boolean > { @Override public Boolean call () throws Exception { for (int i=0 ; i<100 ; i++) { System.out.println("我在吃饭-------------------" ); } return true ; } public static void main (String[] args) throws ExecutionException, InterruptedException { Test test = new Test(); ExecutorService ser = Executors.newFixedThreadPool(1 ); Future<Boolean> result = ser.submit(test); ser.shutdownNow(); for (int i=0 ; i<1000 ;i++) { System.out.println("我在睡觉" ); } } }
初识并发问题 并发问题:多个线程操作同时操作共享数据所导致的
Demo:抢票 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 public class Test implements Runnable { private int tecikNums = 10 ; @Override public void run () { while (true ) { if (tecikNums != 0 ) { try { tecikNums--; System.out.println(Thread.currentThread().getName() + "==>" + "拿到了第" + tecikNums + "张票" ); Thread.sleep(500 ); } catch (InterruptedException e) { e.printStackTrace(); } }else { break ; } } } public static void main (String[] args) throws IOException { Test test = new Test(); new Thread(test,"黄牛" ).start(); new Thread(test,"小明" ).start(); new Thread(test,"小红" ).start(); } }
Demo:龟兔赛跑 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 public class Test implements Runnable { private static String winner; @Override public void run () { for (int i=0 ; i<=100 ; i++) { if (gameOver(i)) { break ; } if (Thread.currentThread().getName().equals("乌龟" )) { try { Thread.sleep(10 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("乌龟走了第" + i + "步" ); }else if (Thread.currentThread().getName().equals("兔子" )) { if (i==50 ) { try { Thread.sleep(1700 ); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("兔子走了第" + i + "步" ); } } } public static void main (String[] args) { Test test = new Test(); new Thread(test,"乌龟" ).start(); new Thread(test,"兔子" ).start(); } public boolean gameOver (int step) { if (winner != null ) { return true ; }else if (step == 100 ) { winner = Thread.currentThread().getName(); System.out.println("胜利者:" + winner); return true ; } return false ; } }
线程方法
1 Thread.currentThread().getName()
1 2 3 4 Demo demo = new Demo(); Thread thread = new Thread(demo); thread.join();
线程优先级:优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用了,还是得看调度器的调度
1 2 3 4 5 6 7 8 9 10 11 12 Thread.MIN_PRIORITY = 1 ; Thread.MAX_PRIORITY = 10 ; Thread.NORM_PRIORITY = 5 ; Demo demo = new Demo(); Thread thread = new Thread(demo); thread.getPriority(); thread.setPriority(xxx);
Lamda表达式 函数式接口:只包含一个方法的接口就是函数式接口,也叫功能性接口
Lamda简化了匿名内部类,方法引用简化了lamda
基本语法:接口 对象 = (参数表) -> {代码实现};
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 interface Demo { void test (int i) ; } public class Lamda { public static void main (String[] args) { Demo demo01 = (int i) -> { System.out.println("Hello World" + i); }; demo01.test(10 ); Demo demo02 = (i) -> { System.out.println("Hello World" + i); }; demo02.test(10 ); Demo demo03 = i -> { System.out.println("Hello World" + i); }; demo03.test(10 ); Demo demo04 = i -> System.out.println("Hello World" + i); demo04.test(10 ); } }
Lamda表达式在多线程中的运用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Test { public static void main (String[] args) throws IOException { new Thread(() -> { for (int i=0 ; i<100 ; i++) { System.out.println("我在吃饭-------------------" ); }; }).start(); for (int i=0 ; i<1000 ;i++) { System.out.println("我在睡觉" ); } } }
线程状态
线程中断后,进入死亡状态,就不可再次启动了
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 public class Test implements Runnable { @Override public void run () { for (int i=0 ; i<5 ; i++) { try { Thread.sleep(1000 ); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("线程终止了" ); } public static void main (String[] args) throws InterruptedException { Test test = new Test(); Thread thread = new Thread(test); Thread.State state = thread.getState(); System.out.println(state); thread.start(); state = thread.getState(); System.out.println(state); while (Thread.State.TERMINATED != state) { Thread.sleep(200 ); state = thread.getState(); System.out.println(state); } } }
线程停止 JDK提供了stop()和destroy()方法来停止线程,但是这两个方法都已经废除,不推荐使用
最好的做法是,自己创建一个标志位来控制线程的停止,达到某一条件,就自动停止
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 public class Test extends Thread { private static boolean flag = false ; @Override public void run () { for (int i=0 ; i<100 ; i++) { if (flag) { break ; } System.out.println("我在吃饭-------------------" ); } } public static void main (String[] args) throws IOException { Test test = new Test(); test.start(); for (int i=0 ; i<1000 ;i++) { if (i==50 ) { flag = true ; System.out.println("线程停止了" ); } System.out.println("我在睡觉" ); } } }
线程休眠
sleep时间制定当前线程阻塞的毫秒数
sleep存在异常InterruptedException
sleep时间达到后线程进入就绪状态
sleep可以模拟网络延时,倒计时等
每一个对象都有一个锁,sleep不会释放锁
1 2 3 4 5 try { Thread.sleep(5000 ); } catch (InterruptedException e) { e.printStackTrace(); }
线程礼让
线程礼让,让当前正在执行的线程暂停,但不阻塞
将线程从运行状态重新转为就绪状态,等待调度器调度
礼让不一定成功,主要还是看调度器的调度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Test implements Runnable { @Override public void run () { System.out.println(Thread.currentThread().getName() + "线程开始执行" ); Thread.yield(); System.out.println(Thread.currentThread().getName() + "线程停止执行" ); } public static void main (String[] args) { Test test = new Test(); new Thread(test,"A" ).start(); new Thread(test,"B" ).start(); } }
线程强制执行 join合并线程,待此线程执行完成后,再执行其他线程,其他线程会阻塞
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class Test implements Runnable { @Override public void run () { for (int i=0 ; i<=500 ; i++) { System.out.println("插队线程正在执行" + i); } } public static void main (String[] args) throws InterruptedException { Test test = new Test(); Thread thread = new Thread(test); thread.start(); for (int i=0 ; i<=100 ; i++) { if (i == 50 ) { thread.join(); } System.out.println("主线程在执行" + i); } } }
线程优先级 Java提供一个线程调度器来监控处于就绪状态的所有线程,线程调度器按照优先级觉得线程执行顺序的先后,优先级低也不代表一定后执行,主要还是调度器控制
1 2 3 4 5 6 public final static int MIN_PRIORITY = 1 ;public final static int NORM_PRIORITY = 5 ;public final static int MAX_PRIORITY = 10 ;
获取、设置优先级,最好在线程开启之前进行设置
1 2 3 4 5 6 7 8 9 10 Test test = new Test(); Thread thread = new Thread(test); thread.getPriority(); thread.setPriority(4 ); thread.start();
守护线程
线程分为用户线程和守护线程
虚拟机必须确保用户线程执行完毕
虚拟机不用等待守护线程执行完毕
1 2 3 4 Test test = new Test(); Thread thread = new Thread(test); thread.setDaemon(true );
线程同步
形成条件:队列+锁
线程同步是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池,形成队列,前面的线程使用完毕,下一个线程再使用
由于同一进程的多个线程共享同一块存储空间,为了避免访问冲突,加入了锁机制synchronized,当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后再释放锁
使用锁存在一些问题:
一个线程持有锁会导致其他所有需要此锁的线程挂起
在多线程竞争下,加锁、释放锁会导致较多的上下文切换和调度延时,引起性能问题
一个优先级高的线程等待一个优先级低的线程时,会导致优先级倒置,引起性能问题
线程锁
线程同步是依靠锁实现的,锁又分为同步方法和同步代码块两种
对于普通同步方法,锁的是当前实例对象。 如果有多个实例 那么锁的对象必然不同,就无法实现同步。
对于静态同步方法,锁的是当前类的Class对象。有多个实例 但是锁对象是相同的 可以实现同步。
对于同步代码块,锁的是Synchonized括号里的对象。对象最好是线程操作的公共资源
1 2 3 4 5 6 7 8 9 private synchronized void buy () {} synchronized (Obj){}
Lock与Synchonized功能相似,显式定义了锁,配合异常使用,一般在finally里面关闭
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 Lock implements Runnable { private int ticks = 10 ; ReentrantLock lock = new ReentrantLock(); @Override public void run () { while (true ) { try { lock.lock(); if (ticks>0 ) { Thread.sleep(100 ); System.out.println(Thread.currentThread().getName() + "拿到了第" + ticks + "张票" ); ticks--; }else { break ; } } catch (Exception e) { }finally { lock.unlock(); } } } public static void main (String[] args) { Lock lock = new Lock(); new Thread(lock,"线程一" ).start(); new Thread(lock,"线程二" ).start(); new Thread(lock,"线程三" ).start(); } }
Lock与Synchonized的区别:
Lock是显式锁(手动开启与关闭),Synchonized是隐式锁,出作用域自动关闭
Lock只有代码块锁,Synchonized有代码块锁和方法锁
Lock性能好
使用顺序: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 38 39 40 41 42 43 44 45 46 47 public class Test extends Thread { static Knife knife = new Knife(); static Gun gun = new Gun(); @Override public void run () { if (Thread.currentThread().getName().equals("小明" )) { synchronized (knife) { System.out.println("小明得到了玩具枪" ); synchronized (gun) { System.out.println("小明得到了玩具刀" ); } } }else { synchronized (gun) { System.out.println("小黄得到了玩具刀" ); synchronized (knife) { System.out.println("小明得到了玩具刀" ); } } } } public static void main (String[] args) { Test test = new Test(); Thread thread1 = new Thread(test,"小明" ); Thread thread2 = new Thread(test,"小黄" ); thread1.start(); thread2.start(); } } class Knife {} class Gun {}
解决:在抱有资源的情况下,尽量不要去抢夺资源
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 public class Test extends Thread { static Knife knife = new Knife(); static Gun gun = new Gun(); @Override public void run () { if (Thread.currentThread().getName().equals("小明" )) { synchronized (knife) { System.out.println("小明得到了玩具枪" ); } synchronized (gun) { System.out.println("小明得到了玩具刀" ); } }else { synchronized (gun) { System.out.println("小黄得到了玩具刀" ); } synchronized (knife) { System.out.println("小明得到了玩具刀" ); } } } public static void main (String[] args) { Test test = new Test(); Thread thread1 = new Thread(test,"小明" ); Thread thread2 = new Thread(test,"小黄" ); thread1.start(); thread2.start(); } } class Knife {} class Gun {}
线程通信——生成者与消费者关系问题
管程法 利用一个缓冲区来解决问题
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 public class Test { public static void main (String[] args) { SynContainer container = new SynContainer(); new Productor(container).start(); new Consumer(container).start(); } } class Productor extends Thread { SynContainer container; public Productor (SynContainer container) { this .container = container; } @Override public void run () { try { for (int i=0 ; i<20 ; i++) { System.out.println("生产了第" + (i+1 ) + "只鸡" ); container.push(new Chicken(i+1 )); } } catch (InterruptedException e) { e.printStackTrace(); } } } class Consumer extends Thread { SynContainer container; public Consumer (SynContainer container) { this .container = container; } @Override public void run () { try { for (int i=0 ; i<20 ; i++) { System.out.println("消费了第" + container.pop().getId() + "只鸡" ); } }catch (InterruptedException e) { e.printStackTrace(); } } } class Chicken { private int id; public Chicken (int id) { this .id = id; } public int getId () { return id; } public void setId (int id) { this .id = id; } } class SynContainer { static Chicken[] chickens = new Chicken[10 ]; int count = 0 ; public synchronized void push (Chicken chicken) throws InterruptedException { if (count == chickens.length) { this .wait(); } chickens[count] = chicken; count++; this .notify(); } public synchronized Chicken pop () throws InterruptedException { if (count == 0 ) { this .wait(); } count--; Chicken chicken = chickens[count]; this .notify(); 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 91 92 93 94 95 96 97 98 99 100 101 public class Test02 { public static void main (String[] args) { Process process = new Process(); new Productor(process).start(); new Consumer(process).start(); } } class Productor extends Thread { Process process; public Productor (Process process) { this .process = process; } @Override public void run () { for (int i=0 ; i<20 ; i++) { try { this .process.push(new Chicken(i+1 )); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Consumer extends Thread { Process process; public Consumer (Process process) { this .process = process; } @Override public void run () { for (int i=0 ; i<20 ; i++) { try { process.pop(); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Chicken { private int id; public Chicken () { } public Chicken (int id) { this .id = id; } @Override public String toString () { return "鸡,id为" + id; } } class Process { Chicken chicken; private boolean flag = true ; public synchronized void push (Chicken chicken) throws InterruptedException { if (!flag) { this .wait(); } System.out.println("生产了" + chicken); this .notifyAll(); this .chicken = chicken; this .flag = !this .flag; } public synchronized void pop () throws InterruptedException { if (flag) { this .wait(); } System.out.println("消费了" + chicken); this .notifyAll(); this .flag = !this .flag; } }
线程池 1 2 3 4 5 6 7 8 9 10 ExecutorService service = Executors.newFixedThreadPool(10 ); service.execute(new MyThread()); service.execute(new MyThread()); service.execute(new MyThread()); service.shutdown();