多线程学习2(线程同步,死锁,线程通信)
一.线程同步机制
1.三大不安全案例
1.买票,多个线程共同使用票数
2.银行,两个人取同一个账户的钱
3.不能保证线程同步的集合
2.synchronized的同步方法及方法块
1.买票
package syn; //不安全的买票 public class UnsafeBuyTicket { public static void main(String[] args) { BuyTicket buyTicket = new BuyTicket(); new Thread(buyTicket,"me").start(); new Thread(buyTicket,"you").start(); new Thread(buyTicket,"other").start(); } } class BuyTicket implements Runnable{ int ticketNums = 100; boolean flag = true; @Override public void run() { //买票 while(flag){ //模拟延时 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } buy(); } } //synchronized同步方法,锁的是this private synchronized void buy(){ //判断是否有票 if(ticketNums<=0){ flag = false; return ; } System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--); } }
2.银行,不能直接锁run方法,因为run方法的this是银行,而不是账户,锁了银行,两个人仍可以从不同的银行,取同一个账户的钱,导致并发问题,可以锁方法块来解决
package syn; public class UnSafeBank { public static void main(String[] args) { Account account = new Account(100, "MONEY"); Drawing you = new Drawing(account,50,"你"); Drawing me = new Drawing(account,100,"我"); you.start(); me.start(); } } //账户 class Account{ int money;//余额 String name;//卡名 public Account(int money, String name) { this.money = money; this.name = name; } } //银行 class Drawing extends Thread{ Account account; int drawingMoney; //取了多少钱 int nowMoney; //手里有多少钱 public Drawing(Account account,int drawingMoney, String name){ super(name); this.account = account; this.drawingMoney = drawingMoney; } @Override public void run(){ synchronized (account){ if(account.money-drawingMoney<0){ System.out.println(Thread.currentThread().getName()+"钱不够"); return ; } //sleep可以放大问题的发生性 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } account.money = account.money - drawingMoney; nowMoney = nowMoney + drawingMoney; System.out.println(account.name+"余额为"+account.money); System.out.println(this.getName()+"手里的钱"+nowMoney); } } }
juc安全类型的集合
package syn; import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput; import java.util.concurrent.CopyOnWriteArrayList; //测试JUC安全类型的集合 public class TestJUC { public static void main(String[] args) { CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>(); for (int i = 0; i < 100000; i++) { new Thread(() -> { list.add(Thread.currentThread().getName()); }).start(); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size()); } }
死锁
条件:互斥,不可抢占,循环等待,请求和保持
package DeadLock; //死锁:多个线程互相只有对方需要的资源,形成僵持 public class DeadLock { public static void main(String[] args) { Makeup g1 = new Makeup(0,"xiao"); Makeup g2 = new Makeup(1,"da"); g1.start(); g2.start(); } } class Lipstick{ } class Mirror{ } class Makeup extends Thread{ //需要的资源只有一份,用static来保证互斥条件 static Lipstick lipstick =new Lipstick(); static Mirror mirror =new Mirror(); int choice; //选择 String girlName; //使用化妆品的人 Makeup(int choice , String girlName){ this.choice = choice; this.girlName = girlName; } @Override public void run(){ //化妆 try { makeup(); } catch (InterruptedException e) { e.printStackTrace(); } } //化妆,互相持有对方的锁,需要拿到对方的资源 //满足了请求和保持,循环等待,不可抢占 //想要解决死锁,只需要在请求新资源的时候吧旧的资源的锁释放掉就可一了 private void makeup() throws InterruptedException { if(choice==0){ synchronized (lipstick){ //获得口红的锁 System.out.println(this.girlName+"获得口红的锁"); Thread.sleep(1000); //一秒钟后想获得镜子 synchronized (mirror){ System.out.println(this.girlName+"获得镜子的锁"); } } } else{ synchronized (mirror){ System.out.println(this.girlName+"获得镜子的锁"); Thread.sleep(2000); synchronized (lipstick){ //获得口红的锁 System.out.println(this.girlName+"获得口红的锁"); } } } } }
lock锁
不能同步方法,juc
与syn对比
1.syn是隐式的,自动释放,lock是显式的
2.lock只有代码块锁
3.lock性能好
package advanced; import java.util.concurrent.locks.ReentrantLock; public class TestLock { public static void main(String[] args) { TestLock2 testLock2 = new TestLock2(); new Thread(testLock2).start(); new Thread(testLock2).start(); new Thread(testLock2).start(); } } class TestLock2 implements Runnable{ int ticketNums = 10; //定义lock锁 private final ReentrantLock lock = new ReentrantLock(); @Override public void run() { while(true){ try{ lock.lock(); if(ticketNums>0){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(ticketNums--); }else{ break; } }finally{ lock.unlock(); } } } }
生产者消费者问题
wait释放锁,常用来线程通信
noitfyALL唤醒所有线程
生产者消费者问题中用while而不用if,排除假唤醒
###管程法
package advanced; //测试生产者消费者模型->利用缓冲区解决:管程法 //生产者,消费者,缓冲区 public class TestPC { public static void main(String[] args) { SynContainer container = new SynContainer(); new Productor(container).start(); new Consumer(container).start(); } } class Productor extends Thread{ SynContainer container; public Productor(SynContainer container){ this.container = container; } //生产 @Override public void run(){ for (int i = 0; i < 100; i++) { container.push(new Chicken(i)); } } } class Consumer extends Thread{ SynContainer container; public Consumer(SynContainer container){ this.container = container; } @Override public void run(){ for (int i = 0; i < 100; i++) { container.pop(); } } } class Chicken{ int id; public Chicken(int id) { this.id = id; } } //缓冲区 class SynContainer{ //容器大小 Chicken[] chickens = new Chicken[10]; //计数器 int count = 0; //生产者丢入产品 public synchronized void push(Chicken chiken){ //如果容器慢了就需要等待消费者消费 while(count == 10){ //通知消费者消费,生产者等待 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //如果没有满就需要丢入产品 chickens[count] = chiken; count++; System.out.println("生产了"+chiken.id+"只鸡"); //可以通知消费者消费了 this.notifyAll(); } //消费者消费产品 public synchronized Chicken pop(){ while(count == 0){ //等待生产者生产,消费者等待 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //如果可以消费 count--; Chicken chicken = chickens[count]; System.out.println("消费了"+chicken.id+"只鸡"); this.notifyAll(); return chicken; } }
信号灯法
package advanced; //测试生产者消费者问题2:信号灯法 public class TestPC2 { public static void main(String[] args) { TV tv = new TV(); new Player(tv).start(); new Watcher(tv).start(); } } //生产者--演员 class Player extends Thread{ TV tv; public Player(TV tv){ this.tv = tv; } @Override public void run(){ for (int i = 0; i < 20; i++) { if(i%2 == 0){ this.tv.play("haha"); }else{ this.tv.play("hehe"); } } } } //消费者--观众 class Watcher extends Thread{ TV tv; public Watcher(TV tv){ this.tv = tv; } @Override public void run(){ for (int i = 0; i < 20; i++) { tv.watch(); } } } //产品 class TV{ //演员表演,观众等待 //观众观看,演员等待 String voice; //表演的节目 boolean flag = true; //表演 public synchronized void play(String voice){ while(flag==false){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("演员表演了:"+voice); this.notifyAll(); this.voice = voice; this.flag = !this.flag; } //观看 public synchronized void watch(){ while(flag){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("观看了:"+voice); //通知演员表演 this.notifyAll(); this.flag = !this.flag; } }
线程池
package advanced; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class TestPool { public static void main(String[] args) { //1.创建服务,创建线程池,参数为线程池大小 ExecutorService ser = Executors.newFixedThreadPool(10); ser.execute(new MyThread()); ser.execute(new MyThread()); ser.execute(new MyThread()); ser.shutdown(); } } class MyThread implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()); } }