线程、进程、多线程
- 进程:进程是程序的一次执行过程,是一个动态的概念,是系统资源分配的单位
- 线程:通常在一个进程中可以包含若干个线程,一个进程中至少有一个线程,不然没有存在的意义,线程是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();
|