java之多线程

java多线程

1.线程的创建的三种方式

  1. 继承Thread类

    • 自定义线程类继承Thread类
    • 重写run()方法,编写线程执行体
    • 创建线程对象,调用start()方法启动线程
    public class TestThread extends Thread {
        @Override
        public void run() {
            //run方法为线程体
            for (int i = 0; i < 20; i++) {
                System.out.println("我在写代码---"+i);
            }
        }
    
        public static void main(String[] args) {
            TestThread thread1=new TestThread();
            thread1.start();
    
            for (int i = 0; i < 20; i++) {
                System.out.println("主线程我在写代码--"+i);
            }
        }
    }
    
    

    案例下载图片:

    //练习Thread ,实现多线程同步下载图片
    public class TestThread2 extends Thread{
    
        private String name;
        private String url;
        public  TestThread2(String url,String name){//注意构造方法没有返回值类型
            this.name=name;
            this.url=url;
        }
        //2.下载图片的线程执行体
    
        @Override
        public void run() {
            DownloaderWeb downloaderWeb=new DownloaderWeb();
            downloaderWeb.downloader(url,name);
            System.out.println("下载图片----》"+name);
        }
    
        //3.创建线程 https://q4.qlogo.cn/g?b=qq&nk=1518574228@qq.com&s=3?d=retro
        public static void main(String[] args) {
            TestThread2 thread1=new TestThread2("https://q4.qlogo.cn/g?b=qq&nk=1518574228@qq.com&s=3?d=retro","1.jpg");
            TestThread2 thread2=new TestThread2("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1594486623886&di=d2a2cb3cdcd65080e0ab784b4e2d49fd&imgtype=0&src=http%3A%2F%2Ft8.baidu.com%2Fit%2Fu%3D3571592872%2C3353494284%26fm%3D79%26app%3D86%26f%3DJPEG%3Fw%3D1200%26h%3D1290","2.jpg");
            thread1.start();
            thread2.start();
        }
    }
    //1.下载器
    class DownloaderWeb{
        public  void downloader(String url,String name){
            try {
                FileUtils.copyURLToFile(new URL(url),new File(name));//文件工具类
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("下载器发生异常");
    
            }
        }
    }
    
  2. 实现Runnable接口

    • 自定义线程类实现Runnable接口
    • 实现run()方法,编写线程执行体
    • 创建自定义线程类对象,并在创建Thread时作为参数传入,然后调用start()方法启动

    推荐使用第二种方法,因为可以用多个线程跑一个对象

    public class TestThreadRun implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                System.out.println("我是子线程----》"+i);
            }
        }
    
        public static void main(String[] args) {
            TestThreadRun thread1=new TestThreadRun();
            new Thread(thread1).start();
            for (int i = 0; i < 20; i++) {
                System.out.println("主线程输出-----》"+i);
            }
        }
    }
    

    多个线程操作一个对象的例子

    买票:

    public class TestThread implements Runnable  {
        private int num=10;
        @Override
        public void run() {
            while(true)//只要票没取完,就一直取票
            {
                if(num<=0){
                    break;
                }
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"线程取到第"+(num--)+"票");
            }
        }
    
        public static void main(String[] args) {
            TestThread ticket =new TestThread();
            new Thread(ticket,"小明").start();
            new Thread(ticket,"老师").start();
            new Thread(ticket,"黄牛党").start();
        }
    }
    
    
    
    黄牛党线程取到第10票
    老师线程取到第9票
    小明线程取到第8票
    黄牛党线程取到第6票
    小明线程取到第7票
    老师线程取到第5票
    小明线程取到第4票
    老师线程取到第3票
    黄牛党线程取到第3票
    黄牛党线程取到第2票
    小明线程取到第1票
    老师线程取到第2票
    
    进程完成,退出码 0
    

    **发现问题:**老师和黄牛党同时取到第三张票。多个线程操作同一个资源的情况下,线程不安全,数据紊乱。

    案例:龟兔赛跑

    1. 首先来个赛道距离
    2. 判断比赛是否结束,即是否有胜利者
    3. 打印胜利者
    4. 同时要模拟兔子睡觉
    package com.javademo.day7_13;
    
    public class Race implements Runnable {
        //胜利者,用static保证胜利者唯一
        private static String winner;
        @Override
        public void run() {
    
            for (int i = 0; i <=100; i++) {//赛道距离等于100  两个线程都执行各自的for循环,并发执行  上一个例子三个线程共享同一变量num
                //模拟兔子睡觉
                if(Thread.currentThread().getName().equals("兔子-->") && i%10==0) {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                boolean flag=gameOver(i);
                if(flag)
                    break;
                System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");
            }
        }
        private boolean gameOver(int steps){//要判断是否已经有胜利者
            if(winner==null){
                if(steps>=100)
                {
                    winner=Thread.currentThread().getName();
                    System.out.println(Thread.currentThread().getName()+"跑了"+steps+"步");
                    System.out.println("winner is "+winner);
                    return true;
                }
               return  false;
            }
            else{
                return true;
            }
    
        }
    
        public static void main(String[] args) {
            Race race=new Race();
            new Thread(race,"兔子-->").start();
            new Thread(race,"乌龟").start();
        }
    }
    
    
  3. 实现Callable接口(了解即可)

    1. 实现Callable接口,确定call方法的返回值类型

    2. 重写call方法

    3. 创建目标对象,也就是Callable的接口实现类对象

    4. 创建执行服务(线程池)

    5. 提交执行(submit 提交线程)

    6. 获取结果

    7. 关闭服务

    package com.javademo.day7_14;
    
    import com.javademo.day7_11.TestThread2;
    import org.apache.commons.io.FileUtils;
    
    import java.io.File;
    import java.io.IOException;
    import java.net.URL;
    import java.util.concurrent.*;
    
    public class TestCallable implements Callable<Boolean> {//泛型确定call的返回值类型
    
        private String name;
        private String url;
        public  TestCallable(String url,String name){
            this.name=name;
            this.url=url;
        }
    
    
        @Override
        public Boolean call() {
           DownloaderWeb downloaderWeb=new DownloaderWeb();
            downloaderWeb.downloader(url,name);
            System.out.println("下载图片----》"+name);
            return  true;
        }
    
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            TestCallable thread1=new TestCallable("https://q4.qlogo.cn/g?b=qq&nk=1518574228@qq.com&s=3?d=retro","3.jpg");
            TestCallable thread2=new TestCallable("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1594486623886&di=d2a2cb3cdcd65080e0ab784b4e2d49fd&imgtype=0&src=http%3A%2F%2Ft8.baidu.com%2Fit%2Fu%3D3571592872%2C3353494284%26fm%3D79%26app%3D86%26f%3DJPEG%3Fw%3D1200%26h%3D1290","4.jpg");
            //创建执行服务
            ExecutorService ser= Executors.newFixedThreadPool(2);//创建两个线程
    
            //提交执行 线程运行
            Future<Boolean> r1=ser.submit(thread1);
            Future<Boolean> r2=ser.submit(thread2);
    
            //获取结果,执行结果,此处始终返回true
            boolean rs1=r1.get();
            boolean rs2=r2.get();
    
            //关闭服务
            ser.shutdown();
    
    
        }
    }
    //1.下载器
    class DownloaderWeb{
        public  void downloader(String url,String name){
            try {
                FileUtils.copyURLToFile(new URL(url),new File(name));//文件工具类
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("下载器发生异常");
    
            }
        }
    }
    
    

2.静态代理

真实对象和代理对象实现同一个接口,代理对象代理一个真实对象,可以通过代理对象的构造函数传入。

例子:chrome浏览器收藏夹

//创建线程  采用的是静态代理 因为Thread也实现了Runnable接口
new Thread(()->System.out.println("我爱你")).start();//lamda表达式
//对比如下
new Cinema(new RealMovie).play();

3.lamda表达式

  1. 函数式接口:
    • 任何接口如果它只包含一个抽象方法,那么它就是一个函数式接口
    • 对于函数式接口,我们可以通过Lamda表达式来创建该接口的对象
package com.javademo.day7_14;

public class TestLambda1 {
    //1.匿名内部类
    public static void main(String[] args) {


    Animal a =new Animal() {
        @Override
        public void sound() {
            System.out.println("我是匿名内部类的sound");
        }
    };
    a.sound();
        
    //2.通过lambda表达式继续简化
   a=()->{
        System.out.println("Lambda的sound");
    };
    a.sound();
}
}
interface  Animal{
    void sound();
 }

带参数的lambda表达式

package com.javademo.day7_14;

public class TestLambda {
    public static void main(String[] args) {
    //匿名内部类
        Like like1=null;
      like1=new Like() {
        @Override
        public void like(int a, int b) {
            System.out.println("匿名类---》"+a+"另一个参数--》"+b);
        }
    };
      like1.like(1,2);

      //Lambda表达式,可以简化去掉参数的类型,当语句块中只用一个语句的时候,大括号也可以省略
        like1=(a,b)->{
            System.out.println("Lambda参数1:"+a);
            System.out.println("Lambda参数2:"+b);
        };
        like1.like(4,5);
}
}
interface  Like{
    void like(int a,int b);
}

4.线程停止

  1. 状态:创建状态,就绪状态,阻塞状态,运行状态,死亡状态
  2. 停止线程:一般使用一个标志位进行终止变量,当flag=false时终止线程运行
public class TestThreadStop implements  Runnable {
    //1.设置一个标志位
    private  boolean flag=true;
    @Override
    public void run() {
        int i=0;
        while(flag)
            System.out.println("Run---------->"+(i++));
    }

    //2.设置一个公开的方法停止线程,转换标志位
    public void  stop(){
        this.flag=false;
    }

    public static void main(String[] args) {
        TestThreadStop testThreadStop=new TestThreadStop();
        new Thread(testThreadStop).start();
        for (int i = 0; i < 20; i++) {
            System.out.println("main--->"+i);
            if(i==18){
                //调用stop方法切换标志位,让线程停止

                testThreadStop.stop();
                System.out.println("执行stop线程停止");
            }
        }

    }
}

5.线程休眠(Thread的静态方法)

  1. 用sleep模拟倒计时

    public class Down {
        public static void main(String[] args) {
            try {
                Down.timeDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            ;
        }
    
        public static void timeDown() throws InterruptedException {
            int num=10;
            while(true){
                Thread.sleep(1000);//1000ms等于1s
                System.out.println(num--);
                if(num<0){
                    break;
                }
            }
        }
    }
    
    
  • sleep指定当前线程阻塞的毫秒数
  • sleep存在异常InterruptedException
  • sleep时间达到后就进入就绪状态
  • sleep可以模拟网络延迟和倒计时
  • 每个对象都有一个锁,sleep不会释放锁
import java.text.SimpleDateFormat;
import java.util.Date;

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

6.线程礼让 yield(Thread的静态方法)

  • 礼让线程,让当前执行的线程暂停,转入就绪状态。
  • 让cpu重新调度,礼让不一定成功,看CPU心情,因为,礼让之后,线程又同时竞争cpu,如果还是原来的线程占有cpu则没有礼让成功。
public class YeildTest {
    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()+"线程停止执行");
    }
}

a线程开始执行
b线程开始执行
a线程停止执行
b线程停止执行

7.线程强制执行join

可想象为插队,注意插队之后必须待此线程执行完,才能执行其他线程。

public class TestJoin implements  Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("join插队,vip线程,一旦占用cup就执行完------->"+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 < 20; i++) {
            System.out.println("main线程"+i);
            if(i==10){
                System.out.println("vip线程强制占用cpu");
                thread.join();

            }


        }

    }
}

8.线程状态观测

public class TestState {
 /*
 public static enum Thread.State
extends Enum<Thread.State>线程状态。 线程可以处于以下状态之一:
NEW
尚未启动的线程处于此状态。
RUNNABLE
在Java虚拟机中执行的线程处于此状态。
BLOCKED
被阻塞等待监视器锁定的线程处于此状态。
WAITING
正在等待另一个线程执行特定动作的线程处于此状态。
TIMED_WAITING
正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
TERMINATED
已退出的线程处于此状态。
一个线程可以在给定时间点处于一个状态。 这些状态是不反映任何操作系统线程状态的虚拟机状态。

*/
    //通过Lambda表达式来创建线程,因为线程的自定义实现类和Thread都实现Runable接口,因此Thread相当于线程的自定义实现类的代理,
    // 可以通过lambda表达式,创建类
    public static void main(String[] args) throws InterruptedException {
    Thread thread=new Thread(()->{
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("for循环结束");
    });
    //观察状态
    Thread.State state=thread.getState();
    System.out.println(state);//NEW

    //观察启动后
      thread.start();
      state=thread.getState();
      System.out.println(state);//RUNNABLE

     //线程不终止,一直输出状态
     while(state !=Thread.State.TERMINATED){
         Thread.sleep(1000);
         state=thread.getState();
         System.out.println(state);
     }
    }
}

NEW
RUNNABLE
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
for循环结束
TERMINATED

9. 线程优先级

public class TesPriority implements Runnable {
    public static void main(String[] args) {
        //主线程的默认优先级
        System.out.println(Thread.currentThread().getName()+"线程优先级"+Thread.currentThread().getPriority());
        TesPriority tesPriority=new TesPriority();
         Thread t1=new Thread(tesPriority);
        Thread t2=new Thread(tesPriority);
        Thread t3=new Thread(tesPriority);
        Thread t4=new Thread(tesPriority);

        //先设置优先级,在启动
        t1.setPriority(3);
        t1.start();
        t2.setPriority(8);
        t2.start();
        t3.setPriority(1);
        t3.start();
        t4.setPriority(Thread.MAX_PRIORITY);//MAX_PRIORITY=10  可以看源码
        t4.start();
    }
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程优先级"+Thread.currentThread().getPriority());
    }
}


main线程优先级5
Thread-3线程优先级10
Thread-1线程优先级8
Thread-0线程优先级3
Thread-2线程优先级1

10.守护(daemon)线程

  • 线程分为用户线程和守护线程
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护进程执行完毕
  • 如:后台记录操作日志,监控内存,垃圾回收
package com.javademo.day7_15;

public class TestDaemon {
    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 < 5; i++) {
            System.out.println("------------------你活了第"+(i+1)+"天-------------------");

        }
        System.out.println("======bye world========");
    }
}

11.线程同步

  • 并发:多个线程访问同一个资源

  • 线程同步其实是一个排队机制,

  • 队列加锁才能实现线程同步的安全性。

  • synchronized

12.三大不安全案例

  1. 买票
package com.javademo.day7_16;
//不同的人买到相同的票数,线程不安全
public class UnsafeBuyTicket {
    public static void main(String[] args) {
        BuyTicket buyTicket=new BuyTicket();
        new Thread(buyTicket,"老师").start();
        new Thread(buyTicket,"学生").start();
        new Thread(buyTicket,"黄牛党").start();
    }
}
class BuyTicket implements Runnable{
    //票数
    private  int num=10;
    private  boolean flag=true;
    @Override
    public void run() {
        while(flag){
            buy();
        }
    }

    private void buy(){
        if(num<=0){
            flag=false;
            return;
        }
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"线程买到"+num--);

    }
}
  1. 银行取钱
public class UnsafeBank1 {
    public static void main(String[] args) {
        Account1 account=new Account1(100,"结婚基金");
        Thread getBankMoney1=new GetBankMoney1(50,account,"You");
        Thread getBankMoney2=new GetBankMoney1(100,account,"Your GirlFriend");
        getBankMoney1.start();
        getBankMoney2.start();
    }
}
class Account1{
    public Account1(int nowMoney,String accountName){
        this.accountName=accountName;
        this.nowMoney=nowMoney;

    }
    int nowMoney;
    String accountName;
}

class GetBankMoney1 extends  Thread {
    int getMoney;
    Account1 account;

    public GetBankMoney1(int getMoney, Account1 account,String name) {//不是操作同一对象因为取钱数不同,
        // 只不过是操作同一账户,所以给出Account类操作同一个account对象,而不是同一个线程实现类对象
        super(name);//传入线程名称
        this.account = account;
        this.getMoney = getMoney;
    }

    @Override
    public void run() {
        if (account.nowMoney - getMoney < 0) {
            System.out.println(Thread.currentThread().getName() + "余额不足取不了");
            return;
        }
        //sleep可以放大问题的发生性,以便模拟不安全的情况,即同时取钱
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        account.nowMoney = account.nowMoney - getMoney;
        System.out.println(account.accountName + "余额:" + account.nowMoney);
        System.out.println(Thread.currentThread().getName() + "手里的钱增加" + getMoney);
    }

}


结婚基金余额:50
You手里的钱增加50
结婚基金余额:-50
Your GirlFriend手里的钱增加100
    
问题:余额50,依旧取出100
  1. ArrayList不安全
public class UnsafeArr {
    public static void main(String[] args) {
        ArrayList<String > arr=new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            new Thread(()->{
               arr.add(Thread.currentThread().getName());
            }).start();
        }
        System.out.println(arr.size());
    }
}

767
分析:两个线程同一时刻操作同意位置,那么后一个就会覆盖前一个,所以最后size大小不够1000

13.同步方法和同步块

  • 同步方法:

    **由于我们可以通过private关键字来保证数据对象只能被方法访问,所以我们只需要对方法提出一套机制即可,这套机制就是synchronized关键字,**它包括两种用法:synchronzied方法和synchronized块。

    synchronized控制对"对象"的访问,每个对象有一把锁,每个synchronized方法都必须获得调用该方法的对象的锁后,才能执行,否则线程就会阻塞,方法一旦执行就独占该锁,直到方法返回后才释放锁,后面被阻塞的线程才能获得这个锁,继续执行。缺陷:若一个大的方法申明为synchronized将会影响效率

修改不安全的案例使其安全:

  1. 买票:buy方法返回值类型前加synchronized

    //synchronized锁的是this 对象也就是 BuyTicket对象
        private synchronized  void buy(){
            if(num<=0){
                flag=false;
                return;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"线程买到"+num--);
    
        }
    

2.取钱,通过synchronized块使其变得线程安全

  • 同步块:synchronized(Obj){}

    Obj称之为同步监视器

    • Obj可以是任何对象,但是推荐使用共享资源作为同步监视器
    • 同步方法中无需指定同步监视器,因为同步方法的监视器就是this,就是这个对象本身。
public class UnsafeBank1 {
    public static void main(String[] args) {
        Account1 account=new Account1(1000,"结婚基金");
        Thread getBankMoney1=new GetBankMoney1(50,account,"You");
        Thread getBankMoney2=new GetBankMoney1(100,account,"Your GirlFriend");
        getBankMoney1.start();
        getBankMoney2.start();
    }
}
class Account1{
    public Account1(int nowMoney,String accountName){
        this.accountName=accountName;
        this.nowMoney=nowMoney;

    }
    int nowMoney;
    String accountName;
}

class GetBankMoney1 extends  Thread {
    int getMoney;
    Account1 account;

    public GetBankMoney1(int getMoney, Account1 account, String name) {
        //不是操作同一GetBankMoney1对象因为取钱数不同,
        // 只不过是操作同一账户,所以main方法操作同一个account对象,而不是同一个线程实现类对象
        super(name);//传入线程名称
        this.account = account;
        this.getMoney = getMoney;
    }

    //注意我们要锁的一个是account对象,而不是run,因为创建的是两个GetBankMoney1对象,account才是临界资源
    @Override
    public void run() {
        while (true) {
            //sleep可以放大问题的发生性,睡眠以便有机会使其他进程进行取钱
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        synchronized (account) {

                if (account.nowMoney - getMoney < 0) {

                    System.out.println(Thread.currentThread().getName() + "余额不足取不了");
                    break;
                }
                
           

                account.nowMoney = account.nowMoney - getMoney;
                System.out.println(account.accountName + "余额:" + account.nowMoney);
                System.out.println(Thread.currentThread().getName() + "手里的钱增加--------->" + getMoney);
            }
        }
    }
}

14.CopyOnWriteArrayList

线程安全的ArrayList

15.死锁

public class TestDeadLock {
    public static void main(String[] args) {
        Makeup g1=new Makeup(0,"灰姑凉");
        Makeup g2=new Makeup(1,"白雪公主");
        g1.start();
        g2.start();
    }
}

class Lipstick{

}

class Mirror{

}
class Makeup extends  Thread{
    //需要的资源只有一份,用static来保证只有一份
    static Lipstick lipstick=new Lipstick();
    static Mirror mirror=new Mirror();

    int id;
    String name;
    public Makeup(int id,String name){
        this.id=id;
        this.name=name;

    }
    @Override
    public void run() {
        super.run();
        makeup();
    }
    private void makeup(){
        if(id==0){
            synchronized (mirror) {//0号先取得镜子的资源
                System.out.println(this.name + "获得了镜子");
                synchronized (lipstick){
                    System.out.println(this.name+"获得口红资源");
                }
            }
        }
        else{
            synchronized (lipstick) {//1号先取得口红的资源
                System.out.println(this.name + "获得口红资源");
                synchronized (mirror){
                    System.out.println(this.name+"获得了镜子");
                }
            }
        }
    }

如果按如下修改run,则不会发生死锁

 private void makeup(){
        if(id==0){
            synchronized (mirror) {//0号先取得镜子的资源
                System.out.println(this.name + "获得了镜子");

                }
            synchronized (lipstick){
                System.out.println(this.name+"获得口红资源");
            }
        }
        else{
            synchronized (lipstick) {//1号先取得口红的资源
                System.out.println(this.name + "获得口红资源");

                }
            synchronized (mirror){
                System.out.println(this.name+"获得了镜子");
            }
        }

死锁的四个必要提交(逆否命题,缺一就不会发生死锁)

  1. 互斥条件
  2. 请求保持
  3. 不可剥夺
  4. 环路等待

16.Lock锁

Lock是接口,通过ReentrantLock可重入锁,来显式的加锁和释放锁。

格式:

class A{
    private final ReenTrantLock lock=new ReenTrantLock();
    public void m(){
        lock.lock();
        try{
            //保证线程安全的代码
        }
        finally{
            lock.unlock();
            //如果同步代码有异常,需要把unlock()写在finally语句块中
        }
    }
}

synchronized和Lock对比

  1. lock是显式锁,synchronized是隐式锁,lock需要手动的开启和关闭锁,synchronized是隐式锁,出了作用域自动释放。
  2. Lock只有代码块锁,synchronized既有代码块锁又有方法锁。

17.生产者消费者问题

此前其实是线程互斥,此处是线程同步,即线程之间要相互协作,或者是直接制约。

  1. wait():表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁。

  2. notify():唤醒一个处于等待状态的线程

  3. notifyAll():唤醒同一个对象上调用wait()方法的线程,优先级别高的线程优先调度

    以上均是Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出IllegalMonitorStateException

18.管程法解决生产者消费者问题

package com.javademo.day7_18;

import java.util.concurrent.locks.ReentrantLock;

//管程法
public class TestPC {
    public static void main(String[] args) throws InterruptedException {
        //生产者和消费者要使用同一个缓冲区,因此建立一个缓冲区,然后将其传入消费者和生产者中
           BufferPC buffer=new BufferPC();
           Consumer consumer=new Consumer(buffer);
           Productor productor=new Productor(buffer);
          productor.start();
           consumer.start();

    }
}
//消费者
class Consumer extends  Thread{
    BufferPC bufferPC;
    public Consumer(BufferPC bufferPC){
        this.bufferPC=bufferPC;
    }
    @Override
    public void run() {
        for (int i = 0; i < 16; i++) {
         bufferPC.pop();
        }

    }
}
//生产者
class Productor extends  Thread{
    BufferPC bufferPC;
    public  Productor(BufferPC bufferPC){
        this.bufferPC=bufferPC;
    }
    @Override
    public void run() {
        for (int i = 0; i < 16; i++) {

            bufferPC.push();

        }

    }
}
//生产和消费的物品
class Good{
    int post;

    public Good(int post) {
        this.post = post;
    }
}

//缓冲区 就是管程
class BufferPC{
    private  Good[] goods=new Good[10];
   private int count=0;
    private int copyCount;
    //生产者生产
    public synchronized  void push(){
        if(count==goods.length)//缓冲区满,则让生产者等待,否则生产者生产。
        {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        goods[count]=new Good(count);
        System.out.println("在位置"+count+"生产");
        //通知消费
        this.notifyAll();
        count++;

    }

    //消费者消费
    public synchronized void pop(){
        if(count==0){
            //没有产品,消费者停止消费
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        count--;
        System.out.println("消费位置"+count+"上的物品");

        //通知生产
        this.notifyAll();

    }

}

19.线程池

使用线程池:

  1. ExecutorService:真正的线程池接口,常见的子类,ThreadPoolExecutor

  2. void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable

  3. Future subimit (Callable task):执行任务,有返回值,一般用来执行

    Callable

  4. void shutdown():关闭线程池

    public class TestThreadPool {
        public static void main(String[] args) {
            //1.创建线程池,参数为线程池的大小
            ExecutorService service=Executors.newFixedThreadPool(2);
            //2.执行
            service.execute(new my());
            service.execute(new my());
            service.execute(new my());
            service.execute(new my());
            //3.关闭服务
            service.shutdown();
        }
    
    
    }
    
    class my implements Runnable{
    
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
    }
    
    
    pool-1-thread-1
    pool-1-thread-2
    pool-1-thread-1
    pool-1-thread-2
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值