Java多线程:线程优先级,同步,死锁

Java多线程(二)

1.线程优先级

  • java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行
  • 线程的优先级数字表示范围:1~10
    1. Thread.MIN_PRIORITY = 1;
    2. Thread.MAX_PRIORITY = 10;
    3. Thread.NORM_PRIORITY = 5;
  • 获取优先级:getPriority(); 改变优先级:setPriority(int xxx);
  • 优先级低表示被CPU调度的概率低,无法保证优先级高一定先执行
  • 优先级设置在前,start()在后
//测试线程优先级
public class TestPriority {
    public static void main(String[] args) {
        //主线程优先级
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
        MyPriority myPriority = new MyPriority();
        Thread t1 = new Thread(myPriority);
        Thread t2 = new Thread(myPriority);
        Thread t3 = new Thread(myPriority);
        Thread t4 = new Thread(myPriority);
        Thread t5 = new Thread(myPriority);

        t1.start();

        //先设置优先级,再启动
        t2.setPriority(1);
        t2.start();

        t3.setPriority(Thread.MAX_PRIORITY);//最大优先级 10
        t3.start();

        t4.setPriority(Thread.NORM_PRIORITY);
        t4.start();

//        t5.setPriority(11); //异常
//        t5.start();
    }
}

class MyPriority implements Runnable{
    @Override
    public void run() {
        //输出当前线程和其优先级
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
    }
}

2.守护(daemon)线程

  1. 线程分为用户线程守护线程
  2. 虚拟机必须确保用户线程执行完毕,不用等待守护线程执行完,如后台记录操作日志,垃圾回收等

示例:守护线程(世界与你)

public class TestDaemon {
    public static void main(String[] args) {
        World world = new World();
        You you = new You();

        Thread thread = new Thread(world);
        thread.setDaemon(true);//setDaemon()默认为false,表示用户线程
        thread.start();//世界 守护线程启动

        new Thread(you).start();//你 用户线程启动
    }
}

//世界
class World implements Runnable{
    @Override
    public void run() {
        while (true) {
            System.out.println("你存在这世界");
        }
    }
}

class You implements Runnable {
    @Override
    public void run() {
        for(int i = 1; i < 36500; i++) {
            System.out.println("你,开心每一天");
        }
        System.out.println("--------------hello,world!-----------------");
    }
}

3. 线程同步

针对多个线程操作同一个资源的情况。线程同步是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用。

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

  • 由于同一进程的多个线程共享一块存储空间,会带来访问冲突问题,因此在访问时加入锁机制 synchronized,当一个线程获得对象的排他锁时,独占资源,其他线程必须等待该线程使用完毕后释放锁,存在以下问题
    1. 一个线程持有锁会导致其他所有需要此锁的线程挂起;
    2. 多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题
    3. 如果一个优先级高的线程等待一个低优先级的线程释放锁,会导致优先级倒置的性能问题。

示例:线程不安全例子

示例1:购票

//线程不安全:购票,多个人买到同一张票
public class UnsafeTicket implements Runnable{
    private int ticketNums = 10; //总票数
    boolean flag = true; //停止标志位

    @Override
    public void run() {
        //买票
        while (flag) {
            buy();
        }
    }

    public void buy() {
        if(ticketNums <= 0){//判断是否有票
            flag = false;
            return;
        }
        //模拟延时
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
    }

    public static void main(String[] args) {
        UnsafeTicket unsafeTicket = new UnsafeTicket();
        new Thread(unsafeTicket,"t1").start();
        new Thread(unsafeTicket,"t2").start();
        new Thread(unsafeTicket,"t3").start();
    }
}

**示例二:银行取款

//线程不安全示例:银行账户取款
public class UnsafeDemo2 {
    public static void main(String[] args) {
        Account account = new Account(100,"基金");
        Drawing you = new Drawing(account,50,"你");
        Drawing grilFriend = new Drawing(account,100,"grilFriend");

        you.start();
        grilFriend.start();
    }
}

//账户
class Account{
    int money; //余额
    String name; //卡名

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}

//银行
class Drawing extends Thread {
    Account account;  //账户
    int drawingMoney; //取钱数
    int nowMoney;     //手里现金

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

    @Override
    public void run() {
        //判断现金是否足够
        if(account.money - drawingMoney < 0) {
            System.out.println("账户余额不足,无法取出");
        }
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //卡内余额更新
        account.money = account.money - drawingMoney;
        //手里现金更细
        nowMoney = nowMoney + drawingMoney;

        System.out.println(account.name + " 余额为:"+account.money);
        //Thread.currentThread().getName() = this.getName()
        System.out.println(this.getName() + "手里现金为:"+nowMoney);
    }
}

synchronized:控制对“对象”的访问,每个对象对应一把锁,每个synchronized方法必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,直到该方法返回才释放锁。

synchronized两种用法:方法和块。

缺点:大方法申明为synchronized会影响效率。

  • 同步块:synchronized(Obj) {}
  • Obj为同步监视器,可以为任何对象,一般使用共享资源作为同步监视器。同步方法中无需指定同步监视器,其同步监视器就是this,这个对象本身,或者是class
  • 同步监视器的执行过程:
    1. 第一个线程访问,锁定同步监视器,执行其中代码
    2. 第二个线程访问,发现同步监视器被锁定,无法访问
    3. 第一个线程访问完毕,解锁同步监视器
    4. 第二个线程访问,发现同步监视器没有锁,然后锁定它并访问
//线程不安全示例:银行账户取款
public class UnsafeDemo2 {
    public static void main(String[] args) {
        Account account = new Account(100,"基金");
        Drawing you = new Drawing(account,50,"你");
        Drawing grilFriend = new Drawing(account,100,"grilFriend");

        you.start();
        grilFriend.start();
    }
}

//账户
class Account{
    int money; //余额
    String name; //卡名

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}

//银行
class Drawing extends Thread {
    Account account;  //账户
    int drawingMoney; //取钱数
    int nowMoney;     //手里现金

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

    //public synchronized void run()此处加synchronized,只能锁Drawing,不能锁操作的account,还是会出问题
    @Override
    public void run() {
        synchronized (account) {
            //判断现金是否足够
            if(account.money - drawingMoney < 0) {
                System.out.println(Thread.currentThread().getName()+"账户余额不足,无法取出");
                return;
            }
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //卡内余额更新
            account.money = account.money - drawingMoney;
            //手里现金更细
            nowMoney = nowMoney + drawingMoney;

            System.out.println(account.name + " 余额为:"+account.money);
            //Thread.currentThread().getName() = this.getName()
            System.out.println(this.getName() + "手里现金为:"+nowMoney);
        }

    }
}

4.死锁

多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源释放才能运行,而导致多个线程都在等待对方释放资源,都停止执行的情形。某一个同步块同时拥有两个以上对象的锁时,就可能出现死锁的问题。

//死锁示例:化妆
public class DeadLockDemo {
    public static void main(String[] args) {
        Makeup g1 = new Makeup(0,"Alice");
        Makeup g2 = new Makeup(1,"Slina");
        g1.start();
        g2.start();
    }
}

//口红
class Lipstick{

}

//镜子
class Mirror{

}

class Makeup extends Thread{
    //static 确保资源只有一份
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();

    int choice; //选择
    String girlName;//使用者

    public Makeup(int choice, String girlName) {
        this.choice = choice;
        this.girlName = girlName;
    }

    @Override
    public void run() {
        //化妆
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //化妆,互相持有对方的锁,需要拿到对方的资源
    private void makeup() throws InterruptedException {
        if(choice == 0) {
            synchronized (lipstick) {//获得口红锁
                System.out.println(this.girlName + "获得口红锁");
                Thread.sleep(1000);
                synchronized (mirror) {//一秒后想获得镜子锁
                    System.out.println(this.girlName + "获得镜子锁");
                }
            }
        } else {
            synchronized (mirror) {
                System.out.println(this.girlName + "获得镜子锁");
                Thread.sleep(2200);
                synchronized (lipstick) {
                    System.out.println(this.girlName + "获得口红锁");
                }
            }
        }
    }
    //解决死锁:不能同时拥有两把锁
    /*    private void makeup() throws InterruptedException {
        if(choice == 0) {
            synchronized (lipstick) {//获得口红锁
                System.out.println(this.girlName + "获得口红锁");
                Thread.sleep(1000);

            }
            synchronized (mirror) {//一秒后想获得镜子锁
                System.out.println(this.girlName + "获得镜子锁");
            }
        } else {
            synchronized (mirror) {
                System.out.println(this.girlName + "获得镜子锁");
                Thread.sleep(2200);

            }
            synchronized (lipstick) {
                System.out.println(this.girlName + "获得口红锁");
            }
        }
    }*/
}
  • 死锁的四个必要条件:
    1. 互斥条件:一个资源每次只能被一个进程使用
    2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不释放
    3. 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺
    4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系

破坏其中任一体条件就可以避免死锁发生。

5. Lock(锁)

JDK5以后,Java 提供更强大的线程同步机制–通过显示定义同步锁对象来实现同步。

java.util.concurrent.locks.Lock接口时控制多个线程对共享资源进行访问的工具。每次只能有一个线程对Lock对象加锁。ReentrantLock实现了Lock,拥有与 synchronized相同的并发性和内存语义。

Lock是显示锁(需要手动开,关),synchronized是隐式锁,出作用域自动释放

Lock只有代码块锁,JVM花费较少的时间调度线程,性能更好。具有更多子类,扩展性好

一般优先顺序:Lock > 同步代码块(已经进入方法体,分配了相应资源) > 同步方法

//Lock 锁示例
public class TestLockDemo {
    public static void main(String[] args) {
        MyLock myLock = new MyLock();
        new Thread(myLock,"t1").start();
        new Thread(myLock,"t2").start();
        new Thread(myLock,"t3").start();
    }
}

class MyLock implements Runnable {
    int tickNums = 10;
    //定义lock锁
    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try{
                lock.lock();
                if(tickNums > 0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(tickNums--);
                } else {
                    break;
                }
            } finally {
                lock.unlock();
            }
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值