多线程基础

多线程

Thread

现在的Thread中的run方法,已经被实现了,所以已经不需要实现了

操作

  • 继承 extends Thread方法

  • 重写run方法

  • start()

案例

public class ThreadTest extends Thread{
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("多线程" + i);
        }

    }


    public static void main(String[] args) {
        ThreadTest threadTest = new ThreadTest();
        threadTest.start();
        for (int i = 0; i < 100; i++) {
            System.out.println("主线程" + i);
        }
    }
}
  • 存在两个线程,一个是主线程,一个是其他的线程
  • 当start时,并不是线程启动了,而是线程进入了就绪状态,当所有线程都进入了就绪状态,由CPU来分配资源
  • 这个输出是,上面线程与下面线程同时进行的

在这里插入图片描述

Runnable

其实Thread也是实现了Runnable这个接口,同时这个接口中只有一个方法,,就是run方法

实现了Runnable接口只是为了实现这个方法,并创建一个实例,而不是创建一个线程,还需要创建一个线程,多个线程可以同时调用这个实例

操作

  • 实现Runnable接口
  • 重写run方法
  • 创建Thread时将 实现Runnable接口类放进去
  • start

案例

public class RunableTest implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("多线程"+ i);
        }
    }
}



public class Main {
    public static void main(String[] args) {
        Runnable runnable = new RunableTest();
        //Thread thread = new Thread(runnable);
        //thread.start();

        //与上面代码等效
        new Thread(runnable).start();
        for (int i = 0; i < 100; i++) {
            System.out.println("主线程"+i);
        }
    }
}

  • 得到的结果跟上述的是一样的

总结

注意:

  • 线程start(),并不是启动了,而是将加入到就绪队列,是与主线程一起的呢,再有cpu分配资源
  • 实现多线程的方式存在两个 extends Thread类 ,实现Runnable接口 ,同时重写run方法
  • Thread类也是实现了 Runnable接口

Lambda表达式

概念

函数式接口:

一个接口,里面只有一个方法

案例

interface Study {
    public void studyChinese();
}
class Student implements Study{
    @Override
    public void studyChinese() {
        System.out.println("学习中文");
    }
}

public class Main {

    public static void main(String[] args) {
        Study study = new Student();
        study.studyChinese();
    }
}
  • 这里存在一个接口里面只有一个接口,里面只有 一个方法
  • 这是一个常规的实现

进阶------->静态内部类

public class Main {
    static class Student implements Study{
        @Override
        public void studyChinese() {
            System.out.println("学习中文");
        }
    }
    public static void main(String[] args) {
        Study study = new Student();
        study.studyChinese();
    }
}

进阶----->局部内部类

public class Main {
    public static void main(String[] args) {
        class Student implements Study{
            @Override
            public void studyChinese() {
                System.out.println("学习中文");
            }
        }         
        Study study = new Student();
        study.studyChinese();
    }
}

进阶---->匿名内部类

这个就是实例化接口

public class Main {
    public static void main(String[] args) {
        Study study = new Study(){
            @Override
            public void studyChinese() {
                System.out.println("学习中文");
            }
        };
        study.studyChinese();
    }
}

进阶----->lambda表达式

public class Main {

    public static void main(String[] args) {
        Study study = ()->{
                System.out.println("学习中文");
        };
        study.studyChinese();
    }
}
  • 删除了函数的实例化,里面的函数名字都删了 -> 增加了箭头

线程的操作

线程的停止【stop】

  • Thread原本使用的是Thread.stop(),但是却并不被采用了
  • 采用定义一个标志位来使得线程停止
public class ThreadStop implements Runnable{


    private boolean flag = true;
    @Override
    public void run() {
        int i = 0;
        while(flag){
            System.out.println("线程停止" + Thread.currentThread().getName() + i++);
        }
    }


    public void stop(){
        this.flag = false;
    }

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

        for (int i = 0; i < 1000; i++) {
            System.out.println(i);
            if(i == 900){
                threadStop.stop();
            }
        }

    }
}
  • 主线程到了900的时候,调用停止方法,将标志位flag修改位false,使得线程停止

线程睡觉【sleep】

  • 休眠一定的时间
public class ThreadSleep implements Runnable{


    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            if(Thread.currentThread().getName().equals("小明") && i == 8){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println(Thread.currentThread().getName() + "票数"+i);
        }
    }

    public static void main(String[] args) {
        ThreadSleep threadSleep = new ThreadSleep();
        Thread thread1 = new Thread(threadSleep,"小明");
        Thread thread2 = new Thread(threadSleep,"丽丽");
        Thread thread3 = new Thread(threadSleep,"图图");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

线程的礼让【yield】

  • 让线程重新处于就绪状态,大家齐头并进的那种

  • 但是礼让后,CPU去调度谁,还是得CPU说的算

  • 所以说礼让不一定成功

public class TreadYield implements Runnable{


    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() +"开始");
        if(Thread.currentThread().getName().equals("pp")){
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() +"结束");
    }

    public static void main(String[] args) {
        TreadYield treadYield = new TreadYield();
        new Thread(treadYield,"pp").start();
        new Thread(treadYield,"tutu").start();
        new Thread(treadYield,"qq").start();
        new Thread(treadYield,"yuyu").start();
    }
}

线程的强制执行【join】

  • 插队的方式
  • 就是当一个线程执行到一半,你可以插队,而且是等插队的人执行完了,后面的人才能执行
public class ThreadJoin implements Runnable{


    @Override
    public void run() {

        for (int i = 0; i < 100; i++) {
            System.out.println("插队" + i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadJoin threadJoin = new ThreadJoin();
        Thread thread = new Thread(threadJoin);
        thread.start();

        for (int i = 0; i < 100; i++) {
            System.out.println("我是爹" + i);
            if(i == 10){
                thread.join();
            }
        }
    }
}

线程的状态【status】

  • NEW
  • RUNNABLE
  • BOLCOKED
  • WAITING
  • TIMED_WAITING
  • TERMINATED
public class ThreadState implements Runnable{
    @Override
    public void run() {
            //try {
            //    Thread.sleep(1000);
            //} catch (InterruptedException e) {
            //    throw new RuntimeException(e);
            //}
            for (int i = 0; i < 100; i++) {
                System.out.println(i);
            }
            //Thread.yield();

    }

    public static void main(String[] args) throws InterruptedException {
        ThreadState threadState = new ThreadState();
        Thread thread = new Thread(threadState,"线程1");
        System.out.println(thread.getName() + ":" +thread.getState());
        thread.start();
        System.out.println(thread.getName() + ":" +thread.getState());




        Thread thread2 = new Thread(threadState,"线程2");
        //可以看出是共享一个线程的
        System.out.println(thread2.getName() + ":" +thread.getState());
        thread2.start();
        System.out.println(thread2.getName() + ":" +thread.getState());


        while(thread.getState() != Thread.State.TERMINATED){
            System.out.println(thread.getName()+thread.getState());
            //Thread.sleep(100);
        }
        thread2.join();

    }
}
  • 我们可以看出,当多个线程操作一个实例的时候,第一个线程会有新生状态,但是其他的线程就没有新生

线程的优先级【priority】

  • 可以设置线程优先级,谁先进行
  • 但是设置了不一定成功
public class ThreadPriority implements Runnable{

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

}
class Main{
    public static void main(String[] args) {
        ThreadPriority threadPriority = new ThreadPriority();
        Thread thread = new Thread(threadPriority,"first");
        Thread thread1 = new Thread(threadPriority,"second");
        Thread thread2 = new Thread(threadPriority,"third");
        Thread thread3 = new Thread(threadPriority,"forth");



        thread.setPriority(1);
        thread.start();


        thread1.setPriority(4);
        thread1.start();

        thread2.setPriority(9);
        thread2.start();


        thread3.setPriority(6);
        thread3.start();

    }
}

线程的守护【Deamon】

  • 守护进程会一直伴随着需要守护的进程,直到进程的死亡
public class ThreadDeamon {
    public static void main(String[] args) {
        YouYongci youYongci = new YouYongci();
        ZhouXiaoqi zhouXiaoqi = new ZhouXiaoqi();

        Thread threadYou = new Thread(youYongci);
        Thread threadZhou = new Thread(zhouXiaoqi);
        //设置为守护进程
        threadZhou.setDaemon(true);
        threadYou.start();
        threadZhou.start();
    }
}

/**
 * 尤永慈
 */
class YouYongci implements Runnable{

    @Override
    public void run() {
        int days = 365 * 3;
        for (int i = 0; i < days; i++) {
            System.out.println("今天是第"+ i +"天,是开心的一天");
        }
    }
}

/**
 * 周萧齐
 */
class ZhouXiaoqi implements Runnable{

    @Override
    public void run() {
        while (true) {
            System.out.println("尤永慈 开心我就开心");
        }
    }
}

线程同步

定义

线程同步:

1、即当有一个线程在对内存进行操作时,

2、其他线程都不可以对这个内存地址进行操作,

3、直到该线程完成操作 其他线程才能对该内存地址进行操作,

4、而其他线程又处于等待状态

  • java允许多个线程操作同一个对象【资源变量】
  • 当多个线程同时操作同一个数据时,就会造成数据的不一致性
    • 12306买票,只剩下一张票,但是却被很多人看到,如果线程不同步,那么很多人都会买到这张票,而这个数据将会变成负数

多线程操作一个对象,造成数据不一致的案例

案例一 【操作的是一个数据】

public class ThreadSync {

    public static void main(String[] args) {
        BuyTickets buyTickets = new BuyTickets();

        Thread thread1 = new Thread(buyTickets,"皮夹子来买票");
        Thread thread2 = new Thread(buyTickets,"黄牛买票");
        Thread thread3 = new Thread(buyTickets,"大家都来买票");


        thread1.start();
        thread2.start();
        thread3.start();
    }
}


class BuyTickets implements Runnable{

    int number = 10;

    boolean flag = true;
    @Override
    public void run() {
        while (flag) {
            System.out.println(Thread.currentThread().getName() + "买到了第" +number + "张票");
            number--;
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            stop();
        }
    }


    public void stop(){
        if(number <= 0){
            this.flag = false;
        }
    }
}
  • 创建了一个实现runnable接口的实例
  • 多个线程操作一个实例,一个实例中的同一个数据,会造成数据的不一致性

案例二【操作一个对象】

public class UnsafeBank {
    public static void main(String[] args) {

        GetMoney getMoney = new GetMoney();
        getMoney.setGetMoney(0);
        new Thread(getMoney,"皮夹子").start();
        new Thread(getMoney,"皮夹子女朋友").start();
    }

}


class GetMoney implements Runnable{

    private Account account = new Account(100);

    private int getMoney;

    public void setGetMoney(int getMoney) {
        this.getMoney = getMoney;
    }

    @Override
    public void run() {


        if(account.getAllMoney() < getMoney){
            System.out.println(Thread.currentThread().getName() + "取钱不够了");
            return;
        }

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        int endMoney = account.getAllMoney() - getMoney;
        account.setAllMoney(endMoney);
        account.setGetMoney(getMoney);
        account.setEndMoney(endMoney);

        System.out.println(Thread.currentThread().getName() + "取了:" + account.getGetMoney());
        System.out.println(Thread.currentThread().getName() + "剩下:" + account.getEndMoney());

    }
}
class Account{
    private int allMoney;

    private int getMoney;

    private int endMoney;

    public Account(int allMoney) {
        this.allMoney = allMoney;
    }

    public int getAllMoney() {
        return allMoney;
    }

    public void setAllMoney(int allMoney) {
        this.allMoney = allMoney;
    }

    public int getGetMoney() {
        return getMoney;
    }

    public void setGetMoney(int getMoney) {
        this.getMoney = getMoney;
    }

    public int getEndMoney() {
        return endMoney;
    }

    public void setEndMoney(int endMoney) {
        this.endMoney = endMoney;
    }
}
  • 这个是创建一个账户的对象
  • 皮夹子和皮夹子的女朋友同时对其中的钱进行修改
  • 造成了Account对象的不一致性

案例三【多线程下的ArrayList】

public class UnsafeList {

    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 10000 ;i++) {
            int finalI = i;
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    list.add(finalI);
                }
            };

            new Thread(runnable).start();
        }
        System.out.println(list.size());
    }
}
  • ArrayList是不安全,容易造成数据的不一致性,也就是使得多个数据被存放到同一个位置了

线程同步的方式

同步方法

 public synchronized void run(){
     
 }
  • 由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。

  • 在调用该方法前,需要获得内置锁,否则就处于阻塞状态

同步代码块

synchronized(object){
    
}
  • object是一个对象类型
  • 当需要对某个对象进行操作时,就可以使用先将对象锁住,等操作完成后,在开锁

使用特殊域变量(volatile)

  • volatile为关键字提供一个免锁机制,这样就可以直接访问
  • volatile会告诉虚拟机该数据可能会被其他线程更新
  • 使用后,每次线程操作该数据,都需要重新计算,而不是使用寄存器重点值
  • 不可以修饰常量(final类型)
private volatile int account = 100

重入锁(ReentrantLock)

ReentrantLock lock = new ReetrantLock();
try{
    lock.lock();
}catch {
    
}finally{
    lock.unlock();
}
  • 创建一个对象
  • 一般lock.lock()方法放在try里
  • lock.unlock()放在finally里

生产者消费者问题

  • 也就是解决通信问题,当需要当生产者线程生产了商品,如何让消费者知道这个问题,然后去消费他

管程法

  • 定义一个缓存区域
  • 当缓存区满了,告诉消费者消费
  • 当缓存区空了,告诉生产者生产
  • 问题来了???如何告知
    • 使用wati() 和 notify方法
    • 当缓存区满了,可以使用wati(),告诉生产者等待生产
    • 同时通知消费者,不要消费,notify()
public class ThreadContainer {

    public static void main(String[] args) {
        Container container = new Container();
        Producer producer = new Producer(container);
        Consumer consumer = new Consumer(container);
        new Thread(producer).start();
        new Thread(consumer).start();
    }
}

class Chicken {

}
class Producer implements Runnable{


    private Container container;

    public Producer(Container container) {
        this.container = container;
    }

    @Override
    public void run() {
        productChicken(container);
    }

    /**
     * 生产鸡
     * 将鸡放在缓冲区
     */
    int  num = 0;
    public void productChicken(Container container){
        while (num <= 100) {
            Chicken chicken = new Chicken();
            num++;
            System.out.println("生产了" +num +"鸡");
            container.push(chicken);
        }
    }
}

class Consumer implements Runnable{

    private Container container;

    public Consumer(Container container) {
        this.container = container;
    }

    @Override
    public void run() {
        consumeChicken(container);
    }

    /**
     * 消费鸡
     *
     */
    int num = 0;
    public synchronized void consumeChicken(Container container){
        while (num <= 100) {
            num ++;
            System.out.println("消费了" +num +"鸡");
            container.pop();
        }
    }
}

class Container{
    //定义chicken数组,作为缓冲区
    Chicken[] chickens = new Chicken[10];


    int count = 0;
    public synchronized void push(Chicken chicken){
        //判断是否满了
        if(count == chickens.length){
            try {
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        chickens[count++] = chicken;
        //通知消费者消费
        this.notifyAll();
    }


    public synchronized void pop(){
        //如果没有了,通知生产者生产
        if(count == 0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        count--;
        this.notifyAll();
    }
    
}
  • 生产者Producer
    • 生产鸡,并将鸡放到容器里Container.push
  • 消费者Consumer
    • 消费鸡,并将鸡从容器里移动出来Container.pop
  • 缓存区Container
    • 定义缓存区,缓存大小
    • 定义入缓存方法和出缓存方法

信号灯法

  • 就是在容器内定义个变量,告诉别人,下一步该干啥了
  • 15
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值