zoukankan      html  css  js  c++  java
  • Java多线程

    12.多线程

    12.1线程简介

      进程(process)和线程(Thread)

      程序:指令和数据的有序集合,静态

      进程:执行程序的一次执行过程,动态;系统资源的分配单位

      线程:一个进程中包含多个线程;为CPU调度和执行的单位

    核心概念:

    -线程就是独立执行的路径;

    -在程序运行时,即使没有自己创建线程,后台也有多个线程,如主线程,gc线程;

    -main()函数称为主函数,为系统的入口,用于执行整个程序;

    -在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的不能干预先后执行顺序

    -对于同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;

    -线程回带来额外的开销,如cpu调度时间,并发控制开销

    -每个线程在自己的工作内存中交互,内存控制不当会造成数据不一致

    12.2 线程如何创建

    三种创建方式:

    1、Thread class 继承Thread类(重点)

    2、Runnable接口 实现Runnable接口(重点)

    3、Callable接口 实现Callable接口(了解)

    1、Thread class

      -自定义线程类继承Thread类

      -重写run()方法,编写线程执行体

      -创建线程对象,调用start()方法启动线程

      注意:线程开启不一定立即执行,由CPU调度执行

     package com.thread;
     ​
     public class StartThread extends Thread{
         //线程主入口
         @Override
         public void run(){
             //线程体
             for (int i = 0; i < 10; i++) {
                 System.out.println("Thread learning!"+i);
            }
        }
     ​
         public static void main(String[] args) {
             //创建一个线程对象
             StartThread s1 = new StartThread();
             //调用start方法 交替执行
             //s1.start();
             //调用run方法 同时进行
             s1.run();
     ​
             for (int i = 0; i < 5; i++) {
                 System.out.println("Main函数执行"+i);
            }
        }
     }
     ​

    下载图片的栗子:

     package com.thread;
     ​
     import org.apache.commons.io.FileUtils;
     ​
     import java.io.File;
     import java.io.IOException;
     import java.net.URL;
     ​
     //练习Thread,实现多线程下载图片
     public class Demo02_thread extends Thread{
         private String url;
         private String name;
     ​
         public Demo02_thread(String url,String name){
             this.name=name;
             this.url=url;
        }
     ​
         //下载图片线程的执行体
         @Override
         public void run(){
             WebDownloader webDownloader = new WebDownloader();
             webDownloader.downloader(url,name);
             System.out.println("下载的文件名"+name);
        }
     ​
         public static void main(String[] args) {
             Demo02_thread D1 = new Demo02_thread("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1592626650566&di=1eac2c6dfdab88fc21ed847c558fbde6&imgtype=0&src=http%3A%2F%2Fimg3.imgtn.bdimg.com%2Fit%2Fu%3D1154409108%2C2186845244%26fm%3D214%26gp%3D0.jpg","1.jpg");
             Demo02_thread D2 = new Demo02_thread("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1661014703,2331064073&fm=26&gp=0.jpg","2.jpg");
             Demo02_thread D3 = new Demo02_thread("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1592626863891&di=f9684430b5d5af9d3103a0dea98636a6&imgtype=0&src=http%3A%2F%2Fn1.itc.cn%2Fimg8%2Fwb%2Frecom%2F2016%2F07%2F02%2F146744791651796077.JPEG","3.jpg");
     ​
             D1.start();
             D2.start();
             D3.start();
             //线程不是顺序执行,而是同时执行。
        }
     }
     ​
     //下载器
     class WebDownloader{
         //下载方法
         public void downloader(String url,String name){
             try {
                 FileUtils.copyURLToFile(new URL(url),new File(name));
            } catch (IOException e) {
                 e.printStackTrace();
                 System.out.println("IO异常,downloader出现问题");
            }
        }
     }
     ​

    2、Runnable

    -定义MyRunnable类实现Runnable接口

    -实验run()方法,编写线程执行体

    -创建线程对象,调用start()方法启动线程

    案例:

     //买票模拟
     package com.thread;
     ​
     //多个线程同时操作同一个对象
     public class Demo04_thread implements Runnable{
     ​
         //票数
         private int ticketNums = 10;
     ​
     ​
         @Override
         public void run() {
             while (true){
                 if (ticketNums<=0) {
                     break;
                }
                 try {
                     Thread.sleep(300);  //延时;让线程延时
                } catch (InterruptedException e) {
                     e.printStackTrace();
                }
                 System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNums-- + "张票");
            }
        }
     ​
         public static void main(String[] args) {
             Demo04_thread d4 = new Demo04_thread();
     ​
             new Thread(d4,"Joey").start();
             new Thread(d4,"Rose").start();
             new Thread(d4,"YellowCow").start();
        }
     }
     ​
     //模拟龟兔赛跑
     package com.thread;
     ​
     //模拟龟兔赛跑
     public class Race_thread implements Runnable{
     ​
         private static String Winner;
     ​
         @Override
         public void run() {
             for (int i = 0; i <= 100; i++) {
                 //模拟兔子睡觉
                 if (Thread.currentThread().getName().equals("兔子")&&i%10==0){
                     try {
                         Thread.sleep(3);
                    } catch (InterruptedException e) {
                         e.printStackTrace();
                    }
                }
                 //判断比赛是否结束
                 boolean flag = gameOver(i);
                 if (flag){
                     break;
                }
                 System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步");
     ​
            }
        }
     ​
         //判断是否完成比赛
         public boolean gameOver(int steps){
             //判断是否有胜利者
             if (Winner!=null){ //存在胜利者
                 return true;
            }else if (steps == 100){
                 Winner = Thread.currentThread().getName();
                 System.out.println("赢家是"+Winner);
                 return true;
            }
             return false;
        }
     ​
         public static void main(String[] args) {
             Race_thread race = new Race_thread();
     ​
             new Thread(race,"乌龟").start();
             new Thread(race,"兔子").start();
        }
     }
     ​

    12.3 Callable接口

    1、实现Callable接口,需要返回值类型

    2、重写call方法,需要抛出异常

    3、创建目标对象

    4、创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);

    5、提交执行:Future<Boolean> result1 = ser.submit(t1);

    6、获取结果:boolean r1 = result.get()

    7、关闭服务:ser.shutdownNow();

     package com.thread;
     ​
     ​
     import com.oop.inherit.B;
     import org.apache.commons.io.FileUtils;
     ​
     import java.io.File;
     import java.io.IOException;
     import java.net.URL;
     import java.util.concurrent.*;
     ​
     public class Callable_Test implements Callable<Boolean> {
         private String url;
         private String name;
     ​
         public Callable_Test(String url,String name){
             this.name=name;
             this.url=url;
        }
     ​
         //下载图片线程的执行体
         @Override
         public Boolean call(){
             Downloader Downloader = new Downloader();
             Downloader.downloader(url,name);
             System.out.println("下载的文件名"+name);
             return true;
        }
     ​
         public static void main(String[] args) throws ExecutionException, InterruptedException {
             Callable_Test c1 = new Callable_Test("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1592626650566&di=1eac2c6dfdab88fc21ed847c558fbde6&imgtype=0&src=http%3A%2F%2Fimg3.imgtn.bdimg.com%2Fit%2Fu%3D1154409108%2C2186845244%26fm%3D214%26gp%3D0.jpg","1.jpg");
             Callable_Test c2 = new Callable_Test("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1661014703,2331064073&fm=26&gp=0.jpg","2.jpg");
             Callable_Test c3 = new Callable_Test("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1592626863891&di=f9684430b5d5af9d3103a0dea98636a6&imgtype=0&src=http%3A%2F%2Fn1.itc.cn%2Fimg8%2Fwb%2Frecom%2F2016%2F07%2F02%2F146744791651796077.JPEG","3.jpg");
     ​
             //创建执行服务
             ExecutorService ser = Executors.newFixedThreadPool(3);
             //提交执行
             Future<Boolean> r1 = ser.submit(c1);
             Future<Boolean> r2 = ser.submit(c2);
             Future<Boolean> r3 = ser.submit(c3);
             //获取结果
             boolean rs1 = r1.get();
             boolean rs2 = r2.get();
             boolean rs3 = r3.get();
             //关闭服务
             ser.shutdown();
        }
     }
     ​
     //下载器
     class Downloader{
         //下载方法
         public void downloader(String url,String name){
             try {
                 FileUtils.copyURLToFile(new URL(url),new File(name));
            } catch (IOException e) {
                 e.printStackTrace();
                 System.out.println("IO异常,downloader出现问题");
            }
        }
     }

    12.4静态代理模式

     package com.thread;
     ​
     public class StaticProxy {
         public static void main(String[] args) {
     //       WeddingCompany weddingCompany = new WeddingCompany(new You());
     //       weddingCompany.HappyMarry();
             You you = new You();
             new Thread(new Runnable() {
                 @Override
                 public void run() {
                     System.out.println("I love you!");
                }
            }).start();
             new WeddingCompany(new You()).HappyMarry();
        }
     }
     ​
     interface Marry{
         void HappyMarry();
     }
     ​
     //真实角色
     class You implements Marry{
     ​
         @Override
         public void HappyMarry() {
             System.out.println("我要结婚了");
        }
     }
     ​
     //代理角色
     class WeddingCompany implements Marry{
         //真实目标角色(结婚者)
         private Marry target;
     ​
         public WeddingCompany(Marry target) {
             this.target = target;
        }
     ​
         @Override
         public void HappyMarry() {
             before();
             target.HappyMarry();  //真实对象结婚
             after();
        }
     ​
         private void after() {
             System.out.println("结婚之后,收尾款");
        }
     ​
         private void before() {
             System.out.println("结婚前布置现场");
        }
     }
     /**
      * 静态代理模式总结:
      * 1、真实对象和代理对象实现同一个接口(结婚这个事情接口)
      * 2、代理对象代理真实对象
      * 好处:
      * 1、代理角色可以做很多真实对象不方便做的事情
      * 2、真实对象可以专一的做某一件事
      * */

     

    12.5 Lamda表达式

    避免匿名内部类定义过多

    属于函数式编程概念

    lamda表达式

    -理解Functional Interface(函数式接口)是学习Java8 ``lamda表达式的关键所在

    -函数式接口定义:

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

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

     package com.lamda;
     ​
     /**
      * 推导lambda表达式
      * */
     public class Test01 {
         //3、静态内部类
        static class Like2 implements ILike{
             @Override
             public void lambda() {
                 System.out.println("I like lambda22!");
            }
        }
         public static void main(String[] args) {
             ILike like = new Like2();
             like.lambda();
     ​
             //4、局部内部类
             class Like3 implements ILike{
                 @Override
                 public void lambda() {
                     System.out.println("I like lambda33!");
                }
            }
             like = new Like3();
             like.lambda();
     ​
             //匿名内部类 ,没有类的名称,必须借助接口或者父类
             like = new ILike() {
                 @Override
                 public void lambda() {
                     System.out.println("I like lambda4!");
                }
            };
             like.lambda();
     ​
             //lambda简化
             like = ()->{
                 System.out.println("lambda55");
            };
             like.lambda();
        }
     }
     ​
     //1、定义一个接口(只包含一个抽象方法)
     interface ILike{
         void lambda();
     }
     ​
     //2、实现类
     class Like implements ILike{
         @Override
         public void lambda() {
             System.out.println("I like lambda!");
        }
     }
     package com.lamda;
     //lambda表达式的简化过程
     public class Test02 {
     ​
         public static void main(String[] args) {
     ​
             ILove love = null;
             //       ILove love = (int a)-> {
     //           System.out.println("I love yuo" + a);
     //       };
     //       love.love(520);
             //简化1
             love = (a)->{
                 System.out.println("I love yuo" + a);
            };
             love.love(8);
             //简化2
             love = a->{
                  System.out.println("I love yuo" + a);
            };
             love.love(521);
             //简化3
             love = a -> System.out.println("love"+a);
             love.love(999);
             /**
              * 总结:
              * 1、lambda表达式只能有一行代码的情况下才能简化为一行,多行则使用代码块
              * 2、前提:接口是函数式接口(仅有一个方法)
              * 3、多个参数也可以去掉参数类型(要去掉都去掉)必须加上括号
              *
              * */
        }
     }
     ​
     interface ILove{
         void love(int a);
     }
     ​
     //class Love implements ILove{
     //   @Override
     //   public void love(int a) {
     //       System.out.println("I love yuo1"+a);
     //   }
     //}

    12.6线程状态

    线程状态:

                          线程方法:

    方法说明
    setPriority(int newPriority) 更改线程的优先级
    static void sleep(long millis) 在指定毫秒数内让当前正在执行的线程休眠
    void join() 等待该线程终止
    static void yield() 暂停当前正在执行的线程对象,并执行其它线程
    void interrupt() 中断线程(不建议使用)
    boolean isAlive() 测试线程是否处于活动状态

    停止线程:

      -建立一个标志位进行终止变量;当flag=false则终止线程

     package com.thread;
     ​
     public class TestStop implements Runnable{
     ​
         //1、定义一个标志位
         private boolean flag = true;
         @Override
         public void run() {
             int i =0;
             //2、线程体使用该标识
             while (flag){
                 System.out.println("Running thread"+i++);
            }
        }
         //3、对外提供方法改变标识
         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 < 100; i++) {
                 System.out.println("main"+i);
                 if (i == 90){
      //               调用stop方法
                     testStop.stop();
                     System.out.println("线程停止了!");
                }
            }
        }
     }
     ​

    线程休眠

    -sleep(时间)指定当前线程阻塞的毫秒数;

    -sleep存在异常InterruptedException;

    -sleep时间到达后时间进入就绪状态

    -sleep可以模拟网络延时

    -每一个对象都有一个锁,sleep不会释放锁

     package com.thread;
     ​
     public class TestStop implements Runnable{
     ​
         //1、定义一个标志位
         private boolean flag = true;
         @Override
         public void run() {
             int i =0;
             //2、线程体使用该标识
             while (flag){
                 System.out.println("Running thread"+i++);
            }
        }
         //3、对外提供方法改变标识
         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 < 100; i++) {
                 System.out.println("main"+i);
                 if (i == 90){
      //               调用stop方法
                     testStop.stop();
                     System.out.println("线程停止了!");
                }
            }
        }
     }
     ​
     package com.thread;
     ​
     import java.text.SimpleDateFormat;
     import java.util.Date;
     ​
     //模拟倒计时
     public class TestStop02 {
         public static void main(String[] args) {
     //       try {
     //           tenDown();
     //       } catch (InterruptedException e) {
     //           e.printStackTrace();
     //       }
             //打印当前时间间隔一秒
             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 tenDown() throws InterruptedException {
             int num = 10;
     ​
             while (true){
                 Thread.sleep(1000);
                 System.out.println(num--);
                 if (num<=0){
                     break;
                }
            }
        }
     }
     ​

    线程强制执行——Join:

    -Join合并线程后,待此线程合并完成后,在执行其它线程,而其它线程则阻塞

     package com.thread;
     ​
     public class TestJoin implements Runnable{
         @Override
         public void run() {
             for (int i = 0; i < 100; 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==151){
                     thread.join();//插队
                }
                 System.out.println("main"+i);
            }
        }
     }
     ​

    12.7线程优先级

     package com.thread;
     ​
     public class TestPriority {
         public static void main(String[] args) {
             //主线程默认优先级
             System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
             MyPriority myPriority = new MyPriority();
             Thread t1 = new Thread(myPriority);
             Thread t2 = new Thread(myPriority);
             Thread t3 = new Thread(myPriority);
             Thread t4 = new Thread(myPriority);
     ​
             //先设置优先级,再启动
             t1.setPriority(Thread.MIN_PRIORITY); //1
             t1.start();
             t2.setPriority(2);
             t2.start();
             t3.setPriority(4);
             t3.start();
             t4.setPriority(Thread.MAX_PRIORITY); //10
             t4.start();
     ​
        }
     }
     ​
     class MyPriority implements Runnable{
     ​
         @Override
         public void run() {
             System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
     ​
        }
     }
     ​

    12.8守护线程

    线程分为用户线程守护线程

    虚拟机必须确保用户线程执行完毕

    虚拟机不用等待守护线程执行完毕

     package com.thread;
     ​
     //测试守护线程
     public class TestDaemon {
         public static void main(String[] args) {
             God god = new God();
             Self self = new Self();
     ​
             Thread thread = new Thread(god);
             thread.setDaemon(true);   //默认值是false表示用户线程
     ​
             thread.start();//上帝守护进程启动
     ​
             new Thread(self).start();//用户线程启动
        }
     }
     ​
     //上帝
     class God implements Runnable{
         @Override
         public void run() {
             while (true){
                 System.out.println("God bless you!");
            }
        }
     }
     ​
     //自己
     class Self implements Runnable{
     ​
         @Override
         public void run() {
             for (int i = 0; i < 365; i++) {
                 System.out.println("Happy alive!");
            }
             System.out.println("GoodBye! World!");
        }
     }

    12.9线程同步

    并发:同一个对象被多个线程同时操作。(形成条件:队列加锁)

    由于统一进程的多个线程共享同一块存储空间(方便但冲突),为了数据在方法中访问的正确性,必须加上锁机制(synchronized,当一个线程获得对象的排他锁,独占资源,其它线程必须等待,使用后释放锁即可;(公共厕所使用一个原理,代码源于生活)

    -一个线程持有锁会导致其它需要锁的线程处于挂起状态

    -在多线程竞争下加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题

    -如果一个优先级高的线程等待和一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题

     

    由于我们可以通过private关键字来保证数据只能被方法访问,针对方法提出一套机制

    synchronized关键字:synchronized方法synchronized块

    同步方法:public synchronized void methd(int args){}

    影响效率

    synchronized方法控制对“对象的访问”,每个对象对应着一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行。否则线程堵塞,就独占该锁,直到方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行。

    买票安全性改进

     package com.thread.syn;
     ​
     //不安全的买票,出现负数
     public class UnsafeBuyTicket {
         public static void main(String[] args) {
             BuyTicket bt = new BuyTicket();
     ​
             new Thread(bt,"me").start();
             new Thread(bt,"you").start();
             new Thread(bt,"YellowCow").start();
        }
     }
     class BuyTicket implements Runnable{
     ​
         //票
         private int ticketNum = 10;
         private boolean flag = true;
     ​
         @Override
         public void run() {
             //买票
             while (flag){
                 buy();
            }
        }
         //加锁的方法
         private synchronized void buy(){
             if (ticketNum <= 0){
                 flag = false;
                 return;
            }
             //模拟延时
             try {
                 Thread.sleep(100);
            } catch (InterruptedException e) {
                 e.printStackTrace();
            }
     ​
             //买票
             System.out.println(Thread.currentThread().getName()+"拿到"+ticketNum--);
     ​
        }
     }

     

    银行取钱安全化操作:

     package com.thread.syn;
     ​
     //不安全的取钱
     public class UnsafeBank {
         public static void main(String[] args) {
             Account account = new Account(100,"Study");
             Drawing you = new Drawing(account, 50, "You");
             Drawing girlFriend = new Drawing(account, 70, "GirlFriend");
     ​
             you.start();
             girlFriend.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;
        }
     ​
         //取钱
         //synchronized 默认锁的是this
         @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);
            }
        }
     }

    测试JUC安全类型集合

     package com.thread.syn;
     ​
     import java.util.concurrent.CopyOnWriteArrayList;
     ​
     //测试JUC安全类型集合
     public class TestJUC {
         public static void main(String[] args) {
             CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
             for (int i = 0; i < 1000; i++) {
                 new Thread(()->{
                     list.add(Thread.currentThread().getName());
                }).start();
            }
             try {
                 Thread.sleep(3000);
            } catch (InterruptedException e) {
                 e.printStackTrace();
            }
             System.out.println(list.size());
        }
     }

    12.10 死锁

    多个线程各自占有一些公共资源,并且相互等待其它线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止的情形。某一个同步块同时拥有两个以上对象的锁就可能会发生死锁的问题

     package com.thread.syn;
     ​
     //死锁:多个线程互相抱着对方需要的资源,然后形成僵持状态
     public class DeadLock {
         public static void main(String[] args) {
             MakeUp g1 = new MakeUp(0,"Rose");
             MakeUp g2 = new MakeUp(1, "Ruse");
     ​
             g1.start();
             g2.start();
     ​
     ​
        }
     }
     ​
     //口红
     class LipStick{
     ​
     }
     ​
     //镜子
     class Mirror{
     ​
     }
     ​
     //化妆
     class MakeUp extends Thread{
         //需要的资源只有一份,用static来保证只有一份
         static LipStick lipStick = new LipStick();
         static Mirror mirror = new Mirror();
     ​
         int choice;//选择
         String UserName;//化妆的人
         MakeUp(int choice,String UserName){
             this.choice=choice;
             this.UserName=UserName;
        }
     ​
     ​
         @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.UserName+"获得口红的锁");
                     Thread.sleep(1000);
                }
                 synchronized (mirror){
                     System.out.println(this.UserName+"获得镜子的锁");
                }
            }else {
                 synchronized (mirror){
                     //获得口红的锁
                     System.out.println(this.UserName+"获得镜子的锁");
                     Thread.sleep(2000);
                }
                 synchronized (lipStick){
                     System.out.println(this.UserName+"获得口红的锁");
                }
            }
        }
     }

    死锁避免的方法:

    产生死锁的四个必要条件:

    1、互斥条件:一个资源每次只能被一个进程使用

    2、请求与保持条件:一个进程因请求资源而阻塞,对以获得的资源保持不放

    3、不剥夺条件:进程已获得资源,在未使用完之前,不能强行剥夺

    4、循环等待条件:若干进程之间形成一种头尾相接的循环等待资源的关系

    破坏一个就可以避免死锁发生

    高级锁Lock ReentranLock:

     package com.thread.syn;
     ​
     ​
     import java.util.concurrent.locks.Lock;
     import java.util.concurrent.locks.ReentrantLock;
     ​
     //测试Lock锁
     public class TestLock {
         public static void main(String[] args) {
             TestLock2 lock2 = new TestLock2();
             new Thread(lock2).start();
             new Thread(lock2).start();
             new Thread(lock2).start();
        }
     }
     ​
     class TestLock2 implements Runnable{
     ​
         int ticketNum = 20;
     ​
         //定义Lock锁
         private final ReentrantLock lock = new ReentrantLock();
     ​
         @Override
         public void run() {
             while (true){
     ​
                 try{
                     //加锁
                     lock.lock();
                     if (ticketNum>0){
                         try {
                             Thread.sleep(1000);
                        } catch (InterruptedException e) {
                             e.printStackTrace();
                        }
                         System.out.println(ticketNum--);
                    }else {
                         break;
                    }
                }finally {
                     //解锁
                     lock.unlock();
                }
            }
        }
     }
     ​

    12.11线程通信

    Java提供的线程通信解决办法:

    方法名作用
    wait() 表示线程一直等待,直到其它线程通知,与sleep不同,会释放锁
    wait(long timeout) 指定等待的毫秒数
    notify() 唤醒一个处于等待状态的线程
    notifyAll() 唤醒同一个对象上所有的调用wait()方法的线程,按优先级别来调度
     //信号灯法解决生产者、消费者问题;标志位
     package com.thread.syn;
     ​
     //测试生产者、消费者问题2:信号灯法,标志位解决
     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("老友记ing");
                }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++) {
                 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.voice=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;
     ​
        }
     ​
     }
     //管程法;利用缓冲区解决生产者、消费者问题
     package com.thread.syn;
     ​
     //测试生产者消费者--》利用缓冲区解决问题:管程法
     //需要生产者、消费者、缓冲区
     public class TestPC {
         public static void main(String[] args) {
             SysContainer container = new SysContainer();
     ​
             new Producer(container).start();
             new Consumer(container).start();
     ​
        }
     }
     ​
     //生产者
     class Producer extends Thread{
         SysContainer container;
         public Producer(SysContainer container){
             this.container=container;
        }
     ​
         //生产
         @Override
         public void run() {
             for (int i = 0; i < 100; i++) {
                 container.push(new Product(i));
                 System.out.println("生成了"+i+"个产品");
            }
        }
     }
     ​
     //消费者
     class Consumer extends Thread{
         SysContainer container;
         public Consumer(SysContainer container){
             this.container=container;
        }
     ​
         //消费
         @Override
         public void run() {
             for (int i = 0; i < 100; i++) {
                 System.out.println("消费了"+container.pop().id+"个产品");
            }
        }
     }
     ​
     //产品
     class Product{
         int id;
     ​
         public Product(int id) {
             this.id = id;
        }
     }
     ​
     //缓冲区
     class SysContainer{
         //容器大小
         Product[] products=new Product[10];
     ​
         //容器计数器
         int count = 0;
     ​
         //生产者放入产品(因为是需要变动的值,所以需要安全性考虑)
         public synchronized void push(Product product){
             //如果容器满了,则需要等待消费者消费
             if (count==products.length){
                 //通知消费者消费,生产者停止生成
                 try{
                     this.wait();
                }catch (InterruptedException e){
                     e.printStackTrace();
                }
            }
             //如果没有满,则生产者继续生产产品
             products[count]=product;
             count++;
             //通知消费者消费
             this.notifyAll();
        }
         //消费者消费产品(安全考虑)
         public synchronized Product pop(){
             //判断能否消费
             if (count==0){
                 //等待生产者生产
                 try{
                     this.wait();
                }catch (InterruptedException e){
                     e.printStackTrace();
                }
            }
             //count!=0,则可以消费
             count--;
             Product product = products[count];
             //消费完了,则通知生产者生产
             this.notifyAll();
             return product;
        }
     }

     

    12.12线程池

    经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大

    思路:提前创建好多个线程,放入线程池中,使用时直接获取即可,使用完放回池中,

    可以避免频繁的创建销毁线程,实现重复利用。

    好处:

    • 提高响应速度(减少了创建新线程的时间)

    • 降低资源的消耗(重复利用线程池中的线程,不需要每次都创建)

    • 便于线程的管理

      • 核心池的大小

      • 最大线程数量

      • 线程没有任务时最多保持多长时间会终止

      线程池相关的APIExecutorService & Executors

    ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor

     void execute(Runable command) //执行任务/命令;没有返回值,一般用来执行Runable
     <T>Future<T>submit(Callable<T>task) //执行任务,有返回值,一般来执行Callable
     void shutdown() //关闭连接池

    Executors:工具类、线程池的工厂类,用于创建和返回不同类型的线程池

    12.13总结代码

     package com.thread.sum;
     import java.util.concurrent.Callable;
     import java.util.concurrent.ExecutionException;
     import java.util.concurrent.Future;
     import java.util.concurrent.FutureTask;
     ​
     //总结线程创建
     public class SumUpThread {
         public static void main(String[] args) {
             //1、继承Thread类是调用
             new MyThread1().start();
             //2、继承Runnable接口是调用
             new Thread(new MyThread2()).start();
             //3、继承Callable接口调用方法
             FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3());
             new Thread(futureTask).start();
             try {
                 Integer integer = futureTask.get();
                 System.out.println(integer);
            } catch (InterruptedException e) {
                 e.printStackTrace();
            } catch (ExecutionException e) {
                 e.printStackTrace();
            }
        }
     }
     //1、继承Thread类
     class MyThread1 extends Thread{
         @Override
         public void run() {
             System.out.println("MyThread1");
        }
     }
     //2、实现Runnable接口
     class MyThread2 implements Runnable{
         @Override
         public void run() {
             System.out.println("MyThread2");
        }
     }
     //3、实现Callable接口
     class MyThread3 implements Callable<Integer>{
         @Override
         public Integer call() throws Exception {
             System.out.println("MyThread3");
             return 100;
        }
     }

     

  • 相关阅读:
    兄弟连学python(1)——MySQL
    运算和运算符相关知识
    关于python中的快捷键
    关于爬虫
    Hello Python
    [ARC101C] Ribbons on Tree
    CF568E Longest Increasing Subsequence
    2021省选游记
    [NEERC2015]Distance on Triangulation
    dp的一些优化
  • 原文地址:https://www.cnblogs.com/joey-413/p/13234933.html
Copyright © 2011-2022 走看看