Java多线程与高并发(二)

synchronized同步

加synchronization的前提

  1.必须两个或者两个以上的线程同时访问一个共享资源

  2.必须保证同步中只能有一个线程在运行

synchronization锁的是共享对象,而不是代码

 1.同步代码块

语法synchronization(共享资源,共享对象,需要是Object的子类){

核心业务逻辑

}

代码示例:

package com.juc.ticket;

/**
 * @Author zcm
 * @Email [email protected]
 * @date 2020/5/25 20:13
 * @Description 同步代码块的用法
 */
public class Ticket implements Runnable {
    private static int ticket = 5;
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (ticket > 0) {
                synchronized (this) {
                    System.out.println(Thread.currentThread().getName() + "----正在出售第" + (ticket--) + "张票");
                }
            }
        }
    }

    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        for (int i = 1; i <= 5; i++) {
            new Thread(ticket, "买票口" + i).start();
        }
    }
}

2.同步方法的用法

直接在要锁定的资源上加上synchronized关键字就好。

package com.juc.ticket;

public class T_002 implements Runnable {

    private static int ticket = 5;

    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //这里调用同步方法不加this,调用的方法只属于当前对象,加this就属于类
            this.test();
        }
    }

    private synchronized void test() {
        if (ticket > 0) {
            System.out.println(Thread.currentThread().getName() + "---正在出售第" + (ticket--) + "张票");
        }
    }

    public static void main(String[] args) {
        T_002 t_002 = new T_002();
        for (int i = 1; i <= 5; i++) {
            new Thread(t_002, "买票口" + i).start();
        }
    }
}

以上两种加锁方式有什么区别

同步代码块需要指定加锁对象

同步方法不需要指定加锁对象,方法调用的时候记得加上this,让这个方法归属于类,而不是当前对象。

线程同步小结

同步监视器 

1.synchronized(obj){} 的obj成为同步监视器

2.同步代码块中的同步监视器可以是任何对象,推荐共享资源对象作为同步监视器

3.同步方法中无需指定同步监视器,因为同步方法的同步监视器是this,也是该对象本身。

同步监视器执行的过程

1.线程一访问cpu资源并锁定同步监视器,让其他线程等待,自己执行其中代码

2.线程二访问cpu资源,发现监视器被锁,无法访问,只能等待释放。

3.线程一访问完毕并解锁同步监视器

4.线程二访问cpu资源,发现同步监视器未锁,锁定并访问,直至最后访问完毕释放锁,其他的线程才能抢占资源,谁先抢到谁先用。

小练习 

 使用synchronized实现张三和妻子同时去银行取钱的过程。

示例代码:

package com.juc.bank;

import java.math.BigDecimal;

/**
 * @Author zcm
 * @Email [email protected]
 * @date 2020/5/25 21:12
 * @Description 张三和张三的妻子同时取走500元
 */
public class Take implements Runnable {
    private BigDecimal account = new BigDecimal(1500);

    @Override
    public void run() {

        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (account) {
                if (account.compareTo(BigDecimal.valueOf(500)) >0) {
                    System.out.println(Thread.currentThread().getName() + "----取走了500.00元");
                    System.out.println("剩余:" + (account = account.subtract(BigDecimal.valueOf(500))));
                }
            }
        }
    }

    public static void main(String[] args) {
        Take take = new Take();
        new Thread(take, "张三").start();
        new Thread(take, "张三的妻子").start();
    }

}

死锁问题

1.同步可以保证资源共享操作的正确性,但是过多同步也会产生死锁问题。

2.那么死锁什么原因产生的呢?

    死锁一般情况下表示相互等待,是程序运行时出现的一种情况

例如下图

生产者与消费者

1.生产者不断的生产,消费者不断的取走生产者生产的产品

2.生产者将生产的产品放在一个公共的区域中,消费者去这个区域消费生产者生产的产品。

过程图

示例代码1

共享资源

package com.juc.pc.c_001;

/**
 * @Author zcm
 * @Email [email protected]
 * @date 2020/5/25 23:05
 * @Description 共享资源
 */
public class Goods {
    /**
     * @Author zcm
     * @Email [email protected]
     * @date 2020/5/25 23:04
     * @Description 产品名称
     */
    private String name;
    /**
     * @Author zcm
     * @Email [email protected]
     * @date 2020/5/25 23:05
     * @Description 品牌
     */
    private String brand;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }
}

消费者代码

package com.juc.pc.c_001;

/**
 * @Author zcm
 * @Email [email protected]
 * @date 2020/5/25 23:40
 * @Description 消费者
 */
public class Consumer implements Runnable {
    private Goods goods;

    public Consumer() {
    }

    public Consumer(Goods goods) {
        this.goods = goods;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("消费者消费了" + goods.getBrand() + "--" + goods.getName());
        }
    }
}

生产者代码

package com.juc.pc.c_001;

import com.juc.bank.Take;

/**
 * @Author zcm
 * @Email [email protected]
 * @date 2020/5/25 23:06
 * @Description 生产者
 */
public class Producer implements Runnable {
    private Goods goods;

    public Producer() {
    }

    public Producer(Goods goods) {
        this.goods = goods;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {

            if (i % 2 == 0) {
                goods.setBrand("哇哈哈");
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                goods.setName("矿泉水");
            }else{
                goods.setBrand("旺仔");
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                goods.setName("小馒头");
            }
            System.out.println("生产者生产了"+goods.getBrand()+"---"+goods.getName());
        }
    }
}

测试代码

package com.juc.pc.c_001;

public class Test {
    public static void main(String[] args) {
        Goods goods = new Goods();
        Producer producer = new Producer(goods);
        Consumer consumer = new Consumer(goods);
        new Thread(producer).start();
        new Thread(consumer).start();
    }
}

执行结果:

解决方案:

加锁synchronized

示例代码2:

共享代码

package com.java0526.pc.c_002;

/**
 * @program: untitled
 * @ClassName Goods
 * @Description
 * @Author zcm
 * @Date 2020/5/26 12:58
 * @Version V1.0
 */

public class Goods {
    private String brand;

    private String name;

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    /**
     * @Description: 生产者开始生产
     * @Author: zcm
     * @return: Param:
     * @Date:2020/5/26 13:03
     */
    public synchronized void set(String brand, String name) {
        this.setBrand(brand);
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.setName(name);
        System.out.println("生产者开始生产产品:" + this.getBrand() + "---" + this.getName());
    }

    /**
     * @Description: 消费者开始消费产品
     * @Author: zcm
     * @return: Param:
     * @Date:2020/5/26 13:03
     */
    public synchronized void get() {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("消费者开始消费产品:" + this.getBrand() + "---" + this.getName());
    }

}

生产者代码

package com.java0526.pc.c_002;

/**
 * @program: untitled
 * @ClassName Producer
 * @Description
 * @Author zcm
 * @Date 2020/5/26 13:18
 * @Version V1.0
 */
public class Producer implements Runnable {
    private Goods goods;

    public Producer(Goods goods) {
        this.goods = goods;
    }

    public Producer() {
    }

    @Override
    public void run() {
        for (int i = 1; i <=10; i++) {

            if (i % 2 == 0) {
                this.goods.set("娃哈哈", "矿泉水");
            } else {
                this.goods.set("旺仔", "小馒头");
            }

        }
    }
}

消费者代码

package com.java0526.pc.c_002;

/**
 * @program: untitled
 * @ClassName Consumer
 * @Description
 * @Author zcm
 * @Date 2020/5/26 13:18
 * @Version V1.0
 */
public class Consumer implements Runnable {

    private Goods goods;

    public Consumer(Goods goods) {
        this.goods = goods;
    }

    public Consumer() {
    }


    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            goods.get();
        }
    }
}

测试代码

package com.java0526.pc.c_002;

/**
 * @program: untitled
 * @ClassName Test
 * @Description
 * @Author zcm
 * @Date 2020/5/26 13:25
 * @Version V1.0
 */
public class Test {
    public static void main(String[] args) {
        Goods goods=new Goods();
        Producer producer=new Producer(goods);
        Consumer consumer=new Consumer(goods);
        new Thread(producer).start();
        new Thread(consumer).start();
    }
}

执行结果:

Java提供3方法解决线程之间通信问题
 final void wait() 表示线程一直等待,直到其它线程通知
final void wait(long timeout) 线程等待指定毫秒参数的时间
final void wait(long timeout,int nanos) 线程等待指定毫秒、微妙的时间
final void notify() 唤醒一个处于等待状态的线程
final void notifyAll() 唤醒同一个对象上所有调用wait()方法的线程, 优先级别高的线程优先运行 
使用以上方法需注意:
  只能在同步方法或者同步代码块中使用,非同步方法使用会抛出异常IllegalMonitorStateException
Object唤醒与等待过程

实例三:

共享对象代码:

package com.java0526.pc.c_003;

/**
 * @program: untitled
 * @ClassName Goods
 * @Description
 * @Author zcm
 * @Date 2020/5/26 12:58
 * @Version V1.0
 */

public class Goods {
    private String brand;

    private String name;

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    /**
     * @Description: 默认是不存在商品的,如果值等于true的话,代表有商品
     * @Author: zcm
     * @return: Param:
     * @Date:2020/5/26 13:40
     */
    private Boolean flag = false;

    /**
     * @Description: 生产者开始生产
     * @Author: zcm
     * @return: Param:
     * @Date:2020/5/26 13:03
     */
    public synchronized void set(String brand, String name) {
        if (flag) {
            try {
//消费者还没消费,那么让生产者阻塞
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.setBrand(brand);
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.setName(name);
        System.out.println("生产者开始生产产品:" + this.getBrand() + "---" + this.getName());
        flag = true;//生产完毕,等待消费者消费
        notify();//唤醒消费者去消费商品
    }

    /**
     * @Description: 消费者开始消费产品
     * @Author: zcm
     * @return: Param:
     * @Date:2020/5/26 13:03
     */
    public synchronized void get() {
        //生产者还没有生产商品,不能去消费
        if (!flag) {
            try {
                //生产者还没生产,让消费者阻塞
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("消费者开始消费产品:" + this.getBrand() + "---" + this.getName());

        //消费完毕,设为false。提醒生产者该去生产商品了
        flag = false;
        notify();//唤醒生产者去生产

    }

}

生产者代码:

package com.java0526.pc.c_003;

/**
 * @program: untitled
 * @ClassName Producer
 * @Description
 * @Author zcm
 * @Date 2020/5/26 13:18
 * @Version V1.0
 */
public class Producer implements Runnable {
    private Goods goods;

    public Producer(Goods goods) {
        this.goods = goods;
    }


    @Override
    public void run() {
        for (int i = 1; i <=10; i++) {
            if (i % 2 == 0) {
                this.goods.set("娃哈哈", "矿泉水");
            } else {
                this.goods.set("旺仔", "小馒头");
            }

        }
    }
}

消费者代码:

package com.java0526.pc.c_003;

/**
 * @program: untitled
 * @ClassName Consumer
 * @Description
 * @Author zcm
 * @Date 2020/5/26 13:18
 * @Version V1.0
 */
public class Consumer implements Runnable {

    private Goods goods;

    public Consumer(Goods goods) {
        this.goods = goods;
    }

    public Consumer() {
    }
    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            goods.get();
        }
    }
}

执行结果:

以上只是针对简单的消费者和生产者。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章