导航

Java多线程

Posted on 2020-07-04 15:01  乔伊_413  阅读(110)  评论(0编辑  收藏  举报

 

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;
    }
 }