百木园-与人分享,
就是让自己快乐。

线程通信和8锁问题

线程通信

1、场景:生产者和消费者问题

==仓库、生产者、消费者

====仓库只能存放一个产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走

====如果仓库中没有产品,生产者将产品放入仓库,否则停止生产并等待(阻塞),直到仓库中的产品被消费者取走

====如果仓库中有产品,消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止

生产者和消费者共享一个资源,并且生产者和消费者之间相互依赖,互为条件。

==生产者消费者问题中,仅有synchronized是不够的,

synchronized可以阻止并发更新同一个共享资源,实现了同步,但不能用来实现不同线程之间的消息传递(通信)

2、线程通信的操作方法

(Object类的方法,只能在同步方法或同步代码块中使用,否则会抛出异常llegalMonitorStateException)

(1)wait() :线程一直等待,直到其它线程通知,会释放锁

(2)wait(long timeout):指定等待的毫秒数

(3)notify():唤醒一个处于等待状态的线程

(4)notifyAll():唤醒同一个对象上所有调用wait()方法的线程,优先级高的线程先调度。

3、线程通信的方式:

(1)管程法(生产者 缓冲区 消费者)

并发协作模型:生产者消费者模式 管程法

==生产者:负责生产数据的模块(方法、对象、线程、进程)

==消费者:负责处理数据的模块(方法、对象、线程、进程)

==缓冲区:消费者不直接使用生产者的数据,生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据

//测试生产者消费者模式  :    管程法 (生产者 消费者  缓冲区)
public class TestPC {
   public static void main(String[] args) {
       SynContainer synContainer = new SynContainer();
       new Productor(synContainer).start();
       new Consumer(synContainer).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++) {
           Chicken chicken = new Chicken(i);
           container.push(chicken);
           System.out.println(\"productor product no.\"+i+\"chicken\");
      }
  }
}
//消费者
class Consumer extends Thread{
   SynContainer container;
   public Consumer(SynContainer container){
       this.container = container;
  }

   @Override
   public void run() {
       for (int i = 0; i < 100; i++) {
           System.out.println(\"no.\"+container.pop().num+\"chicken ConSume\");
      }
  }
}
//产品
class Chicken{
   int num;

   public Chicken(int num) {
       this.num = num;
  }
}
//缓冲区
class SynContainer{
   //容器大小
   Chicken[] chickens = new Chicken[10];
   //数量
   int count =0;

   //生产者生产 放入
   public synchronized void push(Chicken chicken){
       //如果容器满了
       if (count==chickens.length){
           //产品满了通知消费者消费
           try {
               this.wait();
          } catch (InterruptedException e) {
               e.printStackTrace();
          }
      }
       //如果没满 则生产 进入缓冲区
       chickens[count]=chicken;
       count++;

       //生产者每生产产品就可以 通知消费者消费
       this.notifyAll();
  }

   //消费者消费 取出
   public synchronized Chicken pop(){
       //如果容器为空
       if (count==0){
           //产品为空 通知生产者生产
           try {
               this.wait();
          } catch (InterruptedException e) {
               e.printStackTrace();
          }
      }
       //消费者消费
       count--;
       Chicken chicken=chickens[count];
       //消费者每次消费 则通知生产者生产
       this.notifyAll();

       return chicken;
  }
}

(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 < 10; i++) {
           if (i%2==0){
               this.tv.play(\"琅琊榜\");
          }else {
               this.tv.play(\"周生如故\");
          }
      }
  }
}
//消费者   观众
class Watcher extends Thread{
  TV tv;
  public Watcher(TV tv){
      this.tv= tv;
  }

   @Override
   public void run() {
       for (int i = 0; i < 20; i++) {
           this.tv.watch();
      }
  }
}
//产品   节目
class TV{
   //演员表演 观众等待 T
   //观众观看 演员等待 F
   String voice; //表演的节目
   boolean flag = true;//设置的标志位

   //表演
   public synchronized void play(String voice){
       //如果flag 不为true 代表观众还在观看
       if (!flag){
           try {
               this.wait(); //这个线程等待
          } catch (InterruptedException e) {
               e.printStackTrace();
          }
      }
       // 为true 则可以开始表演
       System.out.println(\"表演了\"+voice);
       //通知观众观看
       this.notifyAll();//通知唤醒所有等待的线程
       this.voice = voice;
       this.flag=!this.flag; //改变标志位 代表表演结束 等观众观看
  }

   public synchronized void watch(){
       //如果flag 为true 则代表演员还在表演
       if (flag){
           try {
               this.wait(); //这个线程等待
          } catch (InterruptedException e) {
               e.printStackTrace();
          }
      }
       System.out.println(\"观众观看了\"+voice);
       //通知演员表演
       this.notifyAll();//通知唤醒
       this.flag = !this.flag;//改变标志位 代表观看结束 等演员表演
  }

}

(3)线程池

 

 

 

 

public class TestPool {
   public static void main(String[] args) {
       MyThread myThread = new MyThread();
       //1、创建服务 创建线程池
       // newFixedThreadPool 线程池的大小为 10
       ExecutorService executorService = Executors.newFixedThreadPool(10);
       ExecutorService service = Executors.newFixedThreadPool(4);

       //2、执行 放入线程池中
       executorService.execute(myThread);
       executorService.execute(myThread);
       executorService.execute(myThread);
       service.execute(myThread);
       service.execute(myThread);
       service.execute(myThread);

       //3、关闭服务 关闭线程池连接
       executorService.shutdown();
       service.shutdown();
  }
}

class MyThread implements Runnable{
   @Override
   public void run() {
       for (int i = 0; i < 2; i++) {
           System.out.println(Thread.currentThread().getName()+i);
      }


  }
}

 

8锁问题

1、标准情况下两个线程哪个先打印

(锁的是同一对象,先拿到锁先执行)

public class aTest {
   public static void main(String[] args) {
       // 锁的是同一对象 phone
       // send 先拿到锁先执行   call 后拿到锁后执行
       Phone phone = new Phone();
       new Thread(()->{
           phone.send();
      }).start();

       try {
           TimeUnit.SECONDS.sleep(1);//休眠1秒
      } catch (InterruptedException e) {
           e.printStackTrace();
      }

       new Thread(()->{
           phone.call();
      }).start();
  }
}
//synchronized 锁的是同一对象 Phone
class Phone{
   public synchronized void send(){
       System.out.println(\"发短信\");
  }

   public synchronized void call(){
       System.out.println(\"打电话\");
  }
}

2、有一个线程延迟,另一个线程正常,哪个先打印

(锁的还是同一对象,先拿到锁先执行(虽然有send延迟,但是同一对象内的,还是先执行))

//先send 再call
public class bTest {
   public static void main(String[] args) throws InterruptedException {
       Phone2 phone = new Phone2();
       new Thread(()->{phone.send();}).start();
       TimeUnit.SECONDS.sleep(1);
       new Thread(()->{phone.call();}).start();
  }
}
class Phone2{
   public synchronized void send(){
       try {
           TimeUnit.SECONDS.sleep(4);
      } catch (InterruptedException e) {
           e.printStackTrace();
      }
       System.out.println(\"发短信\");
  }
   public synchronized void call(){
       System.out.println(\"打电话\");
  }
}

两个线程是同一对象调用方法,方法都有synchronized修饰,则锁的是这个对象(相当于锁粗化了) 这个对象的锁是同一把锁 调用的方法即线程 谁先拿到锁谁先执行(相当于 一次只能有一个线程调用该方法)

3、在两个同步方法情况下,增加一个普通方法,调用同步方法和普通方法,哪个先执行?(普通方法先执行,因为不受锁的影响)

//先hello再send、call
public class cTest {
   public static void main(String[] args) throws InterruptedException {
       Phone3 phone = new Phone3();

       new Thread(()->{phone.send();}).start();
       TimeUnit.SECONDS.sleep(1);
       new Thread(()->{phone.call();}).start();
       new Thread(()->{phone.hello();}).start();
  }
}
class Phone3{
   public synchronized void send(){
       try {
           TimeUnit.SECONDS.sleep(4);
      } catch (InterruptedException e) {
           e.printStackTrace();
      }
       System.out.println(\"发短信\");
  }
   public synchronized void call(){
       System.out.println(\"打电话\");
  }
   public void hello(){
       System.out.println(\"hello\");
  }
}

4、创建两个对象,分别调用同步方法,哪个先执行?

(创建两个对象,synchronized锁的是方法的调用者,即这个两个对象都有锁,共两把锁,没有线程休眠的先执行)

//先call再send(有休眠)
public class dTest {
   public static void main(String[] args) throws InterruptedException {
       Phone4 phone1 = new Phone4();//对象1
       Phone4 phone2 = new Phone4();//对象2
       new Thread(()->{phone1.send(); }).start();
       TimeUnit.SECONDS.sleep(1);
//       new Thread(()->{phone2.hello();}).start();
       new Thread(()->{phone2.call();}).start();
//       new Thread(()->{phone1.hello();}).start();
  }

}
class Phone4{
   public synchronized void send(){
       try {
           TimeUnit.SECONDS.sleep(4);
      } catch (InterruptedException e) {
           e.printStackTrace();
      }
       System.out.println(\"发短信\");
  }
   public synchronized void call(){
       System.out.println(\"打电话\");
  }
   //普通方法 没加锁 非同步方法
   public void hello(){
       System.out.println(\"hello\");
  }
}

5、都是静态同步方法static synchronized修饰,哪个先执行?

(按类的方法的顺序来执行)

(static修饰的方法,在类加载时就存在了,锁的是类class,在对象调用时,进入的是类的锁)

//先send再call
public class eTest {
   public static void main(String[] args) throws InterruptedException {
       Phone5 phone = new Phone5();
       new Thread(()->{phone.send();}).start();
       TimeUnit.SECONDS.sleep(1);
       new Thread(()->{phone.call();}).start();
  }
}
class Phone5{
   /* static 修饰的方法 在类加载的时候就创建了就有了
      也就是 静态按顺序(模板)固定了   锁的是类 Class
        在对象调用时 就进入的是类的锁 Class的锁
   */
   public static synchronized void send(){
       try {
           TimeUnit.SECONDS.sleep(4);
      } catch (InterruptedException e) {
           e.printStackTrace();
      }
       System.out.println(\"发短信\");
  }
   public static synchronized void call(){
       System.out.println(\"打电话\");
  }
}

6、两个对象分别调用两个静态同步方法,哪个先执行?

(按class类中的方法调用顺序执行)

(因为两个对象是由同一个类创建的,这个class的方法是静态同步方法,锁的是这个class,所以无论从这个class中创建多少个对象,调用class静态同步方法都是按class中的方法的调用顺序的,同一把锁class,先调用的方法,先得到锁)

//先send 后 call
public class fTest {
   public static void main(String[] args) {
       Phone6 phone1 = new Phone6();
       Phone6 phone2 = new Phone6();
       new Thread(()->{phone1.send();}).start();
       new Thread(()->{phone2.call();}).start();
  }
}
class Phone6{
   public static synchronized void send(){
       try {
           TimeUnit.SECONDS.sleep(4);
      } catch (InterruptedException e) {
           e.printStackTrace();
      }
       System.out.println(\"发短信\");
  }
   public static synchronized void call(){
       System.out.println(\"打电话\");
  }
}

7、一个静态同步方法send和一个普通同步call方法,一个对象,哪个先执行?

(静态同步方法send 先执行)

(静态同步方法锁的是class类,普通同步方法锁的是对象)

//先call再send
public class gTest {
   public static void main(String[] args) throws InterruptedException {
       Phone7 phone = new Phone7();
       new Thread(()->{phone.send();}).start();
       TimeUnit.SECONDS.sleep(1);
       new Thread(()->{phone.call();}).start();
  }
}
class Phone7{
   public static synchronized void send(){
       /*如果有进行休眠,就会先休眠,对象锁先执行
       try {
           TimeUnit.SECONDS.sleep(4);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       */
       System.out.println(\"发短信\");
  }
   public synchronized void call(){
       System.out.println(\"打电话\");
  }
}

8、一个静态同步方法send,一个普通同步方法call,两个对象,哪个先执行?

(看执行顺序)

(静态同步方法锁的是class类,普通同步方法锁的是对象)

//先send再call
public class hTest {
   public static void main(String[] args) throws InterruptedException {
       Phone8 phone1 = new Phone8();
       Phone8 phone2 = new Phone8();
       new Thread(()->{phone1.send();}).start();
       TimeUnit.SECONDS.sleep(1);
       new Thread(()->{phone2.call();}).start();
  }
}
class Phone8{
   public static synchronized void send(){
       /*如果有进行休眠,就会先休眠,对象锁先执行
       try {
           TimeUnit.SECONDS.sleep(4);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       */
       System.out.println(\"发短信\");
  }
   public synchronized void call(){
       System.out.println(\"打电话\");
  }
}

 


来源:https://www.cnblogs.com/hexiayuliang666/p/16157444.html
本站部分图文来源于网络,如有侵权请联系删除。

未经允许不得转载:百木园 » 线程通信和8锁问题

相关推荐

  • 暂无文章