学习大数据,所必需的java基础(4)

等待线程与唤醒线程

概述

学过计算机组成原理的同学,应该对这个比较熟悉,为了大家更好的理解,我们使用生产线程,消费线程作为例子,来给大家介绍。
生产线程:
1.先判断flag的值,如果flag== true,证明有包子,生产线程wait
2.如果flag为false,证明没有包子,就要生产包子
3.修改flag状态为true,证明生产完了,有包子了
4.唤醒消费进程 notify
消费线程:
1.先判断flag的值,如果flag== false,证明没有包子,消费线程wait
2.如果flag为true,证明有包子,就要消费包子
3.修改flag状态为false,证明消费完了,没有包子了
4.唤醒生产进程 notify

等待唤醒状态代码实现

package baozipu;
public class BaoZiPu {
    //包子数量
    private int count;
    //是否有包子
    private boolean flag;

    public BaoZiPu() {
    }

    public BaoZiPu(int count, boolean flag) {
        this.count = count;
        this.flag = flag;
    }

    public void getCount() {
        System.out.println("消费了第"+count+"个包子");
    }

    public void setCount() {
        count++;
        System.out.println("生产了第"+count+"个包子");
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

生产者线程

package baozipu;

public class Produce implements Runnable {
    private BaoZiPu baoZiPu;

    public Produce(BaoZiPu baoZiPu) {
        this.baoZiPu = baoZiPu;
    }
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(100l);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (baoZiPu) {
                //1.判断flag的值,如果为true证明有包子,生产线程wait
                if (baoZiPu.isFlag()){
                    baoZiPu.setCount();
                    try {
                        baoZiPu.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //如果flag为flase,证明没有包子,就要生产包子
                if (!baoZiPu.isFlag()){
                    baoZiPu.setCount();
                    //修改flag状态,证明生产完了
                    baoZiPu.setFlag(true);
                    //唤醒消费进程
                    baoZiPu.notify();
                }
            }
        }
    }
}

消费者线程

package baozipu;

public class Consumer implements Runnable{
    private BaoZiPu baoZiPu;

    public Consumer(BaoZiPu baoZiPu) {
        this.baoZiPu = baoZiPu;
    }

    @Override
    public void run() {
        while (true){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //flag为false证明没有包子,消费线程wait
            synchronized (baoZiPu){
                if (baoZiPu.isFlag() == false) {
                    try {
                        baoZiPu.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                if (baoZiPu.isFlag()){
                //如果flag为true就要消费包子
                    baoZiPu.getCount();
                //修改flag状态为false
                    baoZiPu.setFlag(false);
                //唤醒生产进程
                    baoZiPu.notify();
            }}
        }}
    }

测试类

package baozipu;

public class test01 {
    public static void main(String[] args) {
        BaoZiPu baoZiPu = new BaoZiPu();
        Produce produce = new Produce(baoZiPu);
        final Consumer consumer = new Consumer(baoZiPu);

        new Thread(produce).start();
        new Thread(consumer).start();
    }
}

使用同步方法改造等待唤醒案例

我们将syn代码从消费者生产者线程中拿出来,整体加入到包子铺类中

public class BaoZiPu {
    //包子数量
    private int count;
    //是否有包子
    private boolean flag;

    public BaoZiPu() {
    }

    public BaoZiPu(int count, boolean flag) {
        this.count = count;
        this.flag = flag;
    }

    public synchronized void getCount() {
        //flag为false证明没有包子,消费线程wait
        if (flag == false) {

            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
            if (flag){
                //如果flag为true就要消费包子
                System.out.println("消费了第"+count+"个包子");
                //修改flag状态为false
                this.setFlag(false);
                //唤醒生产进程
                this.notify();
            }
    }

    public synchronized void setCount() {
        //1.判断flag的值,如果为true证明有包子,生产线程wait
        if (flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        if (!flag) {
            //如果flag为flase,证明没有包子,就要生产包子
            count++;
            System.out.println("生产了第" + count + "个包子");
            //修改flag状态,证明生产完了
            this.setFlag(true);
            //唤醒消费进程
            this.notify();
        }
    }


    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

在生产者消费者进程中,只需要调用getcount,setcount方法即可

Lock锁

1.概述:是一个接口,充当锁对象
2.创建: ReentrantLock
3.方法:
void Lock() : 获取锁
void unlock(): 释放锁

public class ticket implements Runnable{
    static int ticket = 100;
    Object obj = new Object();
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //获取锁
            lock.lock();
            if (ticket>0){
                System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"张票");
                ticket--;
            }
            //释放锁
            lock.unlock();
        }
    }

Lock和synchronized区别

lock,属于轻量级锁,乐观锁
底层原理:
CAS机制
Compare And Swap ------- 比较并交换
CAS机制
先从内存获取改变值
先和内存中的值比较,如果已经被改变,重新获取,重新计算
如果没有被改变,直接将改变后的值存到内存中
获取 改变 比较 获取 改变 比较 存

注意
乐观锁的操作,不是操作代码块的,而是操作一个变量
原子类:在java.util.concurrent.atomic包下面有很多原子类:
构造
AtomicInteger() 创建具有初始值0的新AtomicInteger ----- 相当于 int i = 0
AtomicIntger(int initivalValue) 创建具有给定初始值的新 AtomicInteger ---- 相当于int i = 值
方法
int addAndGet(int delta) 以原子方式将给定值与当前值相加
int getAndIncrement() ----- +1
int getAndDecrment() -------- -1
int addAndGet(int delta) ------- 加指定的值

import java.util.concurrent.atomic.AtomicInteger;

public class test02 {
    public static void main(String[] args) {
        //赋初值i=10
        final AtomicInteger i = new AtomicInteger(10);
        //以原子方式,将当前值与给定值相加
        int sum = i.addAndGet(5);
        System.out.println("sum + ="+sum);
    }
}

乐观锁和悲观锁的区别

lock属于乐观锁,同时使用多个线程操作的是同一个变量
synchronized属于悲观锁,使用多个线程操作一段代码
乐观锁:线程A在操作变量是,允许线程B操作,只是会先判断,如果有问题,就放弃本次操作,判断没有问题就正常执行
悲观锁:当线程A正在操作的时候,才允许线程B执行,要等到A出来之后B才有可能进入执行
相对来说,悲观锁效率比较低,乐观锁效率比较高
当多个线程操作同一个数据时,会出现以下问题:
1.可见性
i=9 变量i的初始值为0,每个线程的操作都是减一,两个线程A和B同时访问变量,B先执行i-1,再将结果i=8同步到内存中,A线程
也执行i-1,这样i=9的状态就执行了两次,出现线程安全问题

线程安全问题产生的原因:一个线程对共享数据的修改不能立即为其他线程所见
解决 加上关键字:volatile
2.有序性
多行代码的编写顺序和编译顺序
有些时候,编译器在编译代码时,为了提高效率,会对代码进行重排
在多线程环境下,这种重排可能不是我们希望发生的,因为:重排,可能会影响另一个线程的结果,所以我们不需要代码进行重排
解决:给共享的变量加上关键字:volatile
3.原子性
指的是一个操作不可中断,即在多线程开发的环境下,一个操作一旦开始,就会在同一个CPU时间片内执行完毕
volatie解决不了原子性问题,所以为了多线程操作同一个数据出现的原子性问题,我们可以使用原子类
Atomicxxx类 ----- 代表具体数据类型 ---- 原子类的实现原理就是乐观锁

阻塞队列

概述 阻塞队列,是一个接口
获取:lock接口中的方法
Condition newCondition()
方法
void await() 等待
void signal() 唤醒
为啥要用condition这个接口
之前使用的wait和notify都是本地方法,频繁使用,会比较消耗系统资源
注意
Condition必须和Lock锁结合使用
对上述的同步代码块进行修改
示例如下

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class BaoZiPu {
    //包子数量
    private int count;
    //是否有包子
    private boolean flag;
    Lock lock = new ReentrantLock();
    //为生产者创建Condition
    Condition produceConditon = lock.newCondition();
    Condition consumerConditon = lock.newCondition();

    public BaoZiPu() {
    }

    public BaoZiPu(int count, boolean flag) {
        this.count = count;
        this.flag = flag;
    }

    public  void getCount() {
        //flag为false证明没有包子,消费线程wait
        lock.lock();
        if (flag == false) {
            try {
                //this.wait();
                consumerConditon.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        lock.lock();
        if (flag) {
            //如果flag为true就要消费包子
            System.out.println("消费了第" + count + "个包子");
            //修改flag状态为false
            flag = false;
            //唤醒生产进程
            //this.notify();
            consumerConditon.signal();
        }
        lock.unlock();
    }

    public void setCount() {
        //1.判断flag的值,如果为true证明有包子,生产线程wait
        lock.lock();
        if (flag) {
            try {
                //this.wait();
                produceConditon.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        if (!flag) {
            //如果flag为flase,证明没有包子,就要生产包子
            count++;
            System.out.println("生产了第......." + count + "个包子");
            //修改flag状态,证明生产完了
            flag =true;
            //唤醒消费进程
            //this.notify();
            consumerConditon.signal();
        }
        lock.unlock();
    }


    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

线程池

1.创建一个线程池对象,指定最多能创建多少条线程
2.来了第一个线程任务,判断池子中有没有线程,如果有,直接用,用完还回去
如果没有,创建线程对象,给第一个线程任务使用
3.来了第二个线程任务,判断池子中有没有线程,如果有,直接调用,用完还回去
如果没有,创建一个线程对象,给第二个线程任务使用
4.来了第三个线程任务,判断池子中有没有线程,如果有,直接调用,用完还回去,如果没有,等待,等着有线程任务执行完毕,归还了线程在使用

1.为啥要学线程池
因为我们会频繁的创建线程,去执行线程任务,执行完毕之后,要销毁线程,比较耗费内存资源
所以线程池中的线程可以循环利用

2.线程池对象:
Executors

3.获取:Executors中的静态方法
static ExecutorService newFixedThreadPool(interesting nThreads)
参数最多能创建多少条线程
返回值:ExecutorsService ---- 用来管理线程对象

4.执行线程任务:ExecutorsService中的方法
Future<?> submit(Runnable task)提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future
返回值:Future接口
用来接收run方法的返回值的,但是由于run方法没有返回值,所以不用Future接收

5.关闭线程池:用到的是ExecutorsSevice中的方法
void shutdown() 启动一次顺序关闭,执行以前提交的任务,但不接收新任务

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

public class test01 {
    public static void main(String[] args) {
        //创建线程池对象
        ExecutorService es = Executors.newFixedThreadPool(3);
        //提交线程任务
        es.submit(new MyRunnable());
        es.submit(new MyRunnable());
        es.submit(new MyRunnable());
        //关闭线程,不接受新任务
        es.shutdown();
    }
}

Callable接口

1.ExecutorService中的方法:
Future submit(Callable task)

2.Callable介绍:接口,类似于Runnable

3.Callable中的方法:
V call() — 用于设置线程任务,类似于run方法

4.call和run方法的区别:
a.相同点:
call方法和run方法都是设置线程任务的
b.不同点
run方法,没有返回值,且不能throws
call方法,没有返回值,且能throws

5.call方法的返回值是啥类型
a.Callable后面的泛型写啥类型,call方法的返回值类型就是啥类型
b.<>叫做泛型,规定的是操作的是什么类型的数据,只能传递引用类型
不对

6.Future接口:接收run方法或者call方法返回值的
Future接口中有一个方法: V get() 获取call方法的返回值

import java.util.concurrent.Callable;

public class Callable_1 implements Callable<String>{
    @Override
    public String call() throws Exception {
        return "call me";
    }
}

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class test01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        final ExecutorService es = Executors.newFixedThreadPool(3);
        Future<String> future = es.submit(new Callable_1());
        System.out.println(future.get());
    }
}

定时器_Timer

1.Timer概述:定时器
2.构造:
Timer()
3.方法
void schedule(TimeTask task, Date firstTime, long period)
task:抽象类,可以设置线程任务
firstTime:从哪个时间开始,长记性线程任务
period:时间间隔

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class test03 {
    public static void main(String[] args) {
        final Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("定时器执行!!");
            }
        },new Date(),2000L);
    }
}

单例模式

单例模式:
单:单个,一个
例:实例,对象
目的:让一个类只产生一个对象供外界使用

饿汉式

1.什么是饿汉式
你饿了,很饥饿,想要迫不及待的干饭
单例模式是想要这个对象,所以饿汉式的单例模式是迫不及待的想要这个对象,要求这个对象赶紧产生
相关代码

public class Singleton {
    /*
    * 由于单例模式目的 是让一个类只产生一个对象
    * 所以我们就不能让外界随便使用这个类的构造方法
    * 所以将构造私有化
    * */
    private Singleton(){

    }
    /*
    * 外界new不了,但是对象得产生,所以在本类中new
    * 饿汉式:想着让这个对象赶紧创建出来,所以将对象变成static的
    * */
    private static Singleton singleton = new Singleton();
    //对外提供公共接口,将自己本类中的对象给外界使用
    public static Singleton getSingleton(){
        return singleton;
    }
}
public class test05 {
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            System.out.println(Singleton.getSingleton());
        }
    }
}

懒汉式

简单理解:不着急new对象

public class Singleton_lan {
    //简单理解,不着急new对象
    private Singleton_lan(){}
    /*
    *由于是懒汉式,所以我们就不着急new对象了
    * */
    private static Singleton_lan singleton_lan = null;
    /*
    * 定义一个方法,对外返回一个都对象
    * 什么时候调用,就什么时候new
    * */
    public static Singleton_lan getSingleton_lan(){
        //如果Singleton不是null,就不要抢锁了,直接返回对象即可
        if(singleton_lan == null){
            synchronized (Singleton_lan.class){
                if (singleton_lan==null){
                    singleton_lan = new Singleton_lan();
                }
            }
        }
        return singleton_lan;
    }
}

public class test05 {
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            System.out.println(Singleton_lan.getSingleton_lan());
        }
    }
}

小结
1.构造私有
2.在本类中new对象
3.定义方法,对外返回内部对象

正则表达式

正则表达式的概念及演示

1.概述:是字符串表示的一个规则
2.作用:用于校验
3.需求:要求输入一行数字
a.开头不能是0
b.必须是数字
c,数字必须是5-15位
4.String中有一个方法:
boolean matches(String regex) ------- 判断某个字符串是否符合regex正则表达式规则
常规方法代码

    private static boolean method01(String number){
        if (number.startsWith("0")){
            return false;
        }
        char[] chars = number.toCharArray();
        for (int i = 0; i < number.length(); i++) {
            if (chars[i]<'0'||chars[i]>'9'){
                return false;
            }
        }
        if (number.length()<5 || number.length()> 15){
            return false;
        }
        return true;
        }

正则表达式代码

private static boolean method02(String number){
        boolean result = number.matches("[1-9][0-9]{4,14}");
        return result;
    }

正则表达式-字符类

java.util.regex.Pattern:正则表达式的编译表达形式
正则表达式-----字符类:[]表示一个区间,范围可以自己定义
语法实例:
1. [abc]:代表a或者b,或者c字符串中的一个
2. [6abc]: 代表除a,b,c以外的任何字符
3. [a-z]: 代表a-z的所有小写字符中的一个
4. [A-Z]: 代表A-Z的所有大写字符的一个
5. [0-9]: 代表0-9之间的某一个数字字符
6. [a-zA-Z0-9]: 代表a-z,A-Z,0-9之间的任意一个字符
7. [a-dm-p]: a到d或m到p之间的任意一个字符

 		String name = "wang";
        boolean result = name.matches("[w][g]");
        System.out.println(result);
        String name_2 = "李云龙";
        boolean result_1 = name_2.matches("[李][云丁][龙]");
        System.out.println(result_1);
        System.out.println(name.matches("[a-z][a][^wedfsafd][g]"));

正则表达式-逻辑运算符

正则表达式-逻辑运算符
语法示例
1. &&:并且
2. |: 或者

System.out.println(name.matches("[[a-z]&&[^sdfvs]][a][n|e|w][g]"));

正则表达式–预定义字符

语法示例:
1.“.” 匹配任何字符。
2. “\d”:任何数字[0-9]的简写
3. “\D”: 任何非数字0-9的简写
4. “\s”: 空白字符
5. “\S”: 非空白字符
6. “\w”:单词字符,[a-zA-Z0-9]的简写
7. “\W”非单词数字

System.out.println("999".matches("\\d\\d\\d"));

正则表达式-数量词

语法示例:x代表着字符
1.x?:x出现的数量 零次或者一次
2.x*:x出现的数量 零次或者多次 任意次
3.x+:x出现的数量 多次或者一次 x>=1
4.x{n}:x出现的数量 恰好n次
5.x{n,}:x出现的数量 至少n次,x>=n次
6.x{n,m}: x出现的数量 n次到m次

		System.out.println("1111".matches("\\d{3}"));
        System.out.println("18766985325".matches("[1]\\d{1,11}"));

正则表达式-分组括号()

示例

System.out.println("123123123123".matches("(123)*"));

String类中和正则表达式相关的方法

String类中和正则表达式相关的方法有:
boolean matches(String regex)判断字符串是否匹配给定的正则表达式
String[] split(String regex) 根据给定正则表达式的匹配拆分此字符串
String replaceAll(String regex, String replacement)把满足正则表达式的字符串,替换为新的字符

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值