线程同步锁

本文深入探讨了Java中的多线程概念,包括对象锁、同步锁和互斥锁的使用,避免死锁的策略,线程间的wait、notify和notifyAll通信机制,以及生产者消费者模式的应用。同时,介绍了线程池的概念和工作原理,强调其在管理大量线程时的重要性。

一、对象锁/同步锁/互斥锁

每个对象都有一个自己的同步锁,当一个线程调用了对象的synchronized修饰的方法或语句块时,这个线程就获得 了对象的同步锁,其他线程如果这时调用了对象的synchronized修饰的方法或语句块时,将等待,当线程执行完 synchronized方法或语句块时,释放锁。

public class Account {
    private int money = 20000;
    public synchronized void add() {
        money+=1000;
        System.out.println("存了1000元,余额为:"+money);
    }
    public synchronized void sub() {
        money-=1000;
        System.out.println("取了1000元,余额为:"+money);
    }
    public static void main(String[] args) {
        Account account = new Account();
        AddRunnable ar = new AddRunnable(account);
        SubRunnable sr = new SubRunnable(account);
        Thread t1 = new Thread(ar);
        Thread t2 = new Thread(sr);
        t1.start();
        t2.start();
    }
}

语句块方式:

public void add() {
    //多线程执行到synchronized时,要获得哪个对象的同步锁
    synchronized (this) {
    money+=1000;
    System.out.println("存了1000元,余额为:"+money);
    }
}

两个线程就实现了同步,Account对象是线程安全的

二、死锁

两个线程互相等待对方释放同步锁,造成程序永久等待的情况。死锁是不可预知的,通过编程尽量控制死锁的产 生,例如加锁顺序。

public class DeadLock implements Runnable{
private static Object obj1 = new Object();
private static Object obj2 = new Object();
public boolean flag = true;
    @Override
    public void run() {
        while(true) {
            if(flag) {
                synchronized (obj1) {
                    synchronized (obj2) {
                        System.out.println("obj1 -> obj2");
                    }
                }
            }else {
                synchronized (obj1) {
                    synchronized (obj2) {
                        System.out.println("obj1 -> obj2");
                    }
                }
            }
        }
    }
    public static void main(String[] args) {
        DeadLock d1 = new DeadLock();
        DeadLock d2 = new DeadLock();
        d2.flag = false;
        Thread t1 = new Thread(d1);
        Thread t2 = new Thread(d2);
        t1.start();
        t2.start();
    }
}

三、线程八锁

1. 线程获得对象锁后,如果sleep,不释放对象锁

2. 线程调用synchronized方法或语句块时,获得对象锁;调用普通方法不需要获得对象锁;

3. 线程调用synchronized的静态方法,获得的是类锁(加载的class对象的对象锁)

四、线程通信

1. wait方法:是Object类的方法,意味着所有对象都有wait方法,调用了一个对象的wait方法,获得这个对象锁 的线程将等待,并释放对象锁。调用对象的wait方法时,一定要有一个线程获得对象的对象锁,否则抛出异常

2. notify方法:是Object类的方法,调用一个对象的notify方法,在这个对象上等待的线程中随机唤醒一个。

3. nofifyAll方法:在这个对象上等待的线程全都唤醒。

五、sleep和wait的区别:

1. sleep是Thread类的方法,wait是Object类的方法

2. sleep不释放对象锁,wait释放对象锁

3. sleep只能到指定的时间自动唤醒,wait可以指定时间,也可以通过notify主动唤醒

六、示例:生产者和消费者模式

生产者负责生产,消费者负责消费,生产/消费的资源保持一个平衡

public class Chicken {
    private int count ; //count保持在10
        public synchronized void add() {
            //生产者线程 ,当count>10,让生产者线程等待,唤醒消费者线程
            while(count>10) {
                try {
                    this.wait(); //等待,被唤醒时,执行下面的代码
                } catch (InterruptedException e) {

                    e.printStackTrace();
                }
            }
            count++;
            this.notifyAll();
            System.out.println("生产了一只烧鸡,柜台剩余:"+count);
        }
        public synchronized void sub() {
            //消费线程 ,当count<10,让消费者线程等待,唤醒生产者线程
            while(count<10) {
                try {
                    this.wait();
                } catch (InterruptedException e) {

                    e.printStackTrace();
                }
            }
            count--;
            this.notifyAll();
            System.out.println("消费了一只烧鸡,柜台剩余:"+count);
        }
        public static void main(String[] args) {
            Chicken chicken = new Chicken();
            ProductRunnable pr = new ProductRunnable(chicken);
            ConsummerRunnable cr = new ConsummerRunnable(chicken);
            new Thread(pr).start();
            new Thread(pr).start();
            new Thread(pr).start();
            new Thread(pr).start();
            new Thread(cr).start();
            new Thread(cr).start();
            new Thread(cr).start();
            new Thread(cr).start();
            new Thread(cr).start();
            new Thread(cr).start();
    }
}

七、线程池:

在项目中可能会创建大量的多线程,在执行完多线程后,线程对象被销毁,又有多线程需求时,再次创建多线程, 要反复的创建、销毁线程。可以用线程池管理多线程。

public static void main(String[] args) {
    ThreadPoolExecutor pool = new ThreadPoolExecutor(3, 5, 10,
        TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(6),new ThreadPoolExecutor.DiscardPolicy());
        for(int i = 1;i<=11;i++) {
            pool.execute(new TestRunnable(String.valueOf(i)));
        }
        //pool.shutdown();
    }

线程池的工作过程

创建线程池时,要指定核心线程数、最大线程数、存活时间、时间单位、拒绝策略参数。当线程池开始执行任务 时,创建多线程执行任务,当线程数达到了核心线程数,且都在执行任务,线程池执行新任务时,这个任务将存储 到队列中,当队列满时,线程池执行新任务时,再创建新的线程执行新的任务,要保证线程数不能超过最大线程 数。如果达到了最大线程数、队列也满了,再来任务时,将采取拒绝策略(四种,放弃任务抛异常/不抛异常,当 前执行execute的线程执行这个任务,将队列中最久的任务放弃执行新的任务)。当线程数大于核心线程数,且线 程空闲且达到了设置的存活时间,这个线程被销毁。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

aigo-2021

您的鼓励是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值