Java小记——多线程03

目录

线程间的等待唤醒机制

wait()和sleep()的区别

内存可见性问题 volatile

线程的状态

匿名内部类创建线程


线程间的等待唤醒机制

void wait ()  在其他线程调用此对象的 notify () 方法或 notifyAll () 方法前,导致当前线程等待

void notify () 唤醒在此对象监视器上等待的单个线程,随机唤醒。

//资源 包子
public class Buns {
    public String name;
    public int price;
    //定义一个标记
    public boolean flag = false; //false 表示没有资源,true表示有资源。
}
//生产者线程
public class SetThread extends Thread {
    private Buns buns;
    int i = 0;

    public SetThread(Buns buns) {
        this.buns = buns;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (buns) {
                //生产线程,生产出了资源,也就是有资源,就等着,不能干等着,得通知消费线程,消费资源
                if (buns.flag) {
                    //进来了,表示有资源
                    try {
                        buns.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //生产了资源
                if (i % 2 == 0) {
                    buns.name = "肉包子";
                    buns.price = 3;
                } else {
                    buns.name = "素包子";
                    buns.price = 2;
                }
                //通知消费者线程
                buns.flag = true; //修改标记
                buns.notify(); //唤醒等待的线程,我还是可以再次争抢
            }
            i++;
        }
    }
}
//消费者线程
public class GetThread extends Thread {
    private Buns buns;

    public GetThread(Buns buns) {
        this.buns = buns;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (buns) {
                if (!buns.flag) {
                    //进来了,说明没有资源
                    try {
                        buns.wait(); //线程一旦等待,就会释放锁,下次被唤醒,就从这里醒来,也就是从哪里等待,从哪里醒来
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //消费资源
                System.out.println(buns.name + "====" + buns.price);
                //通知生产线程去生产
                buns.flag = false;
                buns.notify();
            }

        }
    }
}
public class MyTest {

    public static void main(String[] args) {
        //生成者线程  SetThread
        //消费者线程  GetThread
        //资源 Bun  两个线程共享

        //出现线程安全问题,符合的条件
        //1.是多线程环境
        //2.多个线程有共享资源
        //3.有多条语句在操作这个共享资源


        //生产线程,生产出了资源,也就是有资源,就等着,不能干等着,得通知消费线程,消费资源
        //消费线程,有了资源,消费资源,消费后资源没有了,就等着,不能干等着,得通知生产线程,去生产。

    /*    void notify ()
        唤醒在此对象监视器上等待的单个线程。
        void notifyAll ()
        唤醒在此对象监视器上等待的所有线程。

        void wait ()
        在其他线程调用此对象的 notify () 方法或 notifyAll () 方法前,导致当前线程等待。
        void wait ( long timeout)
        在其他线程调用此对象的 notify () 方法或 notifyAll () 方法,或者超过指定的时间量前,导致当前线程等待。*/


        Buns buns = new Buns();
        SetThread th1 = new SetThread(buns);
        GetThread th2 = new GetThread(buns);
        th2.start();
        th1.start();
    }
}

wait()和sleep()的区别

 Wait和Sleep的区别:
 1.它们最大本质的区别是,Sleep()不释放同步锁,Wait()释放同步锁。

 2.还有用法的上的不同是:Sleep(milliseconds)可以用时间指定来使他自动醒过来,如果时间不

 3.到你只能调用Interreput()来强行打断;Wait()可以用Notify()直接唤起。

 4.这两个方法来自不同的类分别是Thread和Object

 5.最主要是Sleep方法没有释放锁,而Wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。

内存可见性问题 volatile

当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取

时,它会去内存中读取新值。

而普通的共享变量不能保证可见性,因为普通共享变量被修改之后,什么时候被写入主存是不确定

的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。

另外,通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个

线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保

证可见性。

public class MyTest {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        new Thread(myRunnable).start();
        while (true) {
            if (myRunnable.getFlag()) { //
                System.out.println("进来了~~~");
                break;
            }
        }
    }
}

class MyRunnable implements Runnable {
    //volatile 可以保证线程的内存可见性的问题,但是不能保证原子性的问题。
    //当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。
    volatile boolean flag = false;

    public boolean getFlag() {
        return flag;
    }

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

    @Override
    public void run() {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //修改flag的值
        flag = true;
        System.out.println("线程修改了flag的值是:" + flag);
    }
}

原子性问题,不能使用volatile,可以使用synchronized,但效率低,性能下降

用CAS算法(Compare-And-Swap)

CAS 是一种无锁的非阻塞算法的实现

CAS 包含了 3 个操作数:

需要读写的内存值 V

进行比较的值 A

拟写入的新值 B

AtomicInteger 原子变量,实现了 CAS 算法

AtomicInteger i = new AtomicInteger();

public class MyTest {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(myRunnable);
            thread.start();
        }
    }
}

class MyRunnable implements Runnable {
    //int i = 1;
    //  AtomicInteger(int initialValue)  创建具有给定初始值的新 AtomicInteger.

    //原子变量,实现CAS算法
    AtomicInteger i = new AtomicInteger(1);

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":" + i.getAndIncrement()); //
        }
    }
}

线程的状态

匿名内部类创建线程

普通方法:

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("线程执行了");
    }
}


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

        MyThread myThread = new MyThread();
        myThread.start();

    }
}
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("线程执行了2222");
    }
}


public class MyTest {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        new Thread(myThread).start();
    }
}

利用匿名内部类创建

public class MyTest {
    public static void main(String[] args) {
        //采用匿名内部类的方式来创建线程

        //方式1
        new Thread() {
            @Override
            public void run() {
                System.out.println("执行了111111");
            }
        }.start();

        //方式2:
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("任务执行了");
            }
        }).start();

    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
接入第三方登录是让用户方便快捷地使用已有账号登录你的网站或应用程序,提高用户体验的一种方式。本文将介绍如何使用 PHP 实现微信公众号第三方登录。 1. 获取微信授权 首先,需要获取微信用户的授权。具体步骤如下: 1)引导用户打开微信授权页面: ```php $appid = 'your_appid'; $redirect_uri = urlencode('http://yourdomain.com/callback.php'); $scope = 'snsapi_userinfo'; $url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=$appid&redirect_uri=$redirect_uri&response_type=code&scope=$scope&state=STATE#wechat_redirect"; header("Location: $url"); ``` 其中,`$appid` 是你的微信公众号的 AppID,`$redirect_uri` 是授权后回调的 URL,`$scope` 是授权作用域,可以是 `snsapi_base` 或 `snsapi_userinfo`,`$state` 是自定义参数,用于防止 CSRF 攻击。 2)获取授权码: 用户同意授权后,会重定向到 `$redirect_uri` 指定的 URL,带上授权码 `code` 和 `state` 参数。 ```php $code = $_GET['code']; $state = $_GET['state']; ``` 3)获取 access_token 和 openid: 使用授权码 `code` 获取 `access_token` 和 `openid`。 ```php $access_token_url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=$appid&secret=$secret&code=$code&grant_type=authorization_code"; $response = file_get_contents($access_token_url); $result = json_decode($response, true); $access_token = $result['access_token']; $openid = $result['openid']; ``` 其中,`$secret` 是你的微信公众号的 AppSecret。 2. 获取用户信息 获取到 `access_token` 和 `openid` 后,可以使用以下代码获取用户信息: ```php $userinfo_url = "https://api.weixin.qq.com/sns/userinfo?access_token=$access_token&openid=$openid&lang=zh_CN"; $response = file_get_contents($userinfo_url); $userinfo = json_decode($response, true); ``` 其中,`$userinfo` 包含用户的昵称、头像等信息。 3. 将用户信息保存到数据库 最后,将获取到的用户信息保存到数据库中,以便下次使用时快速登录。 ```php // 连接数据库 $con = mysqli_connect('localhost', 'username', 'password', 'database'); mysqli_set_charset($con, "utf8"); // 查询用户是否已存在 $sql = "SELECT * FROM users WHERE openid='$openid'"; $result = mysqli_query($con, $sql); if (mysqli_num_rows($result) == 0) { // 用户不存在,插入新用户信息 $nickname = mysqli_real_escape_string($con, $userinfo['nickname']); $headimgurl = mysqli_real_escape_string($con, $userinfo['headimgurl']); $sql = "INSERT INTO users (openid, nickname, headimgurl) VALUES ('$openid', '$nickname', '$headimgurl')"; mysqli_query($con, $sql); } // 保存用户登录状态 $_SESSION['openid'] = $openid; ``` 以上就是使用 PHP 实现微信公众号第三方登录的步骤。需要注意的是,为了确保安全性,应该对用户输入的数据进行过滤和验证,防止 SQL 注入和 XSS 攻击等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jmh-Ethereal

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值