多线程学习笔记

多线程学习笔记

一、多线程的实现方法

1.继承Thread

public class MyThread extends Thread{
    @Override
    public void run() {
       
    }
     public static void main(String[] args) {
        new MyThread().start();
    }
}

2.实现Runnable接口

public class MyThread implements Runnable{
	@Override
    public void run() {
       
    }
     public static void main(String[] args) {
        new Thread(new MyThread()).start();
    }
}

二、多线程的底层原理:静态代理

静态代理的条件:

1.代理类和真实类都实现同一个接口

2.代理对象要代理真实对象

好比结婚这件事,你可以选择自己办婚礼,也可以请婚庆公司办婚礼,婚庆公司就是代理对象,而你就是真实对象。婚庆公司办婚礼,但结婚的人是你

interface MarryHappy{
    void MarryHappy();
}

public class You  implements MarryHappy{
    @Override
    public void MarryHappy() {
        System.out.println("人间四大喜事:洞房花烛夜!秦老师新婚快乐!");
    }
}
class WeddingCompany implements MarryHappy{
    private MarryHappy target;
    public WeddingCompany(MarryHappy target){
        this.target=target;
    }

    @Override
    public void MarryHappy() {
        before();
        target.MarryHappy();
        after();
    }
    public void before(){
        System.out.println("结婚前准备婚礼现场");

    }
    public void after(){
        System.out.println("结婚后收礼金");

    }

    public static void main(String[] args) {
        //这就是静态代理,Thread类的底层原理
        new Thread(()-> System.out.println("结婚快乐")).start();//lambda表达式
        new WeddingCompany(new You()).MarryHappy();
    }
} 

三、线程状态

1、五个状态

线程这部分内容和操作系统的联系上了,与进程的5大状态模型很像

分别是:新建态,就绪态,运行态,阻塞态,以及终止态

2、观察线程状态

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("观察线程状态=========");

        });
        System.out.println(thread.getState());//new

        thread.start();
        System.out.println(thread.getState());//Run

        while (thread.getState()!= Thread.State.TERMINATED){
            Thread.sleep(500);
            System.out.println(thread.getState());
        }
    }
}

四、线程的一些方法总结

//1、查看当前线程的名字
Thread.currentThread().getName();
//2、查看线程状态
new Thread(new RunnaleImpl()).getState();
//3、休眠sleep
Thread.sleep(1000)//参数是毫秒值
//4、线程礼让
new Thread(new RunnaleImpl()).yield();
    //该方法会使得当前线程重新回到就绪队列,但不一定礼让成功,全靠cpu的调度
//5、线程强制执行join,相当于插队,强制该线程执行,其他线程阻塞,包括main线程
new Thread(new RunnaleImpl()).join();
//6、设置,获取线程的优先级priority
new Thread(new RunnaleImpl()).setPriority();
new Thread(new RunnaleImpl()).getPriority();
//优先级在1~10之间,Thread.MIN_PRIORITY=1;Thread.NROM_PRIORITY=5;Thread.MAX_PRIORITY=10;
//优先级低(调度的概率低)也有可能被调度

//7、设置守护线程Daemon
new Thread(new RunnaleImpl()).setDaemon(true);
守护线程 守护线程
//God,和You都是Thread的子类,其中God的Run方法是死循环,You的Run不是

五、线程同步

注:这里简单的说说多个线程访问共享资源的问题

1、多线程不安全的原因

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

这个说实话我也不大懂。拿买票来说,就是假设三个线程抢着买票。不排队,只剩最后一张的时候,三个线程都看到票了,于是都去购票,三个线程都以为自己买到了。造成了数据不一致

2、三个线程不安全案例

1)车站买票

public class UnsafeBuyTicket {
    public static void main(String[] args) {
        Station station=new Station();
        new Thread(station,"苦逼的狂神").start();
        new Thread(station,"牛皮的我").start();
        new Thread(station,"不要脸的黄牛党").start();
    }
}
class Station implements Runnable{
    private int ticketNum=10;
    private boolean flag=true;
    private synchronized void buy() throws InterruptedException {
        if(ticketNum<=0){
            flag=false;
            return;
        }
        Thread.sleep(100);

        System.out.println(Thread.currentThread().getName()+"抢到了第"+ticketNum--+"张票");
    }
    @Override
    public void run() {
        while (flag){
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
}

2)银行取钱

public class UnsafeBank {

    public static void main(String[] args) {
        Account account=new Account(100,"结婚基金");
        Thread you=new Drawing(account,50,"you");
        Thread girlFriend=new Drawing(account,100,"girlFriend");
        you.start();
        girlFriend.start();
    }
}

class Drawing extends Thread{
    private int drawingMoney;
    Account account;
    private int nowMoney=0;
    public Drawing(Account account,int drawingMoney,String name){
        this.account=account;
        this.drawingMoney=drawingMoney;
        this.setName(name);
    }

    @Override
    public void run() {
        synchronized (account){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //取钱
            if(this.account.getTotalMoney()<this.drawingmoney){ system.out.println(this.getname()+"银行存款不足");="" }else="" {="" this.account.settotalmoney(account.gettotalmoney()-this.drawingmoney);="" this.nowmoney+="this.drawingMoney;" system.out.println(this.getname()+"手头有"+this.nowmoney);="" system.out.println(this.account.getname()+"还剩"+this.account.gettotalmoney());="" }="" class="" account="" private="" int="" totalmoney;="" string="" name;="" 钱的用处="" public="" gettotalmoney()="" return="" void="" settotalmoney(int="" totalmoney)="" this.totalmoney="totalMoney;" getname()="" setname(string="" name)="" this.name="name;" account(int="" totalmoney,="" name){="" ```="" ####="" 3)arraylist集合="" ```java="" import="" java.util.arraylist;="" java.util.list;="" unsafelist="" static="" main(string[]="" args)="" list<string=""> list=new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            new Thread(()->  {
                synchronized (list){
                    list.add(Thread.currentThread().getName());
                }
            }).start();
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}
//提一嘴 CopyOnWriteArrayList是线程安全的

3、解决线程安全的方式

1)synchronized代码块,synchronized同步方法

//使用synchronized关键字就是想把需要共享访问的内容锁起来,让线程排队一个一个来。
//个人理解:共享资源就是一扇门,一扇门对应一把锁(门被锁),锁在CPU那,线程要访问门就要找CPU拿锁,谁被CPU分配锁才能访问这扇门,该线程访问完后CPU再进行锁的分配

//synchronized锁的是this对象
public synchronized void test(){
    /*同步方法的弊端:
    *代码有需要修改和只读的
    *使用synchronized关键字导致只读代码也需要锁才能访问
    */
}
//synchronized代码块,obj就是被锁的共享资源
synchronized(obj){
	
}

2)lock

lock的好处就是显示的加锁,放锁

六、线程通信

1、一些有关通信的方法

2、生产者和消费者问题

1)问题说明

用狂神的话说就是:

简单的说就是你去肯德基买炸鸡,没有了你通知工作人员制作,工作人员做鸡,做好了通知你来吃

2)管程法

public class TestPC {
    public static void main(String[] args) {
        SynContainer synContainer=new SynContainer();
        new Producer(synContainer).start();
        new Customer(synContainer).start();

    }
}
class Producer extends Thread{
    private SynContainer synContainer;
    public Producer(SynContainer synContainer){
        this.synContainer=synContainer;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                synContainer.push(new Chicken(i));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("生产者做了第"+i+"只鸡");
        }
    }
}
class Customer extends Thread{
    private SynContainer synContainer;
    public Customer(SynContainer synContainer){
        this.synContainer=synContainer;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                System.out.println("消费者吃了-->第"+synContainer.pop().id+"只鸡");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
class Chicken{
    int id;//产品编号
    public Chicken(int id) {
        this.id = id;
    }
}
class SynContainer{
    //用数组来装商品Chicken
    Chicken[] chickens=new Chicken[10];
    int count=0;

    //生产者做鸡
    public synchronized void push(Chicken chicken) throws InterruptedException {
        //判断当前chicken的数量满了没有,没有满添加,并通知消费者消费:有鸡
        if(chickens.length==count){
            this.wait();
        }
        chickens[count++]=chicken;
        this.notifyAll();

    }
    //消费者吃鸡
    public synchronized Chicken pop() throws InterruptedException {
        //消费者判断有没有鸡,没鸡了等待生产者通知,有鸡吃了通知生产者做鸡
        if(count==0){
            this.wait();
        }
        count--;
        Chicken chicken=chickens[count];
        this.notifyAll();
        return chicken;
    }
}

3)信号灯法

//一个演员类
//一个观众类
//一个TV类
public class TestPC2 {
    public static void main(String[] args) {
        TV tv=new TV();
        new Performer(tv).start();
        new Audience(tv).start();
    }
}
class Performer extends Thread{
    TV tv;
    public Performer(TV tv){
        this.tv=tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            if(i%2==0){
                tv.perform("我的音乐你听吗");
            }else {
                tv.perform("美味持久,久到离谱");
            }
        }
    }
}

class Audience extends Thread{
    TV tv;
    public Audience(TV tv){
        this.tv=tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            tv.view();
        }
    }
}
class TV{
    String programName;
    boolean flag=true;

    //表演
    public synchronized void perform(String programName) {
        if(this.flag!=true){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("演员表演了"+programName);

        this.programName=programName;

        this.flag=!this.flag;
        //通知观众观看
        this.notifyAll();
    }
    //观看
    public synchronized void view()  {
        if(this.flag==true){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("观众观看了"+this.programName);

        this.flag=!this.flag;
        //通知演员表演
        this.notifyAll();
    }
}

七、线程池

线程池相关类:

使用:

创建一个Runnable实现类MyThread

</this.drawingmoney){>

posted @ 2021-11-20 10:47  让时间变成力量  阅读(45)  评论(0)    收藏  举报