#Java多线程02

线程同步

多个线程操作同一个资源

并发

同一个对象被多个线程同时操控

线程同步其实就是一种等待机制(类似于排队),多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用
线程同步条件:队列 + 锁
锁举例:厕所,前一个人上厕所要锁门,等前一个人解决完了才能让下一个人进来厕所
锁机制(synchronized):当一个线程操作一个对象的时候,会获得对象的排它锁,独占资源,其他线程必须等待,当前线程使用完释放锁即可

锁机制存在的一些问题

三大不安全案例

略过,反正就是讲线程不同步就会不安全

同步方法以及同步块(隐式锁)

同步方法:使用synchronized关键字

public synchronized void method(int args){}

同步块:使用synchronized块
每个对象都有一把锁,每个synchronized方法必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就会独占该锁,直到该方法返回才释放锁,后面的线程才能获得这把锁,才能执行
缺陷:将一个大的方法申明为synchronized效率将会降低


只读的代码无需使用同步方法,因为读数据并不会造成线程不安全,要修改数据的代码则需要使用同步方法,只选择有必要的地方使用同步方法,可以提高效率,避免浪费
修改案例(原本不安全买票)

public class UnsafeBuyTicket {

    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();

        new Thread(buyTicket, "hong").start();
        new Thread(buyTicket, "ming").start();
        new Thread(buyTicket, "huang").start();
    }

}

class BuyTicket implements Runnable{
    //票数
    private int ticketNums = 10;
    //外部停止标识
    boolean flag = true;
    //外部停止方法
    public void stop(){
        this.flag = false;
    }

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

//synchronized同步锁,导致三个线程没办法同时操作一个对象,保证了安全性
    private synchronized void buy(){
        if (ticketNums <= 0){
            stop();
            return;
        }
        //模拟延时
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //买票
        System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--);
    }


}

上一章没加synchronized 同步锁的情况

解决了前面那一章不安全的情况

死锁

发生原因:两个线程都独自占有一些资源,然后线程A想要线程B的资源,线程B想要线程A的资源,结果就僵持住了,程序停止运行
某一个同步块同时拥有两个以上对象的锁时,就有可能发生死锁问题
解析:下面这个例子中,hong获得了pencil的锁等1秒过后并没有释放pencil的锁,而是想要继续获得eraser的锁,这肯定是不行的,因为ming已经获得了eraser的锁,并且这个ming还想要pencil的锁,两个线程互相抢对方的资源,就死锁了

public class DeadLock {
    public static void main(String[] args) {
        DoHomeWork t1 = new DoHomeWork(0,"hong");
        DoHomeWork t2 = new DoHomeWork(1,"ming");
        t1.start();
        t2.start();
    }
}

class Pencil{

}
class Eraser{

}

class DoHomeWork extends Thread{
    //保证资源只有一份
    static Pencil pencil = new Pencil();
    static Eraser eraser = new Eraser();

    int choice;
    String name;

    //继承Thread类要手动写构造器
    DoHomeWork(int choice, String name){
        this.choice = choice;
        this.name = name;
    }

    @Override
    public void run() {
        try {
            doYourHomeWork();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void doYourHomeWork() throws InterruptedException {
        if(choice == 0){
            synchronized (pencil){
                System.out.println(this.name + "获得笔的锁");
                Thread.sleep(1000);
                synchronized (eraser){
                    System.out.println(this.name + "获得橡皮擦的锁");
                }
            }
            
        }else {
            synchronized (eraser){
                System.out.println(this.name + "获得橡皮擦的锁");
                Thread.sleep(2000);
                synchronized (pencil){
                    System.out.println(this.name + "获得笔的锁");
                }
            }
        }
    }

    
}

解决方法:把同步块分开写即可

synchronized (pencil){
        System.out.println(this.name + "获得笔的锁");
        Thread.sleep(1000);
}
synchronized (eraser){
        System.out.println(this.name + "获得橡皮擦的锁");
}

这样sleep时间一到,就自动释放锁pencil锁,也就不会发生互相抢资源的现象了,之前是嵌套写,时间到了还是不会释放锁

LOCK锁(显示定义同步锁)

前面写的同步方法和同步块利用的是synchronized 关键字,这是隐式定义锁,出了作用域会自动释放锁,
而LOCK锁需要手动开启锁和关闭锁
首先要定义一个LOCK对象
ReentrantLock---可重入锁

private final ReentrantLock lock = new ReentrantLock();

加锁方法---写在try语块里

lock.lock();

解锁方法---写在finally语块里

lock.unlock();

需要在重写run方法里面使用
例子的话就用上面同步方法修改案例(原本不安全买票)的例子,修改了run方法,以及加了几个语句

    @Override
    public void run() {
        while (flag){
            try {
                lock.lock();//建议使用try-catch语块
                buy();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }

        }
    }

补充:要执行的代码如果没有异常就写finally里,有异常写try里

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容