多线程笔记

    技术2022-07-10  147

    多线程

    继承Thread类

    子类继承Thread类具备多线程能力

    启动线程:子类对象.start()

    不建议是使用:避免oop单继承局限性

    TestThread testThread = new TestThread(); testThread.start();

    实现Runnable接口

    实现接口runnable具有多线程能力

    启动线程:传入目标对象+Thread对象.start()

    推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

    TestThread1 t1 = new TestThread1(); //Thread thread = new Thread(t1); //thread.start(); new Thread(t1).start();

    静态代理模式总结

    真实对象和代理对象都要实现同一个接口代理对象要代理真实角色

    好处:

    代理对象可以做很多真实对象做不了的事情真实对象专注做自己的事情 public class StaticProxy { public static void main(String[] args) { you you = new you();//你要结婚 new Thread( ()-> System.out.println("我爱你")).start(); new WeddingCompany(new you()).HappyMarry(); /*WeddingCompany weddingCompany = new WeddingCompany(you); weddingCompany.HappyMarry();*/ } } interface Marry{ void HappyMarry(); } //真实角色,你去结婚 class you implements Marry{ @Override public void HappyMarry() { System.out.println("我要结婚了,很开心"); } } //代理角色,帮助你结婚 class WeddingCompany implements Marry{ private Marry target; WeddingCompany(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("结婚之前,布置现场"); } }

    lambda表达式

    理解Functional Interface(函数式接口)是学习java8 lambda表达式的关键所在

    函数式接口的定义:

    ​ 任何接口,如果只包含唯一一个抽象方法,那么他就是一个函数式接口

    ​ 对于函数式接口,我们可以通过lambda表达式来创建该接口的对象

    //退到lambda表达式 public class TestLambda1 { //3.静态内部类 static class Like2 implements ILike{ @Override public void lambda() { System.out.println("i like lambda2"); } } public static void main(String[] args) { ILike like = new Like(); like.lambda(); like = new Like2(); like.lambda(); //4.局部内部类 class Like3 implements ILike{ @Override public void lambda() { System.out.println("i like lambda3"); } } like = new Like3(); like.lambda(); //5.匿名内部类,没有类别的名称,必须借助接口或者父类 like = new ILike() { @Override public void lambda() { System.out.println("i like lambda4"); } }; like.lambda(); //6.用lambda简化 like = ()->{ System.out.println("i like lambda5"); }; like.lambda(); } } //1.定义一个函数式接口 interface ILike{ void lambda(); } //2.实现类 class Like implements ILike{ @Override public void lambda() { System.out.println("i like lambda"); } }

    其中,lambda可以简化

    //lambda表示简化 ILove love = (int a)->{ System.out.println("i like lambda"+a); } //简化1.参数类型 ILove love = (a)->{ System.out.println("i like lambda"+a); } //简化2.简化括号 ILove love = a ->{ System.out.println("i like lambda"+a); } //简化3.去掉花括号 ILove love = a -> System.out.println("i like lambda"+a);

    总结:

    lambda表达式只能有一行代码的情况下才能简化成一行,如果有多行,那么就用代码块包裹(花括号)前提必须是函数式接口多个参数也可以去掉参数类型,要去就都去掉,必须加上括号

    停止线程

    不推荐使用jdk提供的stop(),destory()方法,因为已经废弃推荐线程自己停止下来建议使用一个标志位进行终止变量当flag=false,则终止线程运行 public class Stop implements Runnable{ private boolean flag = true; @Override public void run() { int i = 0; while (flag){ System.out.println("run...."+i++); } } public void stop(){ this.flag = false; } public static void main(String[] args) { Stop stop = new Stop(); new Thread(stop).start(); for (int i = 0; i < 1000; i++) { System.out.println("主线程"+i); if (i==500){ stop.stop(); System.out.println("线程该停止了"); } } } }

    线程休眠

    sleep指定当前线程阻塞的毫秒数sleep存在异常InterruptedExceptionsleep时间达到后线程进入就绪状态sleep可以模拟网络延时,倒计时等每一个对象都有一个锁,sleep不会释放锁

    倒计时的例子

    public class Sleep { public static void main(String[] args) throws InterruptedException { tenDown(); } public static void tenDown() throws InterruptedException { int num = 10; while (true){ Thread.sleep(1000); System.out.println(num--); if (num<=0){ break; } } } }

    动态刷新当前的系统时间

    public class Sleep { public static void main(String[] args) { //打印当前系统时间 Date startTime = new Date(System.currentTimeMillis());//获取系统当前时间 while (true){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime)); startTime = new Date(System.currentTimeMillis());//更新当前时间 } } }

    线程礼让

    礼让线程,让当前正在执行的线程暂停,但不阻塞将线程从运行状态转为就绪状态让cpu重新调度,礼让不一定成功!看cpu心情 public class Yield { public static void main(String[] args) { MyYield myYield = new MyYield(); new Thread(myYield,"a").start(); new Thread(myYield,"b").start(); } } class MyYield implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"正在执行"); Thread.yield(); System.out.println(Thread.currentThread().getName()+"停止执行"); } }

    Join

    Join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞可以想象成插队

    线程的优先级

    java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行

    线程的优先级用数字表示,范围从1~10

    Thread.MIN_PRIORITY = 1;Thread.MAX_PRIORITY = 10;Thread.NORM_PRIORITY = 5;

    使用以下方式改变或获取优先级

    getPriority().setPriority(int xxx)

    优先级的设定建议在start()调度前

    优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用了,这都是看cpu的调度

    守护线程(setDaemon)

    线程分为 用户线程和 守护线程虚拟机必须确保用户线程执行完毕虚拟机不用等待守护线程执行完毕 public class daemon { public static void main(String[] args) { God god = new God(); You you = new You(); Thread thread = new Thread(god); thread.setDaemon(true);//默认是false表示用户线程,正常的线程都是用户线程 thread.start();//守护线程启动 new Thread(you).start();//用户线程启动 } } class God implements Runnable{ @Override public void run() { while (true){ System.out.println("上帝保佑你"); } } } class You implements Runnable{ @Override public void run() { for (int i = 0; i < 36500; i++) { System.out.println("我开心的活着"); } System.out.println("=======goodbye world====="); } }

    并发

    同一个对象 被 多个线程同时操作

    线程同步

    处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象.这时候我们就需要线程同步,线程同步其实就是一种 等待机制,多个需要同时访问此对象的线程进入这个 对象的等待池形成队列,等待前面线程使用完毕,下个线程再使用

    形成条件:队列+锁保证安全性

    由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突的问题,为了保证数据在方法中被访问的正确性,在访问时加入了 锁机制 synchronized,当一个线程获得对象的排它锁,独占资源其他线程必须等待,使用后释放锁即可

    一个线程持有锁会导致其他所有需要此锁的线程挂起在多线程竞争下,加锁,释放锁会导致会比较多的上下文切换和调度延时,引起性能问题如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题

    同步块

    synchronized(obj){}

    obj称之为 同步监视器

    obj可以为任何对象,但是推荐使用共享资源作为同步监视器

    同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,默认锁住是this就是这个对象本身,或者是class

    同步块可以锁任何东西

    锁的东西就是变化的量,需要增删改的对象

    //两个人去银行取钱,账户 public class UnsafeBank { public static void main(String[] args) { Account account = new Account(100,"结婚基金"); Drawing me = new Drawing(account,50,"自己"); Drawing GirlFriend = new Drawing(account,100,"女朋友"); GirlFriend.start(); me.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() { synchronized (account){ //判断没有钱 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); } } } public class UnSafeList { public static void main(String[] args) throws InterruptedException { List<String> list = new ArrayList<String>(); for (int i = 0; i < 10000; i++) { new Thread(()->{ synchronized (list){ list.add(Thread.currentThread().getName()); } }).start(); } Thread.sleep(3000); System.out.println(list.size()); } }

    死锁

    多个线程占用一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或多个线程都在等待对方释放资源,都停止执行的情况.某一个同步块同时拥有**“两个以上对象的锁”**时,就可能发生"死锁"的问题

    产生死锁的四个必要条件

    互斥条件:一个资源每次只能被一个进程使用请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系

    Lock锁

    class A implements Runnable{ private final ReentrantLock lock = new ReentrantLock(); @Override public void run(){ lock.lock(); try{ //保证线程安全的代码; } finally { lock.unlock(); //如果同步代码有异常,要将unlock()写入finally语句块 } } }

    信号灯法

    public class deng { 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){ try { this.tv.play("快乐大本营播放中"); } catch (InterruptedException e) { e.printStackTrace(); } }else { try { this.tv.play("抖音美好生活"); } catch (InterruptedException e) { e.printStackTrace(); } } } } } class Watcher extends Thread{ TV tv; public Watcher(TV tv){ this.tv = tv; } @Override public void run() { for (int i = 0; i < 20; i++) { try { tv.watch(); } catch (InterruptedException e) { e.printStackTrace(); } } } } class TV{ //演员表演,观众等待 //观众观看,演员等待 String voice;//表演的节目 boolean flag = true; //表演 public synchronized void play(String voice) throws InterruptedException { if (!flag){ this.wait(); } System.out.println("演员表演了:"+voice); //通知观众观看 this.notifyAll();//通知唤醒 this.voice = voice; this.flag = !this.flag; } //观看 public synchronized void watch() throws InterruptedException { if (flag){ this.wait(); } System.out.println("观众观看了"+voice); //通知演员表演 this.notifyAll(); this.flag=!this.flag; } }
    Processed: 0.015, SQL: 9