多线程笔记

多线程

多线程笔记_第1张图片
多线程笔记_第2张图片
多线程笔记_第3张图片

// 创建新线程的方法: 1、继承extends Thread 2、重写 run() 3、实例化后start()开启
public class TestThread extends Thread{
    // 重写 run()
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("我是新线程------"+i);
        }
    }

    // 主线程
    public static void main(String[] args) {
        // 创建线程对象
        TestThread testThread1 = new TestThread();
        testThread1.start();

        for (int i = 0; i < 1000; i++) {
            System.out.println("我是主线程------"+i);
        }
    }
}
/**
 * 主线程与新线程执行顺序由cpu去调度。
 */

/**
 * @Description 加入common.io-2.11.0.jar包,然后开启多线程下载网络图片
 */
public class TestThread1 extends Thread {
    private String url;
    private String name;

    public TestThread1(String url, String name) {
        this.url = url;
        this.name = name;
    }

    public void run() {
        webDownLoader Down = new webDownLoader();
        Down.downLoader(this.url, this.name);
        System.out.println("下载成功"+ this.name);
    }

    public static void main(String[] args) {
        TestThread1 t1 = new TestThread1("https://www.kuangstudy.com/assert/images/coursebg.jpg", "1.jpg");
        TestThread1 t2 = new TestThread1("https://www.kuangstudy.com/assert/images/xiaok.png", "2.jpg");
        TestThread1 t3 = new TestThread1("https://www.kuangstudy.com/assert/images/index_topleft_logo_black.png", "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");
        }
    }
}

多线程笔记_第4张图片
多线程笔记_第5张图片

我的理解就是,继承Thread类的化,每次开启新线程,则需要 new TestThread1() 创建一个新的对象,开启一个就多一个新对象。而通过 implements runnable接口开启新线程,只需要将一个对象放入 new Thread(实现了runnable的类的实例)就可以了,开启多个新线程,公用一个对象,自然是更好的啦!


初识并发问题: 多个线程同时操作一个资源,会出现资源紊乱,不安全!例子:比如多线程抢火车票,可能不同线程抢到同一张票,出现超卖的问题!


使用callable 接口实现与使用runnable接口实现多线程对比:
多线程笔记_第6张图片
多线程笔记_第7张图片
多线程笔记_第8张图片

实际上new Thread(实现了runnable的类的实例) 就是一种静态代理模式, Thread类和你创建的对象都实现了 Runnable接口, 并且 new Thread(xxx)帮助你去实现更多的事情!



多线程笔记_第9张图片

也就是说如果是函数式接口(只包含一个抽象方法),我们就可以使用lamda表达式,来简化书写

// 一步一步简化的过程分析, 从外部类->静态内部类->局部内部类->匿名内部类, 需要有一个接口或者父类->lambda
public class Test01 {

    //静态内部类
    static class Test02 implements Runnable{
        @Override
        public void run() {
            System.out.println("你好呀lambda!");
        }
    }


    public static void main(String[] args) {
        Test02 test02 = new Test02();
        new Thread(test02).start();

        //局部内部类
        class Test03 implements Runnable{
            @Override
            public void run() {
                System.out.println("你好呀lambda2!");
            }
        }

        Test03 test03 = new Test03();
        new Thread(test03).start();


        //匿名内部类, 需要有一个接口或者父类
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("你好呀lambda3!");
            }
        }).start();

        //lambda  jdk.8新增方式
        new Thread(()->{
            System.out.println("你好呀lambda4!");
        }).start();


    }
}

多线程笔记_第10张图片
多线程笔记_第11张图片
多线程笔记_第12张图片
多线程笔记_第13张图片

public class TestSleep2 {
    public static void main(String[] args) throws InterruptedException {

        TestSleep2 testSleep2 = new TestSleep2();

        //获取系统时间
        Date startTime = new Date(System.currentTimeMillis());

        while (true) {
            System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
            // 线程的休眠方法
            Thread.sleep(1000);
            startTime = new Date(System.currentTimeMillis());
        }

    }

    //模拟倒计时方法
    private void tenDown() throws InterruptedException {
        int num = 10;
        for (int i = 10; i > 0; i--) {
            // 线程的休眠方法
            Thread.sleep(1000);
            System.out.println("倒计时:"+i);
        }

    }
}

多线程笔记_第14张图片
多线程笔记_第15张图片

public class TestJoin implements Runnable{
    public static void main(String[] args) throws InterruptedException {
        TestJoin testJoin = new TestJoin();


        Thread thread = new Thread(testJoin);
        thread.start();

        for (int i = 0; i < 100; i++) {
            if (i==80){
                //强制执行,而且会阻塞主线程,等到thread线程跑完了,主线程才继续执行
                thread.join();
            }
            System.out.println("我是主线程:"+i);
        }

    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("我是要插队的线程:"+i);
        }
    }
}

多线程笔记_第16张图片

线程可以通过 thread.getState() 来获得当前线程的状态

状态分类:

多线程笔记_第17张图片

多线程笔记_第18张图片

//测试线程优先级
public class TestPriority {

    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());


        MyPriority myPriority = new MyPriority();
        Thread thread1 = new Thread(myPriority);
        Thread thread2 = new Thread(myPriority);
        Thread thread3 = new Thread(myPriority);
        Thread thread4 = new Thread(myPriority);
        Thread thread5 = new Thread(myPriority);


        thread1.setPriority(1);
        thread1.start();

        thread2.setPriority(4);
        thread2.start();

        thread3.setPriority(8);
        thread3.start();

        thread4.setPriority(9);
        thread4.start();

        thread5.setPriority(10);
        thread5.start();
    }
}


class MyPriority implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
    }
}

优先级只是给cpu一个参考,实际还是看它心情!

多线程笔记_第19张图片

//测试守护线程
//用户线程: 虚拟机要等待用户线程执行完毕
//守护线程: 虚拟机 不用等待 守护线程 执行完毕
public class TestDaemon {
    public static void main(String[] args) {
        God god = new God();
        Thread thread = new Thread(god);
        thread.setDaemon(true); //设置线程为守护线程,默认为false
        thread.start();

        //用户线程
        new Thread(()->{
            for (int i = 0; i < 30; i++) {
                System.out.println("你开心的在这个世界上活着"+i);
            }
            System.out.println("=======Goodbye , World!");
        }).start();
    }
}

//守护线程
class God implements Runnable{
    @Override
    public void run() {
        for (;true;){
            System.out.println("上帝保佑着你");
        }
    }
}

多线程笔记_第20张图片

三大不安全的例子

我认为不安全造成的原因都是因为,同一时间,多个线程操作同一对象,导致发生对象数据变化发生错误,不是正常的改变,因为同时操作的缘故。

就好比只有一张票,三个人同时抢,对于抢的一瞬间,三个人都能看到那张票,也因此都抢到了,而此时系统被抢一张就减去一,导致系统最后存放的数据是-2,也就是前面所说的导致对象数据变化发生错误

//线程同步问题一 :线程不安全,买票问题
public class UnsafeBuyTicket implements Runnable {
    //票数
    private int ticketNums = 10;
    //标志位
    private boolean flag = true;

    @Override
    public void run() {
        //买票
        while (flag) {
            buyTicket();
        }
    }

    public void buyTicket() {
        if (ticketNums <= 0) {
            flag = false;
            return;
        }

        //模拟网络延时
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName() + "-->拿到了第" + ticketNums-- + "张票");
    }

    public static void main(String[] args) {
        UnsafeBuyTicket station = new UnsafeBuyTicket();

        new Thread(station,"苦逼的我").start();
        new Thread(station,"牛逼的你们").start();
        new Thread(station,"可恶的黄牛党").start();
    }

}

//银行(存钱:存了多少,取钱:去了多少) , 两个人 , 账户
//并发问题,线程不安全

public class UnsafeBank {
    public static void main(String[] args) {
        Account account = new Account(100,"招商卡");

        Bank you = new Bank("痛苦的你",account,50);
        Bank wife = new Bank("开心的媳妇",account,100);

        you.start();
        wife.start();
    }
}


//账户
class Account{
    int money;//余额
    String name; //卡名

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}


//银行
class Bank extends Thread{
    //存钱:存了多少,取钱:取了多少

    Account account;  //账户
    int drawingMoney; //取了多少钱
    int nowMoney; //手里有多少钱

    public Bank(String name,Account account,int drawingMoney){
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }

    @Override
    public void run() {

        //判断能否取钱
        if (account.money-drawingMoney<0){
            return;
        }

        //为了放大问题发生性,我们加个延时.
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //余额 = 余额 - 你去走的钱
        account.money = account.money - drawingMoney;
        //你的钱 = 你的钱 + 你取的钱
        nowMoney = drawingMoney + nowMoney;

        System.out.println(this.account.name+"账户余额:"+account.money);
        System.out.println(this.getName()+"手里的钱:"+nowMoney);


    }
}
package com.kuang.syn;

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

//线程不安全问题3,集合操作
//思考?怎么让这些问题变安全.
public class UnSafeList {
    public static void main(String[] args) throws InterruptedException {
        List<String> list = new ArrayList<String>();

        for (int i = 0; i < 20000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }

        for (int i = 5;i>0;i--){
            Thread.sleep(1000);
            System.out.println("倒计时"+i);
        }

        System.out.println(list.size());

    }
}

多线程笔记_第21张图片

多线程笔记_第22张图片

// 第一个案例只需要锁住买票的方法就可以了,实际上就是锁住了票这个共享资源
public synchronized void buyTicket() {
        if (ticketNums <= 0) {
            flag = false;
            return;
        }

        //模拟网络延时
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName() + "-->拿到了第" + ticketNums-- + "张票");
    }

第二个案例,给 public void run()给这个方法上锁是不行的,锁住的是银行这个对象,而实际上共享的资源(会被线程修改的资源)是放在账户这个对象当中。此时就需要用到同步块的概念!

多线程笔记_第23张图片

每个对象都有自己的一把索,利用 synchronized 修饰关键字,我们可以让当前方法或者块所在的线程获得这把锁,那么其它线程,想去操作个他们的这个方法或者块被禁止,只有等到这把锁被释放,才行!

public void run() {
        drwaing();
    }

    //synchronized本身锁的是this.就是这个对象本身
    public void drwaing(){

        //提高性能的代码
        if (account.money<=0){
            return;
        }

        //如何判断锁的对象
        // 谁需要实现增删改就去锁定他
        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 = drawingMoney + nowMoney;

            System.out.println(this.account.name+"账户余额:"+account.money);
            System.out.println(this.getName()+"手里的钱:"+nowMoney);
        }

    }

同步方法: 也就是加在方法上的 synchronized 是锁住当前方法所在的对象,也就是this

同步块: synchronized(account){...}是锁住传入的参数所对应的这个对象

我的理解就是: 锁的概念在于,同时的情况下,保证只有一个线程在操作需要修改共享资源!!!

package com.kuang.syn;

import java.util.concurrent.CopyOnWriteArrayList;

public class SafeJUCList {
    public static void main(String[] args) throws InterruptedException {
        //保证线程安全的list , ArrayList
        CopyOnWriteArrayList list = new CopyOnWriteArrayList();

        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                    list.add(Thread.currentThread().getName());
            }).start();
            
            // 这里就是属于用synchronized方法来锁住要修改的对象list,来保证安全
             new Thread(()->{
                 synchronized(list) {
                     list.add(Thread.currentThread().getName());
                 }                    
            }).start();
        }

        for (int i = 5;i>0;i--){
            Thread.sleep(1000);
            System.out.println("倒计时"+i);
        }

        System.out.println(list.size());
    }
}

多线程笔记_第24张图片

我的理解:多线程都想要对方拥有的资源(而这个资源又是独一份的,也就是用static定义的,只执行一次)的锁,形成僵持,也就是死锁! 放在代码中就是

// 也就是同步块想要拥有两个以上对象的锁
// 线程1
synchronized(a) {
    ....
    synchronized(b) {
		...
    }
}
// 线程2
synchronized(b) {
    ....
    synchronized(a) {
		...
    }
}
// 解决方案:将里面的抽离出来,那么就会a,b对象的锁都会用完就释放,也就不会形成死锁了
// 线程1
synchronized(a) {
    ....  
}
synchronized(b) {
		...
}
// 线程2
synchronized(b) {
    ....
}
synchronized(a) {
		...
}

例子:

package com.kuang.gaoji;

//死锁问题
//两个线程抱着自己的锁 , 然后想要对方的锁 .
// 于是产生一个问题 ---> 死锁
public class DeadLocked {

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

        new Thread(g1).start();
        new Thread(g2).start();

    }


}

//化妆
class Makeup implements Runnable{

    //选择
    int choice;
    //谁进来了
    String girlName;

    //两个对象,而且是独一份的资源, 因为static只执行一次
    static LipStick lipStick = new LipStick();
    static Mirror mirror = new Mirror();

    public Makeup(int choice,String girlName){
        this.choice = choice;
        this.girlName = girlName;
    }

    @Override
    public void run() {
        //化妆
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //化妆的方法
    public void makeup() throws InterruptedException {
        if (choice==0){ //先拿口红,再拿镜子
            synchronized (lipStick){
                System.out.println("拿到口红");
                Thread.sleep(1000);
                //等待拿镜子的人释放锁
                synchronized (mirror){
                    System.out.println("拿到镜子");
                }
            }

        }else { //先拿镜子 , 再拿口红
            synchronized (mirror){
                System.out.println("拿到镜子");
                Thread.sleep(2000);
                //等待拿口红的人释放锁
                synchronized (lipStick){
                    System.out.println("拿到口红");
                }
            }
        }

    }

}


//口红
class LipStick{

}

//镜子
class Mirror{

}

多线程笔记_第25张图片

多线程笔记_第26张图片

多线程笔记_第27张图片
多线程笔记_第28张图片

package com.kuang.gaoji;

import java.util.concurrent.locks.ReentrantLock;

public class TestLock {
    public static void main(String[] args) {
        HelloWorld helloWorld = new HelloWorld();

        new Thread(helloWorld).start();
        new Thread(helloWorld).start();

    }
}


class HelloWorld implements Runnable{

    int ticketNums = 100;
    //可重入锁
    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true){
            try {
                lock.lock(); //加锁
                //判断是否有票
                if (ticketNums>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(ticketNums--);
                }else {
                    break;
                }
            } finally {
                lock.unlock();//解锁
            }

        }

    }

}

多线程笔记_第29张图片

多线程笔记_第30张图片
多线程笔记_第31张图片

多线程笔记_第32张图片
多线程笔记_第33张图片

管程法

/*
    生产者只管生产
    消费者只管消费
    鸡: 实体类

    容器 :

    容器添加数据.
    要判断容器是否满 , 满了等待消费者消费
    没有满,通知生产者生产

    容器减少数据
    判断还有没有数据, 没有数据的话 . 等待生产者生产
    消费完毕 , 通知生产者生产
 */


import java.sql.SQLOutput;

//测试生产者和消费者问题
public class TestPC {
    public static void main(String[] args) {
        SynContainer synContainer = new SynContainer();

        new Productor(synContainer).start();
        new Consumer(synContainer).start();
    }
}


//生产者
class Productor extends Thread{
    //需要向容器中加入产品
    SynContainer container;
    public Productor(SynContainer container){
        this.container = container;
        }
        @Override
        public void run() {
            for (int i = 1; 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 = 1; i < 100; i++) {
            //消费者拿走产品
            Chicken chicken = container.pop();
            System.out.println("消费者消费了"+chicken.id+"鸡");
        }
    }
}


//缓冲区-->容器
class SynContainer{

    //容器
    Chicken[] chickens = new Chicken[10];



    //容器的计数器
    int num = 0;

    //生产者放入产品
    public synchronized void push(Chicken chicken) {

        //假如容易已经满了,就不用放,等待消费者消费
        if (num>=chickens.length){

            //等待消费者消费
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        //假如容器没有满 , 通知生产生成

        System.out.println("num,,,,,"+num);
        chickens[num] = chicken;
        System.out.println("数组有多少个元素"+num);
        num++;
        //通知消费者消费
        this.notifyAll();

    }

    //消费者拿走产品
    public synchronized Chicken pop(){
        //假如容器空的,等待
        if (num<=0){
            //等待生产
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        num--;
        Chicken chicken = chickens[num];
        //通知生产者生产
        this.notifyAll();
        return chicken;
    }


}

//产品->鸡
class Chicken {
    int id;

    public Chicken(int id) {
        this.id = id;
    }
}

信号灯法(也叫标志位法)

package com.kuang.gaoji;

//生产者消费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("节目:快乐大本营播放中");
                System.out.println();
            }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{
    //演员说话 , 观众等待
    //观众观看 , 演员等待
    boolean flag = true;

    //说话
    String voice;

    //表演
    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.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;
    }

}

this.wait() 会将当前线程丢入等待池中,等待被唤醒,并且立即释放对象的锁,这样别的线程可以占用该锁了

this.notifyAll() 会唤醒同一对象中所有调用 this.wait()这个方法被丢入等待池的线程

boolean flag = true;

//说话
String voice;

//表演
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.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;
}

}


this.wait()  会将==当前线程==丢入==等待池==中,等待被唤醒,并且立即==释放对象的锁==,这样别的线程可以占用该锁了

this.notifyAll() 会唤醒同一对象中所有调用 this.wait()这个方法被丢入等待池的线程

你可能感兴趣的:(java,java,多线程)