2. Lock锁,生产者消费者问题
公平锁:十分公平,先来后到
非公平锁:可以插队(默认)
Synchronized
public class SaleTicketDemo01 { //并发,多线程操作同一个资源类,把资源类丢入线程就行 //lambda表达式 (参数)->{代码} public static void main(String[] args) { final Ticket ticket = new Ticket(); new Thread(() -> { for (int i = 1; i < 40; i++) { ticket.sale(); } }, "A").start(); new Thread(() -> { for (int i = 1; i < 40; i++) { ticket.sale(); } }, "B").start(); new Thread(() -> { for (int i = 1; i < 40; i++) { ticket.sale(); } }, "C").start(); } } //资源类 属性+方法 class Ticket { private int number = 50; //卖票 (传统方式解决并发问题synchronized)本质是排队+锁 public synchronized void sale() { if (number > 0) { System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票,剩余" + number); } } }
lock锁
//在真正的多线程开发中,线程就是一个资源类没有任何附属关系 public class SaleTicketDemo02 { //并发,多线程操作同一个资源类,把资源类丢入线程就行 //lambda表达式 (参数)->{代码} public static void main(String[] args) { final Ticket ticket = new Ticket(); new Thread(() -> { for (int i = 1; i < 40; i++) { ticket.sale(); } }, "A").start(); new Thread(() -> { for (int i = 1; i < 40; i++) { ticket.sale(); } }, "B").start(); new Thread(() -> { for (int i = 1; i < 40; i++) { ticket.sale(); } }, "C").start(); } } //资源类 属性+方法 class Ticket2 { private int number = 50; Lock lock=new ReentrantLock();//可重入锁 通过true、false来改变成公平、非公平 //卖票 public void sale() { lock.lock();//上锁 try{ //业务代码 if (number > 0) { System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票,剩余" + number); } }catch (Exception e){ e.printStackTrace(); }finally { lock.unlock();//解锁 } } }
生产者消费者问题:两个线程实现0 1 0 1数字改变
public class A { public static void main(String[] args) { Data data=new Data(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"A").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"A").start(); } } //资源类 class Data{ private int num=0; //+1 public synchronized void increment() throws InterruptedException { if(num!=0){ this.wait(); } num++; System.out.println(Thread.currentThread().getName()+"=>"+num); this.notifyAll();//通知其他线程我已经完毕了 } //+1 public synchronized void decrement() throws InterruptedException { if(num==0){ this.wait(); } num--; System.out.println(Thread.currentThread().getName()+"=>"+num); this.notifyAll();//通知其他线程我已经完毕了 } }
问题存在,如果是四个线程还安全吗(不安全)
if 改为wihle判断就可以解决四条线程的该问题
就是用if判断的话,唤醒后线程会从wait之后的代码开始运行,但是不会重新判断if条件,直接继续运行if代码块之后的代码,而如果使用while的话,也会从wait之后的代码运行,但是唤醒后会重新判断循环条件,如果不成立再执行while代码块之后的代码块,成立的话继续wait。
JUC版本的生产者,消费者问题
public class B { public static void main(String[] args) { Data2 data=new Data2(); new Thread(()->{ for (int i = 0; i < 10; i++) { data.increment(); } },"A").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { data.decrement(); } },"B").start(); } } //资源类 class Data2{ private int num=0; Lock lock=new ReentrantLock(); Condition condition = lock.newCondition();//取代了对象监视器的方法 //+1 public void increment() { lock.lock(); try{ while(num!=0){ condition.await();//等待 } num++; System.out.println(Thread.currentThread().getName()+"=>"+num); condition.signalAll();//唤醒全部 }catch (Exception e){ e.printStackTrace(); }finally { lock.unlock(); } } //+1 public void decrement() { lock.lock(); try { while(num==0){ condition.await(); } num--; System.out.println(Thread.currentThread().getName()+"=>"+num); condition.signalAll(); } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } } }
Condition可以精准的通知和唤醒线程
public class C { public static void main(String[] args) { Data3 data = new Data3(); new Thread(() -> { for (int i = 0; i < 5; i++) { data.printA(); } }, "A").start(); new Thread(() -> { for (int i = 0; i < 5; i++) { data.printB(); } }, "B").start(); new Thread(() -> { for (int i = 0; i < 5; i++) { data.printC(); } }, "C").start(); } } //资源类 class Data3 { Lock lock = new ReentrantLock(); Condition condition1 = lock.newCondition();//取代了对象监视器的方法 Condition condition2 = lock.newCondition();//取代了对象监视器的方法 Condition condition3 = lock.newCondition();//取代了对象监视器的方法 private int number = 1; //1,2,3->A,B,C public void printA() { lock.lock();//锁必须上在外面 try { while (number != 1) { condition1.await(); } System.out.println(Thread.currentThread().getName() + "AAAA"); number = 2; condition2.signal();//1 执行完了去唤醒2 } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void printB() { lock.lock();//锁必须上在外面 try { while (number != 2) { condition2.await(); } System.out.println(Thread.currentThread().getName() + "BBBB"); number = 3; condition3.signal();//2 执行完了去唤醒3 } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void printC() { lock.lock();//锁必须上在外面 try { while (number != 3) { condition3.await(); } System.out.println(Thread.currentThread().getName() + "CCCC"); number = 1; condition1.signal();//去唤醒1 } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }