Lesson26_多线程
程序、进程、线程
-
程序
死的,躺在硬盘上的应用程序,是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。
-
进程
是执行程序的一个过程。程序运行起来,就变成了进程,需要占据内存条上的空间,是系统分配资源的单位。
-
线程
进程是包工头,线程是农民工。一个进程中至少要有一个线程。
进程才有资格向操作系统申请资源。多个线程共享进程申请来的资源。
-
注意:很多多线程是模拟出来的,正在的多线程是指多个cpu,即多核,如服务器,如果是模拟出来多线程,即在一个cpu的情况下,在同一时间点,cpu只能执行一个代码,因为切换的很快,所以就有同事执行的错觉。
核心概念
- 线程就是独立的执行路径;
- 在程序运行时,即时没有自己创建线程,后台也会有多个线程,如主线程,gc线程等;
- main()称之为主线程,为系统的入口,用于执行整个程序;
- 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为干预的;
- 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;
- 线程会带来额外的开销,如cpu调度时间,并发控制开销。
- 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致。
多线程的目的
-
并发
假的,多个任务同时执行,CPU时间片轮转
-
并行
真的,多个任务同时执行,多个CPU一起干活
最大化地使用CPU资源
线程模型
-
继承Thread类
-
线程类,继承这个类,就是线程类。
-
extends Thread ,重写run方法
-
可以直接start()启动一个线程
/** * @Author: 邪灵 * @Date: 2020/7/14 10:27 * @Description: * @version: 1.0 */ public class ThreadDemo1 extends Thread { public ThreadDemo1(String name) { super(name); } @Override public void run() { for (int i = 0; i < 200; i++) { System.out.println("我是"+Thread.currentThread().getName()+",我执行了"+(i+1)+"次"); } } public static void main(String[] args) { new ThreadDemo1("线程1").start(); new ThreadDemo1("线程2").start(); for (int i = 0; i < 200; i++) { System.out.println("我是"+Thread.currentThread().getName()+",我执行了"+(i+1)+"次"); } } }
-
-
实现Runnable接口
-
可运行的能力,实现这个接口,就可以被Thread装载,运行
-
implements Runnable
-
不可以直接start(),需要借助Thread对象
/** * @Author: 邪灵 * @Date: 2020/7/14 10:36 * @Description: Runnable实现多线程 * @version: 1.0 */ public class RunnableDemo implements Runnable{ @Override public void run() { for (int i = 0; i < 200; i++) { System.out.println("我是"+Thread.currentThread().getName()+"第"+(i+1)+"次出现"); } } public static void main(String[] args) { RunnableDemo runnableDemo = new RunnableDemo(); new Thread(runnableDemo,"线程A").start(); new Thread(runnableDemo,"线程B").start(); for (int i = 0; i < 200; i++) { System.out.println("我是"+Thread.currentThread().getName()+"第"+(i+1)+"次出现"); } } }
/** * @Author: 邪灵 * @Date: 2020/7/14 10:50 * @Description: 多线程实现买票 * @version: 1.0 */ public class PickDemo implements Runnable { private int pick = 100; @Override public void run() { while (pick>0){ System.out.println(Thread.currentThread().getName()+"购买到第"+pick--+"张票"); } } public static void main(String[] args) { PickDemo pickDemo = new PickDemo(); new Thread(pickDemo,"张三").start(); new Thread(pickDemo,"李四").start(); new Thread(pickDemo,"王五").start(); } }
-
-
实现Callable接口
/** * @Author: 邪灵 * @Date: 2020/7/13 04:11 * @Description: * @version: 1.0 */ public class TreadTest implements Callable<Boolean> { @Override public Boolean call() throws Exception { for (int i = 0; i < 5; i++) { //Thread.sleep(100); System.out.println(Thread.currentThread().getName()+"打印了"+i); } return true; } public static void main(String[] args) throws ExecutionException, InterruptedException { //第一种方式,通过线程池实现 ExecutorService es = Executors.newFixedThreadPool(4); Future<Boolean> s1 = es.submit(new TreadTest()); Future<Boolean> s2 = es.submit(new TreadTest()); Future<Boolean> s3 = es.submit(new TreadTest()); System.out.println(s1.get()); es.shutdown(); //第二种方式,通过Futuretask实现 FutureTask<Boolean> task = new FutureTask<>(new TreadTest()); new Thread(task).start(); task.get(); }
线程的生命周期
线程停止
-
java中提供了停止线程的方法,有stop();destroy();
-
java中提供的两个方法已经过时,不推荐使用。一般自定义
/** * @Author: 邪灵 * @Date: 2020/7/14 13:29 * @Description: 测试线程停止 * @version: 1.0 */ public class TestStop implements Runnable{ private boolean flag = true; @Override public void run() { int i = 0; while (flag) { System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date())+" 跑了第"+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++) { if (i%100==0) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+"走了"+(i+1)+"步"); if (i==800) { testStop.stop(); System.out.println("线程停止"); } } } }
线程休眠
- 模拟网咯延迟,放大并发问题
/**
* @Author: 邪灵
* @Date: 2020/7/14 13:53
* @Description:
* @version: 1.0
*/
public class TenDown {
public static void main(String[] args) {
new Thread(() -> {
Date date = new Date(System.currentTimeMillis());
while (true) {
try {
Thread.sleep(1000);
date=new Date(System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(new SimpleDateFormat("ss").format(date));
}
}).start();
}
}
线程礼让
-
礼让就是当前线程让出cpu,重新回到就绪状态,所以礼让有可能不成功
/** * @Author: 邪灵 * @Date: 2020/7/14 14:02 * @Description: 线程礼让 * @version: 1.0 */ public class YieldTest implements Runnable{ @Override public void run() { System.out.println("线程"+Thread.currentThread().getName()+"开始执行"); if (Thread.currentThread().getName().equals("A")) { Thread.yield(); } System.out.println("线程"+Thread.currentThread().getName()+"执行完毕"); } public static void main(String[] args) { YieldTest yieldTest = new YieldTest(); new Thread(yieldTest,"A").start(); new Thread(yieldTest,"B").start(); } }
监测线程状态
/**
* @Author: 邪灵
* @Date: 2020/7/14 15:24
* @Description: 监测线程状态
* @version: 1.0
*/
public class StateThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("执行最后一句话");
}
public static void main(String[] args) {
StateThread stateThread = new StateThread();
Thread thread = new Thread(stateThread);
Thread.State state = thread.getState();
System.out.println(state);
thread.start();
state = thread.getState();
System.out.println(state);
while (state!=Thread.State.TERMINATED) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
state = thread.getState();
System.out.println(state);
}
}
}
线程优先级
/**
* @Author: 邪灵
* @Date: 2020/7/14 15:24
* @Description: 监测线程状态
* @version: 1.0
*/
public class StateThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"执行");
}
public static void main(String[] args) {
StateThread stateThread = new StateThread();
Thread t1 = new Thread(stateThread,"t1");
Thread t2 = new Thread(stateThread,"t2");
Thread t3 = new Thread(stateThread,"t3");
Thread t4 = new Thread(stateThread,"t4");
Thread t5 = new Thread(stateThread,"t5");
t1.setPriority(10);
t2.setPriority(8);
t3.setPriority(6);
t4.setPriority(4);
t5.setPriority(2);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
System.out.println(Thread.currentThread().getPriority());
强制执行
-
强制执行可以理解为插队,比较霸道的做法
-
强制执行只是一种类似插队的做法,有可能在join前自己已经开始执行只是强制以后就会执行完毕该线程
-
总结下来可知,能够理解join是等待线程执行完毕。
/** * @Author: 邪灵 * @Date: 2020/7/14 15:05 * @Description: 测试join方法 * @version: 1.0 */ public class JoinTest implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("我是VIP,第"+(i+1)+"次执行"); } } public static void main(String[] args) { Thread thread = new Thread(new JoinTest()); thread.start(); for (int i = 0; i < 100; i++) { System.out.println("我是main,第"+(i+1)+"次执行"); if (i==50) { try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
线程同步
-
锁
每一个对象身上都有且只有一个锁
-
synchronized 关键字
-
用在方法体中
synchronized(对象){ 同步代码块 } 抢对象(锁),如果抢到了,则能够执行同步代码块,执行同步代码块期间,其它线程不会争夺; 如果抢不到,则进入阻塞状态(锁定集),等待锁的释放。 /** * @Author: 邪灵 * @Date: 2020/7/14 10:50 * @Description: 多线程实现买票 * @version: 1.0 */ public class PickDemo implements Runnable { private int pick = 100; @Override public synchronized void run() { while (pick>0){ System.out.println(Thread.currentThread().getName()+"购买到第"+pick--+"张票"); try { wait(1); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { PickDemo pickDemo = new PickDemo(); new Thread(pickDemo,"张三").start(); new Thread(pickDemo,"李四").start(); new Thread(pickDemo,"王五").start(); } }
-
用在方法体上
// 1. 用在普通方法上(访问修饰符、返回类型、方法名、参数列表都可以无视) public synchronized void test(){} 等价于 public void test(){ synchronized(this){ // 整个方法体 } } // 2. 用在静态方法上 public static synchronized void test(){} public static void test(){ synchronized(当前类.class){ // 整个方法体 } }
-
线程通信
-
wait()
主动释放锁(其它线程可以争夺锁资源),进入阻塞状态(等待集),等待其它线程notify\notifyAll
-
wait(xxx)
主动释放锁(其它线程可以争夺锁资源),进入阻塞状态(等待集),时间到了会自动醒来(进入锁定集)
-
notify()
唤醒阻塞状态(等待集)中有竞争关系的某一个线程(随机选取)
-
notifyAll()
唤醒阻塞状态(等待集)中有竞争关系的所有线程
-
生产者消费者
/** * @Author: 邪灵 * @Date: 2020/7/16 18:59 * @Description: * @version: 1.0 */ public class Lanzi { private static List<String> list = new ArrayList<>(); public synchronized void putter() { while (list.size()>0) { System.out.println("还没有消费完,不生产"); try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } for (int i = 1; i < 6; i++) { System.out.println("正在生产第"+i+"件商品"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } list.add("商品"+i); } notifyAll(); } public synchronized void getter() { while (list.size()<5) { System.out.println("没有商品了,等待生产"); try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } for (int i = 5; i > 0; i--) { System.out.println("正在消费第"+i+"件商品"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } list.remove("商品"+i); } notifyAll(); } } class Pro implements Runnable { Lanzi lanzi; public Pro(Lanzi lanzi) { this.lanzi = lanzi; } @Override public void run() { while (true) { lanzi.putter(); } } } class XFz implements Runnable { Lanzi lanzi; public XFz(Lanzi lanzi) { this.lanzi = lanzi; } @Override public void run() { while (true) { lanzi.getter(); } } } class TestLanz{ public static void main(String[] args) { Lanzi lanzi = new Lanzi(); Pro pro = new Pro(lanzi); XFz xFz = new XFz(lanzi); Thread thread = new Thread(xFz); Thread thread1 = new Thread(xFz); Thread thread2 = new Thread(xFz); thread.setPriority(10); thread1.start(); thread2.start(); thread.start(); new Thread(pro).start(); } }
单例模式懒汉式(多线程同步版)
【推荐阅读:我是一个线程】https://juejin.im/entry/5703a3b8d342d3005cfe517f
线程池中的线程 VS 普通线程
- 合同工
- 临时工