多线程
核心概念
-
线程就是独立的执行路径,而进程是一个动态的概念,进程相当于是多个线程的保护伞
-
在程序运行时,及时没有自己创建的线程,后台也会有多个线程,如主线程,gc线程
-
main()称之为主线程,系统的入口,用于执行整个程序
-
多线程之间的顺序是不能人为干预的
-
对于同一份资源的操作,可能会有争抢,所以要加入并发控制(让所有人排队)
-
线程会带来额外的开销(cpu调度时间,并发控制开销)
-
线程中的数据一定要同步
创建线程
方式一
-
继承Thread类 (不怎么使用:避免oop单继承局限性)
-
重写run()方法,编写线程体
-
创建一个线程对象
-
调用start开启线程
public class TestThread1 extends Thread { //1 @Override //2 public void run() { //run 线程体 for (int i = 0; i < 200; i++) { System.out.println("执行Run线程"+i); } } public static void main(String[] args) { //main 线程体 TestThread1 testThread1 = new TestThread1(); //3 testThread1.start(); //4 for (int i = 0; i < 200; i++) { System.out.println("执行Main线程"+i); } } } //两个方法交替执行
注意:线程开启不一定会立即执行,会由CPU来调度
方式二
-
implements 实现Runnable接口 (用得多:灵活方便,方便同一个对象被多个线程使用)
-
实现run()方法,编写线程执行体
-
先创建实现类对象
-
再创建线程对象(把实现类对象放进去),调用start()方法启动线程
public class TestThread2 implements Runnable { //1 @Override public void run() { //2 //run线程 for (int i = 0; i < 200; i++) { System.out.println("执行run线程"+i); } } public static void main(String[] args) { //main线程 TestThread2 testThread2 = new TestThread2(); //3 Thread thread = new Thread(testThread2); //4 thread.start(); for (int i = 0; i < 200; i++) { System.out.println("执行main线程"+i); } } }
多个线程操作同一个对象
买火车票
public class TestThread3 implements Runnable { private int tikect = 10; @Override public void run() { while (true) { if (tikect<=0) { break; } System.out.println(Thread.currentThread().getName()+"拿到第"+tikect--+ "张票"); } } public static void main(String[] args) { TestThread3 tk = new TestThread3(); //多个线程操作一个对象 Thread t1 = new Thread(tk,"小明"); Thread t2 = new Thread(tk,"小花"); Thread t3 = new Thread(tk,"小郭"); t1.start(); t2.start(); t3.start(); } }
发现问题:多个线程操作同一个资源的时候,线程不安全,数据紊乱
解决:队列 + 🔒
静态代理
好处:代理对象可以做很多真实对象做不了的事情
真实对象专注做自己的事情
public class StaticProxy { public static void main(String[] args) { // You you = new You(); // you.happyMarry(); Wedding wedding = new Wedding(new You()); wedding.happyMarry(); } } interface Marry{ void happyMarry(); } class You implements Marry{ @Override public void happyMarry() { System.out.println("结婚啦"); } } class Wedding implements Marry{ private Marry target; public Wedding(Marry target) { //通过构造方法把真实对象传进去 this.target = target; } @Override public void happyMarry() { before(); //代理对象额外能做的事情 this.target.happyMarry(); //真实对象专心做自己的事情 after(); //代理对象额外能做的事情 } private void after() { System.out.println("结婚后的准备"); } private void before() { System.out.println("结婚前的准备"); } }
总结:真实对象和代理对象都要实现同一个接口
代理对象要代理真是角色 (构造方法传入)
Thread 实现 Runnable是一个道理~
Lambda 表达式
最大的大前提:接口为函数式接口,一个接口里面只写了一个函数
目的:
-
避免匿名内部类定义过多
-
可以让代码看起来更简洁
-
去掉很多没有意义的代码
演变过程:外部实现类-->静态内部类-->局部内部类-->匿名内部类(没有类名,并且方法名字都是一样的所以直接去掉)-->Lambda
public class TestLambda1 { //3.静态内部类 static class Love1 implements Ilove{ @Override public void iLove(int a) { System.out.println("I Love YOU--->"+a); } } public static void main(String[] args) { //4.局部内部类 class Love2 implements Ilove{ @Override public void iLove(int a) { System.out.println("I Love YOU--->"+a); } } Ilove love = new Love(); love.iLove(2); love = new Love1(); love.iLove(3); love = new Love2(); love.iLove(4); //5.匿名内部类 Ilove ilove = new Ilove(){ @Override public void iLove(int a) { System.out.println("I Love YOU--->"+a); } }; ilove.iLove(5); } } //1.接口类 interface Ilove{ void iLove(int a); } //2.外部实现类 class Love implements Ilove{ @Override public void iLove(int a) { System.out.println("I Love YOU--->"+a); } }
Lambda简化:
//6.Lambda简化 ilove = (int a)-> { System.out.println("I Love YOU--->"+a); }; ilove.iLove(6);
Lambda简化终极爆炸版:
前提:只能有一行表达式,多行还是需要用花括号包括起来
多个参数也可以去掉参数类型,要去掉都去掉,必须加上括号,用逗号隔开
ilove = a->System.out.println("I Love YOU--->"+a); ilove.iLove(6);
停止线程
推荐线程自己停下来:
怎么做呢:使用一个标志位进行终止变量,当flag=false,则终止。
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 static void main(String[] args) { TestStop testStop = new TestStop(); new Thread(testStop).start(); for (int i = 0; i < 200; i++) { System.out.println("main........thread"+i); if (i==150) { testStop.stop(); System.out.println("该线程停止了"); } } } public void stop(){ this.flag = false; } }
线程休眠
-
sleep指定当前线程阻塞的毫秒数:1000->1s
-
sleep存在异常InterruptedException
-
sleep时间打倒后线程进入就绪状态
-
sleep可以模拟网络延时,倒计时等
-
每一个对象都有一个🔒,sleep不会释放锁
模拟倒计时
public class TestSleep implements Runnable { @Override public void run() { int num = 10; while (true) { try { Thread.sleep(1000); System.out.println("倒计时"+num--); } catch (InterruptedException e) { e.printStackTrace(); } if (num<=0) { break; } } } public static void main(String[] args) { TestSleep testSleep = new TestSleep(); new Thread(testSleep).start(); } }
模拟时钟
public class TestSleep implements Runnable { @Override public void run() { Date startTime = new Date(System.currentTimeMillis()); while (true) { try { Thread.sleep(1000); System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime)); startTime = new Date(System.currentTimeMillis()); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { TestSleep testSleep = new TestSleep(); new Thread(testSleep).start(); } }
线程礼让
礼让不一定成功,礼让后线程之间重新竞争,CPU还是老大哥
Thread.yield
线程强制执行(插队)
天龙人地位高,我走完了你才能走.....
public class TestJoin implements Runnable{ @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println("VIP"+i); } } public static void main(String[] args) throws InterruptedException { TestJoin testJoin = new TestJoin(); Thread thread = new Thread(testJoin); thread.start(); for (int i = 0; i < 500; i++) { if (i==10) { thread.join(); } System.out.println("main"+i); } } }
守护线程
God bless you my man~!
-
线程分为用户线程和守护线程
-
虚拟机必须确保用户线程执行完毕
-
不用等守护线程执行完毕
GC线程 记录操作日志 监控内存都是守护线程
public class TestDaemon { public static void main(String[] args) { We we = new We(); Thread thread = new Thread(we); God god = new God(); Thread thread1 = new Thread(god); thread1.setDaemon(true); //默认false为用户线程 , true为守护线程 thread.start(); thread1.start(); } } class We implements Runnable{ @Override public void run() { for (int i = 0; i < 365; i++) { System.out.println("我还活着!!"+i); } System.out.println("上票"); } } class God implements Runnable{ @Override public void run() { while (true) { System.out.println("God bless you my man~"); } } }
synchronized
-
在方法上修饰的时候可以给这个方法上锁,不同线程访问这个方法就要看是否有锁
-
用同步块:synchronized(Obj){}来🔒的话 关键在于这个Obj是什么
-
Obj是需要增删改的对象
同步方法,同步代码块
public class UnSafeBuyTicket implements Runnable{ private boolean flag = true; private int ticket = 10; @Override public void run() { //买票 while (flag) { try { buy(); } catch (InterruptedException e) { e.printStackTrace(); } } } private synchronized void buy() throws InterruptedException { //判断是否有票 if (ticket<=0) { flag = false; return; } //模拟延时 Thread.sleep(100); //买票 System.out.println(Thread.currentThread().getName()+"买到第"+ticket--+"张票"); } public static void main(String[] args) { UnSafeBuyTicket unSafeBuyTicket = new UnSafeBuyTicket(); new Thread(unSafeBuyTicket,"小明").start(); new Thread(unSafeBuyTicket,"小花").start(); } }
死锁
产生的四个必要条件:
-
互斥条件:一个资源每次只能被一个进程使用
-
请求与保持条件:一个进程因请求资源而阻塞时,队以获得的资源保持不放
-
不剥夺条件:进程以获得的资源,在没有使用完的时候,不能被强制剥夺
-
循环等待条件:进程之间首尾相接循环等待资源
public class DeadLock { public static void main(String[] args) { MakeUp m1 = new MakeUp("小花", 0); MakeUp m2 = new MakeUp("小红", 2); m1.start(); m2.start(); } } //口红 class Lipstick { } //镜子 class Mirror { } class MakeUp extends Thread { String girlName; int choice; private static Lipstick lipstick = new Lipstick(); private static Mirror mirror = new Mirror(); public MakeUp(String girlName, int choice) { this.girlName = girlName; this.choice = choice; } @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+"拿到镜子"); } } }else{ synchronized (mirror){ System.out.println(this.girlName+"拿到镜子"); Thread.sleep(2000); synchronized (lipstick){ System.out.println(this.girlName+"拿到口红"); } } } } }
解决:用完了释放🔒
public class DeadLock { public static void main(String[] args) { MakeUp m1 = new MakeUp("小花", 0); MakeUp m2 = new MakeUp("小红", 2); m1.start(); m2.start(); } } //口红 class Lipstick { } //镜子 class Mirror { } class MakeUp extends Thread { String girlName; int choice; private static Lipstick lipstick = new Lipstick(); private static Mirror mirror = new Mirror(); public MakeUp(String girlName, int choice) { this.girlName = girlName; this.choice = choice; } @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+"拿到镜子"); } }else{ synchronized (mirror){ System.out.println(this.girlName+"拿到镜子"); Thread.sleep(2000); } synchronized (lipstick){ System.out.println(this.girlName+"拿到口红"); } } } }
为了更好的控制多个线程对共享资源进行访问--->产生了一个工具类!它就是Lock!!当当当~~
java.util.concurrent.locks.Lock //接口
ReentrantLock 类实现了Lock -->更加常用 可重入锁
因为加锁之后最后都要释放锁
所以一般都与try finally一起用
快捷键: crtl +alt +t
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{ private int ticket = 10; //定义Lock private final ReentrantLock lock = new ReentrantLock(); //这一句很重要 @Override public void run() { while (true) { try { lock.lock(); //显示的加锁 if (ticket>0) { System.out.println(ticket--); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } finally { lock.unlock(); //显示的释放锁 } } } }
synchronized 与Lock 的对比
-
Lock时显示🔒 (手动挡)synchronized是隐式🔒,除了作用域自动释放(自动挡)
-
synchronized 比 Lock 多了一个方法🔒
-
使用Lock🔒,JVM花费时间少些,性能更好,并且具有更好的发展性(提高更多的子类)
-
优先使用顺序:
-
Lock > 同步代码块 > 同步方法
-
线程通信
wait(): 表示线程一直等待,直到其他线程通知,与Sleep不同,wait会释放锁 notify(): 唤醒处于等待中的线程 notifyAll(): 唤醒同一对象上所有调用wait()方法的线程,优先级别高的的线程优先调度
注意:均是Object类的方法,都只能再同步方法或者同步代码块中使用,否则会抛出异常
illegalMonitorStateException
生产者与消费者
public class TestPC { public static void main(String[] args) { SynContainer container = new SynContainer(); Productor productor = new Productor(container); Consumer consumer = new Consumer(container); productor.start(); consumer.start(); } } //生产者 class Productor extends Thread { SynContainer container; public Productor(SynContainer container) { this.container = container; } //生产 @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("生产了" + i + "只鸡"); container.push(new Chicken(i)); } } } //消费者 class Consumer extends Thread { SynContainer container; public Consumer(SynContainer container) { this.container = container; } @Override public void run() { //消费 for (int i = 0; i < 100; i++) { System.out.println("消费了"+container.pop().id+"只鸡"); } } } //缓冲区 class SynContainer { //放鸡的容器 Chicken[] chickens = new Chicken[10]; //记录容器里面鸡的数量 int count = 0; //生产者生产鸡 public synchronized void push(Chicken chicken){ //容器满了 if (count==chickens.length) { //通知消费者消费 } //没满就往容器里面放 chickens[count] = chicken; count++; } //消费者消费 public synchronized Chicken pop(){ //判断能否消费 if (count==0) { //通知生产者生产 } Chicken chicken = chickens[count]; count--; return chicken; } } //产品 class Chicken{ int id; public Chicken(int id) { this.id = id; } }
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++) { this.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; } }