java基础之多线程(2)

一.多线程的安全问题

1.线程同步(加锁)这里针对上一节的卖票程序做改进,就是线程的同步

		多线程并发访问的时候回出现数据安全问题:
 *          解决方式:
 *              1、同步代码块
 *                  synchronized(共享资源、共享对象,需要是object的子类){具体执行的代码块}
 *              2、同步方法
 *                  将核心的代码逻辑定义成一个方法,使用synchronized关键字进行修饰,此时不需要指定共享对象

同步代码块:

*
 * 使用接口的方式,每次只创建了一个共享对象,所有的线程能够实现资源共享
 *      1、数据不一致的问题
 *          解决方法:线程同步(同步代码块)
 *
 */
public class TicketRunnable2 implements Runnable {

    private int ticket = 5;

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (this){
                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + "正在出售第" + (ticket--) + "张票");
                }
            }
        }
    }

    public static void main(String[] args) {
        TicketRunnable2 ticket = new TicketRunnable2();
        Thread t1 = new Thread(ticket,"A");
        Thread t2 = new Thread(ticket,"B");
        Thread t3 = new Thread(ticket,"C");
        Thread t4 = new Thread(ticket,"D");

        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }

同步方法:


**
 * 使用接口的方式,每次只创建了一个共享对象,所有的线程能够实现资源共享
 * 1、数据不一致的问题
 * 解决方法:线程同步
 */
public class TicketRunnable3 implements Runnable {

    private int ticket = 5;

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.sale();
        }
    }

    /*
     * 使用同步方法解决多线程数据安全的问题
     * */
    public synchronized void sale() {

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

    public static void main(String[] args) {
        TicketRunnable3 ticket = new TicketRunnable3();
        Thread t1 = new Thread(ticket, "A");
        Thread t2 = new Thread(ticket, "B");
        Thread t3 = new Thread(ticket, "C");
        Thread t4 = new Thread(ticket, "D");

        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }

2.线程同步小结:

 同步监视器
    synchronized(obj){}中的obj称为同步监视器
    同步代码块中同步监视器可以是任何对象,但是推荐使用共享资源作为同步监 视器
    同步方法中无需指定同步监视器,因为同步方法的监视器是this,也就是该对象 本身
   
 同步监视器的执行过程 
 第一个线程访问,锁定同步监视器,执行其中代码 
 第二个线程访问,发现同步监视器被锁定,无法访问 
 第一个线程访问完毕,解锁同步监视器 
 第二个线程访问,发现同步监视器未锁,锁定并访问 

二。生产者和消费者

生产者:

package com.mashibing.Thread.pc3;

public class Producer implements Runnable {

    private Goods goods;

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

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

消费者:

package com.mashibing.Thread.pc3;

public class Comsumer implements Runnable {

    private Goods goods;

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

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

共享对象:

package com.mashibing.Thread.pc3;

public class Goods {
    private String brand;
    private String name;

    //默认是不存在商品的,如果值等于true的话,代表有商品
    private boolean flag = false;



    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;
    }

    //生产者生产商品
    public synchronized void set(String brand,String name){
        //当生产者抢占到cpu资源之后会判断当前对象是否有值,如果有的话,以为着消费者还没有消费,需要提醒消费者消费,同时
        //当前线程进入阻塞状态,等待消费者取走商品之后,再次生产,如果没有的话,不需要等待,不需要进入阻塞状态,直接生产即可
        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
        flag = true;
        //唤醒消费者去进行消费
        notify();
    }



    //消费者获取商品
    public synchronized void get(){
        /*
         * 如果flag等于false的话,意味着生产者没有生产商品,此时消费者无法消费,需要让消费者线程进入到阻塞状态,等待生产者生产,当
         * 有商品之后,再开始消费
         * */
        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());
        flag = false;
        //唤醒生产者去进行生产
        notify();

    }
}

测试类:

package com.mashibing.Thread.pc3;

public class Test {
    public static void main(String[] args) {
        Goods goods = new Goods();
        Producer producer = new Producer(goods);
        Comsumer comsumer = new Comsumer(goods);

        Thread thread = new Thread(producer);
        Thread thread1 = new Thread(comsumer);

        thread.start();
        thread1.start();
    }
}

具体的版本更替从pc1-pc4详细讲本机代码;

三。用juc实现的生产者和消费者案例

生产者:

package com.mashibing.Thread.pc4;

import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;

public class ProducerQueue implements Runnable {

    private BlockingQueue<Goods> blockingQueue;

    public ProducerQueue(BlockingDeque<Goods> blockingDeque) {
        this.blockingQueue = blockingDeque;
    }

    @Override
    public void run() {
        for (int i=0;i<10;i++){
            Goods goods = null;
            if (i%2==0){
                goods = new Goods("哇哈哈","矿泉水");
            }else {
                goods = new Goods("旺仔","小馒头");
            }
            System.out.println("生产者开始生产商品:"+goods.getBrand()+"--"+goods.getName());
            try {
                blockingQueue.put(goods);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

消费者:

package com.mashibing.Thread.pc4;

import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;

public class ConsumerQueue implements Runnable {

    private BlockingQueue<Goods> blockingQueue;

    public ConsumerQueue(BlockingDeque blockingDeque) {
        this.blockingQueue = blockingDeque;
    }

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

        }
    }
}

共享对象:

package com.mashibing.Thread.pc4;


public class Goods {

    private String brand;
    private String name;

    public Goods(String brand, String name) {
        this.brand = brand;
        this.name = 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;
    }

}

测试类:

package com.mashibing.Thread.pc4;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class Test {

    public static void main(String[] args) {
        BlockingQueue<Goods> queue1 = new ArrayBlockingQueue<Goods>(5);
        ProducerQueue producerQueue = new ProducerQueue(queue1);
        ConsumerQueue consumerQueue = new ConsumerQueue(queue1);
        new Thread(producerQueue).start();
        new Thread(consumerQueue).start();
}

你可能感兴趣的:(JavaSE,java)