多线程编程学习笔记

概述

笔记参照b站up主:遇见狂神说的视频,链接https://www.bilibili.com/video/BV1V4411p7EF

狂神:只要学不死,就往死里学

1、多线程实现方式

  • 继承Thread类
  • 实现runable接口
  • 实现callable接口

2、继承Thread

1、使用

package com.xwy;

/**
 * @author levi
 * @create 2020/9/13 5:11 下午
 */
//创建线程方法1 集成Thread
public class TestThread01 extends Thread{
    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("我在看代码。。。。"+i);
        }
    }
    public static void main(String[] args) {
        //main线程 ,主线程
//        创建线程对象
        TestThread01 testThread01 = new TestThread01();
//        调用start方法开启线程
        testThread01.start();
        for (int i = 0; i < 2000; i++) {
            System.out.println("我在学多线程。。。。"+i);
        }
    }
}

2、网图下载

package com.xwy;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

/**
 * @author levi
 * @create 2020/9/13 5:31 下午
 */
public class TestThread02 extends Thread{
    private String url;
    private String name;
    public TestThread02(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) {
        TestThread02 t1= new TestThread02("http://127.0.0.1:8000/file/1.jpg","1.jpg");
        TestThread02 t2= new TestThread02("http://127.0.0.1:8000/file/1.jpg","2.jpg");
        TestThread02 t3= new TestThread02("http://127.0.0.1:8000/file/1.jpg","3.jpg");
        t1.start();
        t2.start();
        t3.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方法出现错误");
        }
    }
}

3、实现Runable接口

创建线程方式二:实现runable接口,重写run方法,执行线程需要丢入runable接口实现类,调用start方法

package com.xwy;

/**
 * @author levi
 * @create 2020/9/13 6:06 下午
 */
//创建线程方式二:实现runable接口,重写run方法,执行线程需要丢入runable接口实现类,调用start方法
public class TestThread03 implements Runnable{
    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("我在看代码。。。。"+i);
        }
    }
    public static void main(String[] args) {
//      创建runable接口的实现对象
        TestThread03 testThread03 = new TestThread03();
//      创建线程对象
        Thread thread = new Thread(testThread03);
//      调用start方法开启线程
        thread.start();

//      new Thread(testThread03).start();
        
        for (int i = 0; i < 2000; i++) {
            System.out.println("我在学多线程。。。。"+i);
        }
    }
}

建议使用实现Runable接口的方式,因为java的单继承局限性

4、并发问题

package com.xwy;

/**
 * @author levi
 * @create 2020/9/13 6:14 下午
 */
public class TestThread04 implements Runnable{
    private int ticketNums = 10;

    @Override
    public void run() {
        while (true){
            if(ticketNums <= 0){
                break;
            }
//            模拟延时
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"张票");
        }
    }
    public static void main(String[] args) {
        TestThread04 ticket = new TestThread04();
        new Thread(ticket,"小明").start();
        new Thread(ticket,"老师").start();
        new Thread(ticket,"黄牛").start();
    }
}

小明拿到了第8张票
黄牛拿到了第10张票
老师拿到了第9张票
小明拿到了第7张票
老师拿到了第6张票
黄牛拿到了第7张票
老师拿到了第5张票
小明拿到了第5张票
黄牛拿到了第5张票
老师拿到了第4张票
黄牛拿到了第3张票
小明拿到了第4张票
黄牛拿到了第2张票
老师拿到了第2张票
小明拿到了第1张票

出现问题:同一张票被多人拿到

6、龟兔赛跑

package com.xwy;

/**
 * @author levi
 * @create 2020/9/13 6:22 下午
 */
public class Race 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(100);
                } 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){
            return true;
        }{
            if (steps>=100){
                winner = Thread.currentThread().getName();
                System.out.println("winner is "+winner);
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        Race race = new Race();
        new Thread(race,"兔子").start();
        new Thread(race,"乌龟").start();
    }
}

这里我设置成10ms兔子也参与不进来。。。,可能电脑运行太快了吧 假装不开心~

7、实现Callable接口

用callable重新实现图片下载

package com.xwy02;

import com.xwy.TestThread02;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;

/**
 * @author levi
 * @create 2020/9/13 8:54 下午
 */

public class TestCallable implements Callable<Boolean> {
    private String url;
    private String name;
    public TestCallable(String url,String name){
        this.name = name;
        this.url = url;
    }
    @Override
    public Boolean call() {
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downloader(url,name);
        System.out.println("下载了文件名为"+name+"的文件");
        return  true;
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestCallable t1= new TestCallable("http://127.0.0.1:8000/file/1.jpg","1.jpg");
        TestCallable t2= new TestCallable("http://127.0.0.1:8000/file/1.jpg","2.jpg");
        TestCallable t3= new TestCallable("http://127.0.0.1:8000/file/1.jpg","3.jpg");
//        1创建执行服务
        ExecutorService ser = Executors.newFixedThreadPool(3);
//        2提交执行
        Future<Boolean> r1 = ser.submit(t1);
        Future<Boolean> r2 = ser.submit(t2);
        Future<Boolean> r3 = ser.submit(t3);
//        3获取结果
        boolean rs1 = r1.get();
        boolean rs2 = r2.get();
        boolean rs3 = r3.get();
//        4关闭服务
        ser.shutdown();
    }
}
//下载器
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方法出现错误");
        }
    }
}

callable的好处:

  • callable可以定义返回值
  • 可以抛出异常

8、静态代理

结婚:

  • 你:真实角色
  • 婚庆公司: 代理你,帮你处理结婚的事情
  • 结婚:实现都实现结婚接口

静态代理模式:

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

好处:

  • 代理对象可以做好多真实对象做不了的事情
  • 真实对象专注做自己的事情
package com.proxystate;

/**
 * @author levi
 * @create 2020/9/13 9:04 下午
 */

public class StateProxy{
    public static void main(String[] args) {
        WeddingCompany weddingCompany = new WeddingCompany(new You());
        weddingCompany.HappyMarry();
    }
}
interface Marry{
    void HappyMarry();
}
//真实角色:我要结婚
class You implements Marry{
    @Override
    public void HappyMarry() {
        System.out.println("xxx和xwy结婚了");
    }
}
//代理角色 帮助你结婚
class WeddingCompany implements Marry{
    private Marry target;

    public WeddingCompany(Marry target) {
        this.target = target;
    }

    @Override
    public void HappyMarry() {
        before();
        this.target.HappyMarry();
        after();
    }
    private  void  before(){
        System.out.println("结婚前布置现场");
    }
    private  void  after(){
        System.out.println("结婚之后收尾款");
    }
}

代理模式在多线程内使用:

package com.proxystate;

/**
 * @author levi
 * @create 2020/9/13 9:04 下午
 */

public class StateProxy{
    public static void main(String[] args) {
        You you = new You();
        new Thread( ()-> System.out.println("我爱你") ).start();
        new WeddingCompany(you).HappyMarry();
//        WeddingCompany weddingCompany = new WeddingCompany(you);
//        weddingCompany.HappyMarry();
    }
}
interface Marry{
    void HappyMarry();
}
//真实角色:我要结婚
class You implements Marry{
    @Override
    public void HappyMarry() {
        System.out.println("xxx和xwy结婚了");
    }
}
//代理角色 帮助你结婚
class WeddingCompany implements Marry{
    private Marry target;

    public WeddingCompany(Marry target) {
        this.target = target;
    }

    @Override
    public void HappyMarry() {
        before();
        this.target.HappyMarry();
        after();
    }
    private  void  before(){
        System.out.println("结婚前布置现场");
    }
    private  void  after(){
        System.out.println("结婚之后收尾款");
    }
}

9、Lambda

1、推导lamadba表达式

package com.lamdba;

/**
 * @author levi
 * @create 2020/9/13 9:39 下午
 */
public class TestLamdba {
//    3静态内部类
    static class Like2 implements ILike {
        @Override
        public void lamdba() {
            System.out.println("I love Lamdba2");
        }
    }

    public static void main(String[] args) {
        ILike like = new Like();
        like.lamdba();

        like = new Like2();
        like.lamdba();

//        4 局部内部类
        class Like3 implements ILike {
            @Override
            public void lamdba() {
                System.out.println("I love Lamdba3");
            }
        }
        like = new Like3();
        like.lamdba();

//        5匿名内部类,没有类的名字,必须借助接口或父类
        like = new ILike() {
            @Override
            public void lamdba() {
                System.out.println("I love Lamdba4");
            }
        };
        like.lamdba();
//        6 lamdba简化
        like = ()->{
            System.out.println("I love Lamdba5");
        };
        like.lamdba();
    }
}
//1定义一个接口
interface ILike{
    void lamdba();
}
//2定义一个实现类
class Like implements ILike{
    @Override
    public void lamdba() {
        System.out.println("I love Lamdba");
    }
}

2、带参简化

package com.lamdba;

/**
 * @author levi
 * @create 2020/9/13 9:49 下午
 */
public class TestLamdba2 {
    public static void main(String[] args) {

        ILove love = (int a) ->{
            System.out.println("i love you--->" + a);
        };
//        1干掉参数类型
        love = (a) ->{
            System.out.println("i love you--->" + a);
        };
//        2干掉括号
        love = a ->{
            System.out.println("i love you--->" + a);
        };
//        3干掉{}
        love = a -> System.out.println("i love you--->" + a);
        
        love.Love(2);
    }
}
interface ILove{
    void Love(int a);
}

总结:

  • 表达式只有一行代码的情况下,才能去掉{},不然必须用代码块包裹
  • 接口必须是函数式接口
  • 多个参数也可以去掉参数类型,要去掉都去掉,必须加括号

10、线程状态

1、线程停止

  • 不推荐使用stop,destory,一般使用标志位停止
  • 建议线程正常停止,利用次数,不建议死循环
package com.state;

/**
 * @author levi
 * @create 2020/9/13 10:01 下午
 */
public class TestStop implements Runnable {
    private boolean flag  = true;
    @Override
    public void run() {
        int i = 0;
        while (flag){
            System.out.println("run .......thread..--->"+i++);
        }
    }
//    转换标志位方法
    public void stop(){
        this.flag = false;
    }
    public static void main(String[] args) {
        TestStop testStop = new TestStop();
        new Thread(testStop).start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("main" + i);
            if(i == 900){
//                调用stop,线程停止
                testStop.stop();
                System.out.println("线程该停止了");
            }
        }
    }
}

2、线程休眠

Sleep

  • 每个对象都有一个锁,sleep不会释放锁
  • sleep存在异常抛出InterruptedException()
  • sleep时间达到后进入就绪状态
模拟网络延时
  • 放大问题发生性
  • 模拟倒计时
package com.state;

/**
 * @author levi
 * @create 2020/9/13 10:10 下午
 */
public class TestSleep2 {
    public static void main(String[] args) {
        try {
            tenDown();
        } 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;
            }
        }
    }
}

  • 打印当前系统时间
package com.state;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author levi
 * @create 2020/9/13 10:10 下午
 */
public class TestSleep2 {
    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;
            }
        }
    }
}

3、线程礼让

  • 让当前线程暂停,但是不阻塞
  • 运行转为就绪
  • cpu重新调度,礼让不一定成功,看cpu心情
package com.state;

/**
 * @author levi
 * @create 2020/9/13 10:20 下午
 */
public class TestYield{
    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()+"结束执行");
    }
}

4、线程强制执行

  • Join合并线程,待此线程执行完成后,再执行其他线程,其他线程堵塞
  • 可以想象成插队
package com.state;

/**
 * @author levi
 * @create 2020/9/13 10:25 下午
 */
public class TestJoin implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("vip来了"+i);
        }
    }
    public static void main(String[] args) throws InterruptedException {
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);
        thread.start();

//        主线程
        for (int i = 0; i < 500; i++) {
            if(i == 200){
                thread.join(); //插队
            }
            System.out.println("main" + i);
        }
    }
}

结果,主线程200前,并发执行,200之后,剩下的没执行的线程先执行完,主线程才继续

5、线程状态观测

package com.state;

/**
* @author levi
* @create 2020/9/14 9:44 上午
*/
//观察测试线程的状态
public class TestState {
   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("/../../../");
       });
//        观察状态
       Thread.State state = thread.getState();
       System.out.println(state); //new

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

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

6、线程优先级

package com.state;

/**
 * @author levi
 * @create 2020/9/14 9:56 上午
 */
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);
        Thread t5 = new Thread(myPriority);
        Thread t6 = new Thread(myPriority);

//        设置优先级,在启动
        t1.start();

        t2.setPriority(1);
        t2.start();

        t3.setPriority(2);
        t3.start();

        t4.setPriority(Thread.MAX_PRIORITY);
        t4.start();

        t5.setPriority(3);
        t5.start();

        t6.setPriority(4);
        t6.start();
    }

}
class MyPriority implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());
    }
}
  • 优先级设定在start之前
  • 优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用,都是看cpu的调度

7、守护线程

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

/**
 * @author levi
 * @create 2020/9/14 10:16 上午
 */
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 < 36500; i++) {
            System.out.println("你一生都开心的活着");
        }
        System.out.println("====good bye world=====");
    }
}

11、线程同步

1、三大不安全案例

买票问题:

  • 好几个人抢到同一张票,或者可能出现-1

银行取钱问题:

  • 出现负数
package com.syn;

/**
 * @author levi
 * @create 2020/9/14 10:53 上午
 */
public class UnSafeBank {
    public static void main(String[] args) {
        Account account = new Account(100,"结婚基金");
        Drawing xwy = new Drawing(account,50,"xwy");
        Drawing xxx = new Drawing(account,100,"xxx");
        xwy.start();
        xxx.start();
    }
}
//账户
class Account{
    int money; //余额
    String name; //卡名
    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}
//y银行
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() {
        //判断有没有钱
        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);
    }
}


不安全的集合

package com.syn;

import java.util.ArrayList;
import java.util.List;

/**
 * @author levi
 * @create 2020/9/14 11:10 上午
 */
//线程不安全的集合
public class UnSafeList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()-> {
                list.add(Thread.currentThread().getName());
            }).start();
        }
//        try {
//            Thread.sleep(3000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        System.out.println(list.size());
    }

}

2、同步方法和同步块

synchronized关键字

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

  • 缺陷:若将一个大的方法申明为synchronized将会影响效率

解决三个不安全案例

  • 银行取钱
package com.syn;

/**
 * @author levi
 * @create 2020/9/14 10:53 上午
 */
public class UnSafeBank {
    public static void main(String[] args) {
        Account account = new Account(1000,"结婚基金");
        Drawing xwy = new Drawing(account,50,"xwy");
        Drawing xxx = new Drawing(account,100,"xxx");
        xwy.start();
        xxx.start();
    }
}
//账户
class Account{
    int money; //余额
    String name; //卡名
    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}
//y银行
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);
        }

    }
}

  • 买票
package com.syn;

import sun.security.krb5.internal.Ticket;

/**
 * @author levi
 * @create 2020/9/14 10:43 上午
 */
//不安全的买票
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 ticketNums = 10;
    private boolean flag = true;
    @Override
    public void run() {
//        买票
        while (flag){
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    private synchronized void buy() throws InterruptedException {
//        判断是否有票
        if(ticketNums <=0){
            flag = false;
            return;
        }
//        模拟面试
        Thread.sleep(100);
//        买票
        System.out.println(Thread.currentThread().getName() + "拿到了" + ticketNums--);
    }
}
  • list
package com.syn;

import java.util.ArrayList;
import java.util.List;

/**
 * @author levi
 * @create 2020/9/14 11:10 上午
 */
//线程不安全的集合
public class UnSafeList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()-> {
                synchronized (list){
                    list.add(Thread.currentThread().getName());
                }
            }).start();
        }
//        try {
//            Thread.sleep(3000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        System.out.println(list.size());
    }

}

3、JUC

package com.syn;

import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @author levi
 * @create 2020/9/16 2:55 下午
 */
//测试juc安全类型的集合
public class TestJUC {
    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

12、死锁

  • 某个同步块同时拥有“两个对象以上的锁”时就会出现死锁的问题
package com.lock;

/**
 * @author levi
 * @create 2020/9/16 3:01 下午
 */
//多个线程互相抱着对方需要的资源,形成僵持
public class DeadLock {
    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 Lipstick lipstick =new Lipstick();
    static Mirror mirror = new Mirror();
    int choice;  //选择
    String girlName;  //使用化妆品的人
    MakeUp(int choice,String girlName){
        this.choice = choice;
        this.girlName = girlName;
    }
    @Override
    public void run() {
        //化妆
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    //化妆,互相持有对方的锁
    private void makeup() throws InterruptedException {
        if(choice == 0){
            synchronized (lipstick){
                System.out.println(this.girlName+ "获得口红的锁");
                Thread.sleep(1000);
                synchronized (mirror){ //一秒钟后想获得镜子
                    System.out.println(this.girlName + "获得镜子的锁");
                }
            }
        }else{
            synchronized (mirror){
                System.out.println(this.girlName + "获得镜子的锁");
                Thread.sleep(2000);
                synchronized (lipstick){ //一秒钟后想获得口红
                    System.out.println(this.girlName + "获得口红的锁");
                }
            }
        }
    }
}

13、Lock

package com.lock;

import java.util.concurrent.locks.ReentrantLock;

/**
 * @author levi
 * @create 2020/9/16 3:21 下午
 */

public class TestLock {
    public static void main(String[] args) {
        TestLock2 testLock2 = new TestLock2();
        new Thread(testLock2).start();
        new Thread(testLock2).start();
        new Thread(testLock2).start();
    }

}
class TestLock2 implements  Runnable{
    int ticketNums = 10;
//    定义lock锁
    private final ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true){
            try {
                lock.lock();//加锁
                if(ticketNums>0){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(ticketNums--);
                }else {
                    break;
                }
            }finally {
                lock.unlock(); //解锁
            }

        }
    }
}

synchronized和lock的对比:

  • lock是显式锁(手动开启关闭),synchronized是隐式锁,出了作用域自动释放
  • lock只有代码块锁,synchronized有代码块锁和方法锁
  • 使用lock锁,jvm花费较少时间来调度线程,性能更好,具备更好的扩展性
  • 优先使用顺序:lock - 同步代码块 - 同步方法

14、线程协作

1、生产者消费者

管程法

package com.gaoji;

/**
 * @author levi
 * @create 2020/9/16 4:51 下午
 */
//测试生产者消费者,利用缓冲区解决:管程法
public class TestPc {
    public static void main(String[] args) {
        SynContainer container = new SynContainer();
        new Productor(container).start();
        new Consumer(container).start();
    }
}
//生产者
class Productor extends  Thread{
    SynContainer container;
    public Productor(SynContainer container) {
        this.container = container;
    }
    //生产

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            container.push(new Chicken(i));
            System.out.println("生产了" + i + "只鸡");
        }
    }
}
//消费者
class Consumer extends Thread{
    SynContainer  container;
    public Consumer(SynContainer container) {
        this.container = container;
    }
    //消费

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("消费了第" + container.pop().id + "只鸡");
        }
    }
}
//产品
class Chicken{
    int id; //编号
    public Chicken(int id) {
        this.id = id;
    }
}
//缓冲区
class SynContainer{
    //容器计数器
    int count = 0;
    //需要一个容器大小
    Chicken[] chickens = new  Chicken[10];
    //生产者放入产品
    public synchronized void push(Chicken chicken){
        //如果容器满了,就需要等待消费者消费
        if(count==chickens.length){
            //t通知消费者消费,生产等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果没有满,我们就需要丢入产品
        chickens[count] = chicken;
        count++;
        //通知消费者可以消费了
        this.notifyAll();
    }
    //消费者消费产品
    public synchronized Chicken pop(){
        if(count==0){
            //等待生产者生产,消费者等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果可以消费
        count--;
        Chicken chicken = chickens[count];
        //通知生产者生产
        this.notifyAll();
        return chicken;
    }
}

信号灯法

package com.gaoji;

/**
 * @author levi
 * @create 2020/9/16 5:16 下午
 */
//标志位
public class TestPC2 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Player(tv).start();
        new Watcher(tv).start();
    }
}
//生产者
class Player extends Thread{
    TV tv;
    public Player(TV tv){
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if(i%2==0){
                this.tv.play("快乐大本营");
            }else{
                this.tv.play("广告中");
            }
        }
    }
}

//消费者
class Watcher extends Thread{
    TV tv;
    public Watcher(TV tv){
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            tv.watch();
        }
    }
}

//产品
class TV{
    String voice; //节目
    //演员表演观众等待 T
    //观众观看,演员等待 F
    boolean flag = true;
    //表演
    public synchronized void play(String voice){
        if(!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("演员表演了" + voice);
        //通知观众观看
        this.notifyAll();
        this.voice = voice;
        this.flag = !this.flag;
    }
    //观看
    public synchronized void watch(){
        if(flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("观看了" + voice);
        //通知演员表演
        this.notifyAll();
        this.flag = !this.flag;
    }
}

2、线程池

package com.gaoji;

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author levi
 * @create 2020/9/16 4:43 下午
 */
public class TestPool {
    public static void main(String[] args) {
        //1创建线程池
        ExecutorService service = Executors.newFixedThreadPool(10);//参数为i线程池大小
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        //2关闭连接
        service.shutdown();
    }

}
class MyThread implements Runnable{
    @Override
    public void run() {
            System.out.println(Thread.currentThread().getName());
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值