多线程p4

本文介绍了Java中的守护线程概念,其作为后台线程为用户线程提供服务,以及如何设置线程为守护线程。文章还探讨了线程同步的重要性,特别是如何通过synchronized关键字和锁机制来解决并发问题,避免数据不一致。
摘要由CSDN通过智能技术生成

守护线程

  • 定义:守护线程–也称“服务线程”,他是后台线程,它有一个特性,即为用户线程 提供 公共服务,在没有用户线程可服务时会自动离开。
  • 优先级:守护线程的优先级比较低,用于为系统中的其它对象和线程提供服务。
  • 设置:通过 setDaemon(true)来设置线程为“守护线程”;将一个用户线程设置为守护线程的方式是在线程对象创建后,启动前调用setDaemon()方法将该线程设置为守护线程。
  • 在 Daemon 线程中产生的新线程也是 Daemon 的。

线程分为守护线程和用户线程,一般创建的都是用户线程,通过setDaemon()设置线程为守护线程。Jvm必须确保用户线程执行完毕、Jvm不用等待守护线程执行完毕

/**
 *      本节为测试守护线程
 *    上帝守护着你
 *      创建两个线程一个设置为守护线程,另一个为用户线程
 *          虚拟机会确保用户线程执行完毕
 *          虚拟机不用等到守护线程执行完毕
 */
public class TestDaemon {
    public static void main(String[] args) {
        God god = new God();
        You you = new You();
        Thread thread = new Thread(god);//创建God 线程
        thread.setDaemon(true); //将该线程设置为守护线程
        thread.start();
        new Thread(you).start();
    }
}

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

//你----用户线程
class You implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 36500; i++) {
            System.out.println("开开心心的活每一天");
        }
        System.out.println("-----Good Bye! The beautiful world!-----");
    }
}

这段代码创建了两个线程一个是守护线程(上帝),一个是用户线程(你)。守护线程会一直运行输出“上帝在守护着你”,程序在执行完用户线程中的run()方法后销毁线程,由于守护线程是设置为守护线程的,当所有用户线程结束后,守护线程也会随之结束,即使他可能还 没有执行结束。因此用户线程执行完毕后程序结束,但由于虚拟机关闭还需时间所以会在输出"-----Good Bye! The beautiful world!-----“后继续输出一会"上帝在守护着你”

线程同步

并发

并发:同一个对象被多个线程同时操作

情景再现
现实生活中,会遇到“并发”的问题,例如下课铃声一响,大伙直冲食堂干饭,此时就会出现同一个资源,多个人都想使用的问题。最天然的解决办法就是排队,一个个来
代码案例
/**
 *      本节为线程同步中三大不安全案例1
 *   买票不安全案例
 *      线程不安全有负数。
 * */
public class UnsafeBuyTicket {

    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();
        //并发:同一对象被多个线程操作
        new Thread(buyTicket,"张三").start();
        new Thread(buyTicket,"李四").start();
        new Thread(buyTicket,"王五").start();
        new Thread(buyTicket,"老刘").start();
        new Thread(buyTicket,"路人甲").start();
    }
}

class BuyTicket implements Runnable{
    int tickets = 20;
    boolean flag = true;//外部停止方法
    @Override
    public void run() {
        while (flag){
            try {
                buy();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
    //买票
    private void buy() throws InterruptedException {
        //判断还有没有票
        if(tickets <= 0){
            flag = false;
            return;
        }
        //网络延迟,放大问题的发生性
        Thread.sleep(100);
        System.out.println(Thread.currentThread().getName() + "买了第" + tickets-- +"票");
    }
}

解释网络延迟会提高问题的发生性和出现票数为-1的情况

​ 多个线程操作一个对象,当票数为1时,线程a判断出票数大于0可以购买,但要延时一段时间后才能买,在这个程序中,各个线程时并发交替执行的,当线程a在等待延迟完购买时,线程b已经等待完延迟,进行了购买,此时线程a在购买导致出现了票数为-1的情况


这就是我们处理多线程问题的情景,多个线程访问同一个对象,并且某些线程还想修改这个对象。这时候我们就需要线程同步,线程同步其实就是一种等待机制,多个需要访问该对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕,下一个线程再使用。

与此同时,由于同一进程的多个线程共享同一块存储空间,在带来便利的同时也带来了访问冲突问题。为了保证数据在方法中被访问的正确性,因此在访问时加入锁机制。一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可。存在以下问题:

  • 锁被线程持有后,其他线程会进入阻塞状态
  • 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换调度延时,引起性能问题
  • 如果一个优先级高的线程等待一个性能低的线程释放锁也会导致优先级倒置,引起性能问题
并发问题解决

队列 + 锁

synchronized同步锁

在java中,每个对象都有一把内置的锁,这个锁是用来实现对象级别的同步的。当一个线程尝试获取对象的锁时,如果这个锁没有被其他线程持有,那么这个线程就会获得该对象的锁,并且可以执行被同步的代码块。如果该锁已经被其他线程持有,则线程会被阻塞,直到获取这个锁为止。

在多线程编程中,通常情况下使用synchronized关键字等同步机制来对共享资源进行保护,防止多个线程同时修改该资源造成数据的不一致。当使用同步机制时,线程要对资源进行操作时会被先要求获取该对象的锁,只有成功获取到锁的线程才能操作共享资源

因此,拿到锁的意思就是线程要先获取对象的锁,才能执行被同步的代码块,确保对共享资源的访问是安全的

synchronized关键字

在方法前加上synchronized关键字使其变为同步方法,锁的是this本身(对象|类)

//synchronized 同步方法,锁的是this
    private synchronized void buy() throws InterruptedException {
        //判断还有没有票
        if(tickets <= 0){
            flag = false;
            return;
        }
        //网络延迟,放大问题的发生性
        Thread.sleep(100);
        System.out.println(Thread.currentThread().getName() + "买了第" + tickets-- +"票");
    }

缺陷:若将一个大的方法申明为synchronized将会印象效率

因此,方法里面需要修改的内容才需要锁,锁的太多,浪费资源

synchronized同步块

synchronized(obj){}

obj称之为同步监视器,可以是任何对象,但是推荐使用共享资源作为同步监视器

/**
 *      本节是关于不安全线程案例三----银行取钱
 *    账户总余额100万用于结婚基金
 *    girlFriend取100万
 *    我取五十万
 * */
public class UnsafeBank {
    public static void main(String[] args) {
        Account account = new Account(1000,"婚礼基金");
        DrawMoney drawMoney = new DrawMoney(account,50,"You");
        DrawMoney girlFriend = new DrawMoney(account,100,"girlFriend");
        drawMoney.start();
        girlFriend.start();
    }
}
//账户
class Account{
    int money;//金额
    String name;//卡名

    public Account(int money, String name){
        this.money = money;
        this.name = name;
    }
}
//银行:模拟取钱
class DrawMoney extends Thread{
    //账户
    Account account;
    int getMoney;

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

    @Override
    public void run() {
        //锁的对象就是变化的量,需要增删改的对象
        synchronized (account){
            //判断能不能取钱
            if (account.money - getMoney < 0){
                System.out.println(account.name +"钱不够了,不能取");
                return;
            }
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            account.money = account.money - getMoney;
            System.out.println(this.getName()+"取走了"+ getMoney);
            System.out.println(account.name+"里的余额为"+account.money);
        }

    }
}
总结

Java多线程中,如果多个线程操作一个对象,就会引起并发问题。

  • 在对对象进行增删改的方法中添加synchronized关键字,该方法就会变成同步方法
  • 将对对象进行增删改的语句放入synchronized (){}同步块中
  • 17
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基于Python多线程的为四人分发扑克牌的程序: ```python import threading import random # 定义扑克牌花色和点数 SUITS = ['Spades', 'Hearts', 'Diamonds', 'Clubs'] RANKS = ['Ace', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'Jack', 'Queen', 'King'] # 初始化一副扑克牌 deck = [(r, s) for r in RANKS for s in SUITS] random.shuffle(deck) # 定义一个类表示一个玩家 class Player: def __init__(self, name): self.name = name self.hand = [] def add_card(self, card): self.hand.append(card) def show_hand(self): print(f"{self.name}'s hand: {self.hand}") # 定义一个类表示一个发牌器 class Dealer: def __init__(self): self.players = [] self.num_players = 4 def add_player(self, player): self.players.append(player) def deal(self): while len(deck) > 0: for player in self.players: if len(deck) == 0: break card = deck.pop() player.add_card(card) # 创建四个玩家并加入发牌器 p1 = Player('Player 1') p2 = Player('Player 2') p3 = Player('Player 3') p4 = Player('Player 4') dealer = Dealer() dealer.add_player(p1) dealer.add_player(p2) dealer.add_player(p3) dealer.add_player(p4) # 创建一个线程来发牌 def deal_cards(): dealer.deal() # 启动线程并等待线程结束 thread = threading.Thread(target=deal_cards) thread.start() thread.join() # 显示每个玩家的手牌 p1.show_hand() p2.show_hand() p3.show_hand() p4.show_hand() ``` 运行这个程序后,它将输出每个玩家的手牌。注意,由于随机性,每次运行程序输出的手牌可能不同。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值