Java基础知识十六:同步代码块、同步方法、线程同步、LOCK锁、生产者与消费者

7、卖票案例:同步代码块

1、锁多条语句操作共享数据,可以使用同步代码块实现

格式:  synchronized(任意对象){

多条语句操作共享数据的代码

}

2、synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁

优点:解决多线程的数据安全问题

缺点:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率

/*卖票案例
* 需求:某电影院有100张票,三个窗口卖票,设计一个程序模拟该电影院卖票
* 思路:
* 1.定义一个sellTicket类实现Runnable接口,里面定义一个成员变量:private int ticket = 100;
* 2.在sellTicket类中重写run()实现卖票,代码步骤如下:
*   A.判定票数大于0就卖票,并告知是哪个窗口卖的
*   B.卖了票之后,总票数要减1
*   C.票卖完了,也有可能有人来问余票,所以这里用死循环让卖票的动作一直执行
* 3.定义一个测试类sellTicketDemo,里面有main方法,代码步骤如下:
*   A.创建sellTicket类的对象
*   B.创建三个Thread类对象,把sellTicket对象作为构造方法的参数,并给出对应的窗口名称
*   C.启动线程
* */
public class SellTicketDemo {
   public static void main(String[] args) {
       sellTicket st = new sellTicket();
       //创建三个对象
       Thread t1 = new Thread(st,"NO1");
       Thread t2 = new Thread(st,"NO2");
       Thread t3 = new Thread(st,"NO3");
       //启动线程
       t1.start();
       t2.start();
       t3.start();
   }
}

ublic class sellTicket implements Runnable {
   private int ticket = 100;
   private Object obj = new Object(); //定义在外面就是三个对象使用同一把锁
   @Override
   public void run() {
       while(true) {
           //使用同步锁保证三个窗口不会出售同一张票
           synchronized (obj) {
               if (ticket > 0) {
                   //模拟出票时间
                   try {
                       Thread.sleep(100);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
                   System.out.println(Thread.currentThread().getName() + "正在出售第" + ticket + "张票");
                   ticket--;
               }
           }
       }
   }
}

8、同步方法

1、格式:修饰符 synchronized 返回值类型 方法名(方法参数){ }

2、同步方法的锁对象是:this

3、同步静态方法格式:修饰符 static synchronized 返回值类型 方法名(方法参数){ }

4、同步静态方法的锁对象是:. 类名.class

/*卖票案例
* 需求:某电影院有100张票,三个窗口卖票,设计一个程序模拟该电影院卖票
* 思路:
* 1.定义一个sellTicket类实现Runnable接口,里面定义一个成员变量:private int ticket = 100;
* 2.在sellTicket类中重写run()实现卖票,代码步骤如下:
*   A.判定票数大于0就卖票,并告知是哪个窗口卖的
*   B.卖了票之后,总票数要减1
*   C.票卖完了,也有可能有人来问余票,所以这里用死循环让卖票的动作一直执行
* 3.定义一个测试类sellTicketDemo,里面有main方法,代码步骤如下:
*   A.创建sellTicket类的对象
*   B.创建三个Thread类对象,把sellTicket对象作为构造方法的参数,并给出对应的窗口名称
*   C.启动线程
* */
public class SellTicketDemo {
   public static void main(String[] args) {
       sellTicket st = new sellTicket();
       //创建三个对象
       Thread t1 = new Thread(st,"NO1");
       Thread t2 = new Thread(st,"NO2");
       Thread t3 = new Thread(st,"NO3");
       //启动线程
       t1.start();
       t2.start();
       t3.start();
   }
}

public class sellTicket implements Runnable {
   private static int ticket = 100;
   private Object obj = new Object(); //定义在外面就是三个对象使用同一把锁
   private int x = 0;
   @Override
   public void run() {
       while(true) {
           if(x%2 == 0) {
               //使用同步锁保证三个窗口不会出售同一张票
               synchronized (sellTicket.class) {
                   if (ticket > 0) {
                       //模拟出票时间
                       try {
                           Thread.sleep(100);
                       } catch (InterruptedException e) {
                           e.printStackTrace();
                       }
                       System.out.println(Thread.currentThread().getName() + "正在出售第" + ticket + "张票");
                       ticket--;
                   }
               }
           }else{
               sellTicket();
           }
           x++;
       }
   }
   private static synchronized void sellTicket() {
       if (ticket > 0) {
           //模拟出票时间
           try {
               Thread.sleep(100);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           System.out.println(Thread.currentThread().getName() + "正在出售第" + ticket + "张票");
           ticket--;
       }
   }

}

9、 线程同步

1、 StringBuffer(线程安全):被StringBuilder(线程不安全)替代

2、 Vector(线程安全):被ArrayList(线程不安全)替代

3、 Hashtable(线程安全):被HashMap(线程不安全)替代

public class ThreadDemo1 {
   public static void main(String[] args) {
       //线程安全
       StringBuffer sb1 = new StringBuffer();
       //线程不安全
       StringBuilder sb2 = new StringBuilder();
       //线程安全
       Vector<String> v = new Vector<String>();
       //线程不安全
       ArrayList<String> arr = new ArrayList<String>();
       //线程安全
       Hashtable<String,String> ht = new Hashtable<String,String>();
       //线程不安全
       HashMap<String,String> hm = new HashMap<String,String>();

       //调用synchronizedList()这个方法就把集合包装成了一个线程安全的集合类
       List<String> list = Collections.synchronizedList(new ArrayList<String>());
   }
}

10、 Lock锁

1、 获得锁:void lock();

2、 释放锁:void unlock();

注:Lock是接口不能直接实例化,采用它的实现类ReentranLock来实例化

ReentranLock无参构造方法:ReentranLock()创建一个ReentranLock实例

public class sellTicket implements Runnable{
   private int tickets = 100;
   private Lock lock = new ReentrantLock();
   @Override
   public void run() {
       while(true){
           //一般情况会把加锁、解锁的动作放在try{...}finally{...}代码块中
          try{
               lock.lock(); //加锁
               if(tickets>0){
                   try {
                       Thread.sleep(100);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
                   System.out.println(Thread.currentThread().getName()+"正在出售第"+tickets+"张票");
                   tickets--;
               }
          }finally {
              lock.unlock(); //解锁
          }
       }
   }
}

public class SellTicketDemo {
   public static void main(String[] args) {
       sellTicket st = new sellTicket();
       Thread t1 = new Thread(st,"窗口1");
       Thread t2 = new Thread(st,"窗口2");
       Thread t3 = new Thread(st,"窗口3");
       t1.start();
       t2.start();
       t3.start();
   }
}

11、生产者消费者

4250ff111bdd45e9875401947c964971.png 

Object类的等待和唤醒方法:

1. Void wait(): 导致当前线程等待,直到另一个线程调用该对象的notify()方法或者notifyAll()方法

2. Void notify(): 唤醒正在等待对象监视器的单个线程

3. Void notifyAll(): 唤醒正在等待对象监视器的所有线程

/*生产者、消费者案例
1.奶箱类(Box):定义一个成员变量,表示第X瓶奶,提供存储牛奶和获取牛奶的操作
2.生产者类(Producer):实现Runnable接口,重写run()方法,调用存储牛奶的操作
3.消费者类(Customer):实现Runnable接口,重写run()方法,调用获取牛奶的操作
4.测试类(BoxDemo):里面有main方法,main方法中的代码步骤如下:
       A.创建奶箱对象,这是共享数据区域
       B.创建生产者对象,把奶箱对箱作为构造方法的参数传递,因为在这个类中要调用存储牛奶的操作
       C.创建消费者对象,把奶箱对箱作为构造方法的参数传递,因为在这个类中要调用获取牛奶的操作
       D.创建2个线程对象,分别把生产者对象和消费者对象作为构造方法参数传递
       E.启动线程
* */
public class BoxDemo {
   public static void main(String[] args) {
           //创建奶箱对象,这是共享数据区域
           Box b = new Box();
           //创建生产者对象,把奶箱对箱作为构造方法的参数传递,因为在这个类中要调用存储牛奶的操作
           Producer p = new Producer(b);
           //创建消费者对象,把奶箱对箱作为构造方法的参数传递,因为在这个类中要调用获取牛奶的操作
          Customer c = new Customer(b);
          //创建2个线程对象,分别把生产者对象和消费者对象作为构造方法参数传递
           Thread t1 = new Thread(p);
           Thread t2 = new Thread(c);
           //启动线程
           t1.start();
           t2.start();
   }
}

//奶箱类
public class Box {
   //定义一个成员变量,表示第X瓶奶
   private int milk;
   //定义一个成员变量,表示奶箱的状态
   private boolean state = false;
   //存储牛奶的方法
   public synchronized void put(int milk){
       //如果有牛奶,等待消费,而不是生产
       if (state){
           try {
               wait();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
       //如果没有牛奶,就生产牛奶
       this.milk = milk;
       System.out.println("送奶工将第"+this.milk+"瓶奶放入奶箱");
       //生产完毕之后,修改奶箱状态
       state = true;
       //唤醒其它等待的线程
       notifyAll();
   }
   //获取牛奶的方法
   public synchronized void get(){
       //如果没有牛奶,就要等待生产
       if(!state){
           try {
               wait();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
       //如果有牛奶,就消费牛奶
       System.out.println("用户拿到第"+this.milk+"瓶奶");
       //消费完毕之后,修改奶箱状态
       state = false;
       notifyAll();
   }
}

//生产者类
public class Producer  implements Runnable{
   private Box b;
   public Producer(Box b) {
       this.b = b;
   }
   @Override
   public void run() {
       //生产了5瓶
       for (int i = 0; i <=5 ; i++) {
           b.put(i);
       }
   }
}

//消费者类
public class Customer implements Runnable {
   private Box b;
   public Customer(Box b) {
       this.b = b;
   }
   @Override
   public void run() {
       while(true){
           b.get();
       }
   }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

蜀州凯哥

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值