技术汇总:第二章:JUC

2022-09-28 14:57:46 浏览数 (1)

JUC是什么?

java.util.concurrent在并发编程中使用的工具类

进程/线程回顾

进程/线程是什么?

进程:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。 线程:通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程可以利用进程所拥有的资源,在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位,由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统多个程序间并发执行的程度。

进程/线程例子

 使用QQ,查看进程一定有一个QQ.exe的进程,我可以用qq和A文字聊天,和B视频聊天,给C传文件,给D发一段语言,QQ支持录入信息的搜索。 大四的时候写论文,用word写论文,同时用QQ音乐放音乐,同时用QQ聊天,多个进程。 word如没有保存,停电关机,再通电后打开word可以恢复之前未保存的文档,word也会检查你的拼写,两个线程:容灾备份,语法检查  

Lock接口

复习Synchronized

多线程编程模板上

1、线程 操作 资源类

2、高内聚低耦合

实现步骤

1、创建资源类

2、资源类里创建同步方法、同步代码块

Lock

lock是什么?

Lock implementations provide more extensive locking operations than can be obtained using synchronized methods and statements. They allow more flexible structuring, may have quite different properties, and may support multiple associated Condition objects. Lock实现提供更广泛的锁定操作可以比使用 synchronized获得方法和声明更好。他们允许更灵活的结构,可以有完全不同的特性,可以支持多个相关的 Condition对象。  

Lock接口的实现ReentrantLock可重入锁

如何使用?

代码语言:javascript复制
class X {
   private final ReentrantLock lock = new ReentrantLock();
   // ...
 
   public void m() {
     lock.lock();  // block until condition holds
     try {
       // ... method body
     } finally {
       lock.unlock()
     }
   }
 }

创建线程方式

继承Thread(不能这样写)

代码语言:javascript复制
public class SaleTicket extends Thread
 
java是单继承,资源宝贵,要用接口方式

new Thread()(不能这样写)

代码语言:javascript复制
Thread t1 = new Thread();
   t1.start();

第三钟:Thread(Runnable target, String name)

实现线程三种方法

新建类实现runnable接口

代码语言:javascript复制
class MyThread implements Runnable//新建类实现runnable接口
 
new Thread(new MyThread,...)
 
这种方法会新增类,有更新更好的方法

匿名内部类

代码语言:javascript复制
new Thread(new Runnable() {
    @Override
    public void run() {
 
    }
   }, "your thread name").start();
 
 这种方法不需要创建新的类,可以new接口

lambda表达式

代码语言:javascript复制
new Thread(() -> {
 
 }, "your thread name").start();
 
  这种方法代码更简洁精炼

代码实例

代码语言:javascript复制
package com.atguigu.thread;
 
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
class Ticket //实例例eld  method
{
 private int number=30;
/* //1同步 public synchronized void sale() 
 {//2同步  synchronized(this) {}
  if(number > 0) {
    System.out.println(Thread.currentThread().getName() "卖出" (number--) "t 还剩number);
   }
 }*/
 
// Lock implementations provide more extensive locking operations
// than can be obtained using synchronized methods and statements. 
 private Lock lock = new ReentrantLock();//List list = new ArrayList()
 
 public void sale() 
 {
   lock.lock();
   
   try {
    if(number > 0) {
     System.out.println(Thread.currentThread().getName() "卖出" (number--) "t 还剩number);
    }
   } catch (Exception e) {
    e.printStackTrace();
   } finally {
    lock.unlock();
   }
   
 }
 
}
 
 
/**
 * 
 * @Description:卖票程序个售票出  0张票
 @author xiale
 * 笔记:J里面如何 1 多线程编-上
    1.1 线程  (资里源类 *   1.2 高内聚 /
public class SaleTicket 
{
 public static void main(String[] args)//main所有程序
   Ticket ticket = new Ticket();
   //Thread(Runnable target, String name) Allocates a new Thread object.
 
   
 new Thread(() -> {for (int i = 1; i < 40; i  )ticket.sale();}, "AA").start();
 new Thread(() -> {for (int i = 1; i < 40; i  )ticket.sale();}, "BB").start();
 new Thread(() -> {for (int i = 1; i < 40; i  )ticket.sale();}, "CC").start();
 
   
/*  new Thread(new Runnable() {
    @Override
    public void run() 
    {
     for (int i = 1; i <=40; i  ) 
     {
       
       ticket.sale();
     }
    }
   }, "AA").start();
   
   new Thread(new Runnable() {
    @Override
    public void run() 
    {
     for (int i = 1; i <=40; i  ) 
     {
       ticket.sale();
     }
    }
   }, "BB").start();
   new Thread(new Runnable() {
    @Override
    public void run() 
    {
     for (int i = 1; i <=40; i  ) 
     {
       ticket.sale();
     }
    }
   }, "CC").start();
   */
 }
}
 
//1 class MyThread implements Runnable
//2 匿名内部类
//3 laExpress

java8新特性

lambda表达式

接口只有一个方法

代码语言:javascript复制
lambda表达式,如果一个接口只有一个方法,我可以把方法名省略
Foo foo = () -> {System.out.println("****hello lambda");};

写法:拷贝小括号(),写死右箭头->,落地大括号{...}

函数式接口

代码语言:javascript复制
lambda表达式,必须是函数式接口,必须只有一个方法
如果接口只有一个方法java默认它为函数式接口。
为了正确使用Lambda表达式,需要给接口加个注解:@FunctionalInterface
如有两个方法,立刻报错
 
Runnable接口为什么可以用lambda表达式?

接口里是否能有实现方法?

default方法

代码语言:javascript复制
接口里在java8后容许有接口的实现,default方法默认实现
default int div(int x,int y) {
  return x/y;
 }
接口里default方法可以有几个?

静态方法实现

代码语言:javascript复制
静态方法实现:接口新增 
public static int sub(int x,int y){
  return x-y;
}
可以有几个?
注意静态的叫类方法,能用foo去调吗?要改成Foo

代码示例:

代码语言:javascript复制
package com.atguigu.thread;
@FunctionalInterface
interface Foo{
 
// public void sayHello() ;
// public void say886() ;
 
 public int add(int x,int y);
 
 default int div(int x,int y) {
   return x/y;
 }
 
 public static int sub(int x,int y) {
   return x-y;
 }
}
 
 
 
/**
 * 
 * @Description: Lambda Express-----> 函数式编程
 * 1 拷贝小括号(形参列表),写死右箭头 ->,落地大括号 {方法实现}
 * 2 有且只有一个public方法@FunctionalInterface注解增强定义
 * 3 default方法默认实现
 * 4 静态方法实现
 */
public class LambdaDemo
{
 public static void main(String[] args)
 {
//  Foo foo = new Foo() {
//   @Override
//   public void sayHello() {
//     System.out.println("Hello!!");
//   }
//
//   @Override
//   public void say886() {
//     // TODO Auto-generated method stub
//     
//   }
//  };
//  foo.sayHello();
//  System.out.println("============");
//  foo = ()->{System.out.println("Hello!! lambda !!");};
//  foo.sayHello();
 
 Foo foo = (x,y)->{
   System.out.println("Hello!! lambda !!");
   return x y;
   };
   
   int result = foo.add(3,5);
   System.out.println("******result=" result);
   System.out.println("******result div=" foo.div(10, 2));
   System.out.println("******result sub=" Foo.sub(10, 2));
   
 }
}

Callable接口

Callable接口是什么?

这是一个功能接口,因此可以用作lambda表达式或方法引用的赋值对象。

与runnable对比

实现方法对比

代码语言:javascript复制
 创建新类MyThread实现runnable接口
class MyThread implements Runnable{
 @Override
 public void run() {
 
 }
}
新类MyThread2实现callable接口
class MyThread2 implements Callable<Integer>{
 @Override
 public Integer call() throws Exception {
  return 200;
 } 
}
 面试题:callable接口与runnable接口的区别?
 
 答:(1)是否有返回值
       (2)是否抛异常
       (3)落地方法不一样,一个是run,一个是call

怎么用?

直接替换runnable是否可行?

 不可行,因为:thread类的构造方法根本没有Callable

这像认识一个不认识的同学,我可以找中间人介绍。 中间人是什么?java多态,一个类可以实现多个接口!!

FutureTask<Integer> ft = new FutureTask<Integer>(new MyThread()); new Thread(ft, "AA").start(); 运行成功后如何获得返回值?

ft.get();

FutureTask

是什么?

未来的任务,用它就干一件事,异步调用 main方法就像一个冰糖葫芦,一个个方法由main串起来。 但解决不了一个问题:正常调用挂起堵塞问题

例子: (1)老师上着课,口渴了,去买水不合适,讲课线程继续,我可以单起个线程找班长帮忙买水, 水买回来了放桌上,我需要的时候再去get。 (2)4个同学,A算1 20,B算21 30,C算31*到40,D算41 50,是不是C的计算量有点大啊, FutureTask单起个线程给C计算,我先汇总ABD,最后等C计算完了再汇总C,拿到最终结果 (3)高考:会做的先做,不会的放在后面做

原理:

代码语言:javascript复制
在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时,可以把这些作业交给Future对象在后台完成,
当主线程将来需要时,就可以通过Future对象获得后台作业的计算结果或者执行状态。
 
一般FutureTask多用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。
 
仅在计算完成时才能检索结果;如果计算尚未完成,则阻塞 get 方法。一旦计算完成,
就不能再重新开始或取消计算。get方法而获取结果只有在计算完成时获取,否则会一直阻塞直到任务转入完成状态,
然后会返回结果或者抛出异常。 
 
只计算一次
get方法放到最后

代码:

代码语言:javascript复制
package com.atguigu.thread;
 
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
 
 
 
 
 
class MyThread implements Callable<Integer>
{
  @Override
  public Integer call() throws Exception
  {
     Thread.sleep(4000);
     System.out.println(Thread.currentThread().getName() "  *****come in call");
     return 200;
  }
}
 
 
/**
 * 
 * @Description: Callable接口获得多线程
 * @author xialei
 * @date 
 * 笔记结论见最后
 */
 
public class CallableDemo
{
  public static void main(String[] args) throws InterruptedException, ExecutionException
  {
     
     FutureTask<Integer> ft = new FutureTask<Integer>(new MyThread());
     new Thread(ft, "AA").start();
     
     /*FutureTask<Integer> ft2 = new FutureTask<Integer>(new MyThread());
     new Thread(ft2, "BB").start();*/
     
     
     System.out.println(Thread.currentThread().getName() "------main");
     
     
     Integer result = ft.get();
     //Integer result2 = ft2.get();
     System.out.println("**********result: " result);
     
  } 
}
 
/**
 * 
 * 
在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时,可以把这些作业交给Future对象在后台完成,
当主线程将来需要时,就可以通过Future对象获得后台作业的计算结果或者执行状态。
 
一般FutureTask多用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。
 
仅在计算完成时才能检索结果;如果计算尚未完成,则阻塞 get 方法。一旦计算完成,
就不能再重新开始或取消计算。get方法而获取结果只有在计算完成时获取,否则会一直阻塞直到任务转入完成状态,
然后会返回结果或者抛出异常。 
 
只计算一次
get方法放到最后
 */

线程间通信

线程间通信:1、生产者 消费者2、通知等待唤醒机制

synchronized实现

代码:

代码语言:javascript复制
package com.atguigu.thread;
 
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
import org.omg.IOP.Codec;
 
 
class ShareDataOne//资源类
{
  private int number = 0;//初始值为零的一个变量
 
  public synchronized void increment() throws InterruptedException 
  {
     //1判断
     if(number !=0 ) {
       this.wait();
     }
     //2干活
       number;
     System.out.println(Thread.currentThread().getName() "t" number);
     //3通知
     this.notifyAll();
  }
  
  public synchronized void decrement() throws InterruptedException 
  {
     // 1判断
     if (number == 0) {
       this.wait();
     }
     // 2干活
     --number;
     System.out.println(Thread.currentThread().getName()   "t"   number);
     // 3通知
     this.notifyAll();
  }
}
 
/**
 * 
 * @Description:
 *现在两个线程,
 * 可以操作初始值为零的一个变量,
 * 实现一个线程对该变量加1,一个线程对该变量减1,
 * 交替,来10轮。 
 * @author xialei
 *
 *  * 笔记:Java里面如何进行工程级别的多线程编写
 * 1 多线程变成模板(套路)-----上
 *     1.1  线程    操作    资源类  
 *     1.2  高内聚  低耦合
 * 2 多线程变成模板(套路)-----下
 *     2.1  判断
 *     2.2  干活
 *     2.3  通知
 
 */
public class NotifyWaitDemoOne
{
  public static void main(String[] args)
  {
     ShareDataOne sd = new ShareDataOne();
     new Thread(() -> {
       for (int i = 1; i < 10; i  ) {
          try {
            sd.increment();
          } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }
       }
     }, "A").start();
     new Thread(() -> {
       for (int i = 1; i < 10; i  ) {
          try {
            sd.decrement();
          } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }
       }
     }, "B").start();
  }
}
/*
 * * 
 * 2 多线程变成模板(套路)-----下
 *     2.1  判断
 *     2.2  干活
 *     2.3  通知
 * 3 防止虚假唤醒用while
 * 
 * 
 * */

换成4个线程

代码语言:javascript复制
换成4个线程会导致错误,虚假唤醒
 
原因:在java多线程判断时,不能用if,程序出事出在了判断上面,
突然有一天加的线程进到if了,突然中断了交出控制权,
没有进行验证,而是直接走下去了,加了两次,甚至多次

解决方法:

解决虚假唤醒:查看API,java.lang.Object

中断和虚假唤醒是可能产生的,所以要用loop循环,if只判断一次,while是只要唤醒就要拉回来再判断一次。if换成while

代码:

代码语言:javascript复制
package com.atguigu.thread;
 
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
import org.omg.IOP.Codec;
 
 
class ShareData//资源类
{
  private int number = 0;//初始值为零的一个变量
 
  public synchronized void increment() throws InterruptedException 
  {
     //判断
     while(number!=0) {
       this.wait();
     }
     //干活
       number;
     System.out.println(Thread.currentThread().getName() " t " number);
     //通知
     this.notifyAll();;
  }
  
  public synchronized void decrement() throws InterruptedException 
  {
     //判断
     while(number!=1) {
       this.wait();
     }
     //干活
     --number;
     System.out.println(Thread.currentThread().getName() " t " number);
     //通知
     this.notifyAll();
  }
}
 
/**
 * 
 * @Description:
 *现在两个线程,
 * 可以操作初始值为零的一个变量,
 * 实现一个线程对该变量加1,一个线程对该变量减1,
 * 交替,来10轮。 
 *
 *  * 笔记:Java里面如何进行工程级别的多线程编写
 * 1 多线程变成模板(套路)-----上
 *     1.1  线程    操作    资源类  
 *     1.2  高内聚  低耦合
 * 2 多线程变成模板(套路)-----下
 *     2.1  判断
 *     2.2  干活
 *     2.3  通知
 
 */
public class NotifyWaitDemo
{
  public static void main(String[] args)
  {
     ShareData sd = new ShareData();
     new Thread(() -> {
 
       for (int i = 1; i <= 10; i  ) {
          try {
            sd.increment();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
       }
     }, "A").start();
     
     new Thread(() -> {
 
       for (int i = 1; i <= 10; i  ) {
          try {
            sd.decrement();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
       }
     }, "B").start();
     new Thread(() -> {
 
       for (int i = 1; i <= 10; i  ) {
          try {
            sd.increment();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
       }
     }, "C").start();
     new Thread(() -> {
 
       for (int i = 1; i <= 10; i  ) {
          try {
            sd.decrement();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
       }
     }, "D").start();
     
  }
}
 
 
/*
 * * 
 * 2 多线程变成模板(套路)-----下
 *     2.1  判断
 *     2.2  干活
 *     2.3  通知
 * 3 防止虚假唤醒用while
 * 
 * */

原理图:

java8新版实现

对标实现

Condition

Condition:查看API,java.util.concurrent

代码语言:javascript复制
class BoundedBuffer {
   final Lock lock = new ReentrantLock();
   final Condition notFull  = lock.newCondition();
   final Condition notEmpty = lock.newCondition();
 
   final Object[] items = new Object[100];
   int putptr, takeptr, count;
 
   public void put(Object x) throws InterruptedException {
     lock.lock();
     try {
       while (count == items.length)
         notFull.await();
       items[putptr] = x;
       if (  putptr == items.length) putptr = 0;
         count;
       notEmpty.signal();
     } finally {
       lock.unlock();
     }
   }

代码:

代码语言:javascript复制
package com.atguigu.thread;
 
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
import org.omg.IOP.Codec;
 
 
class ShareData//资源类
{
  private int number = 0;//初始值为零的一个变量
 
  private Lock lock = new ReentrantLock();
  private Condition condition  = lock.newCondition(); 
   
  public  void increment() throws InterruptedException 
  {
     
      lock.lock();
         try {
          //判断
          while(number!=0) {
            condition.await();
          }
          //干活
            number;
          System.out.println(Thread.currentThread().getName() " t " number);
          //通知
          condition.signalAll();
     } catch (Exception e) {
       e.printStackTrace();
     } finally {
       lock.unlock();
     }
     
  }
  
  
  public  void decrement() throws InterruptedException 
  {
      
      lock.lock();
         try {
          //判断
          while(number!=1) {
            condition.await();
          }
          //干活
          --number;
          System.out.println(Thread.currentThread().getName() " t " number);
          //通知
          condition.signalAll();
     } catch (Exception e) {
       e.printStackTrace();
     } finally {
       lock.unlock();
     }
     
  }
  
  /*public synchronized void increment() throws InterruptedException 
  {
     //判断
     while(number!=0) {
       this.wait();
     }
     //干活
       number;
     System.out.println(Thread.currentThread().getName() " t " number);
     //通知
     this.notifyAll();;
  }
  
  public synchronized void decrement() throws InterruptedException 
  {
     //判断
     while(number!=1) {
       this.wait();
     }
     //干活
     --number;
     System.out.println(Thread.currentThread().getName() " t " number);
     //通知
     this.notifyAll();
  }*/
}
 
/**
 * @Description:
 *现在两个线程,
 * 可以操作初始值为零的一个变量,
 * 实现一个线程对该变量加1,一个线程对该变量减1,
 * 交替,来10轮。 
 * @author xialei
 *
 *  * 笔记:Java里面如何进行工程级别的多线程编写
 * 1 多线程变成模板(套路)-----上
 *     1.1  线程    操作    资源类  
 *     1.2  高内聚  低耦合
 * 2 多线程变成模板(套路)-----下
 *     2.1  判断
 *     2.2  干活
 *     2.3  通知
 
 */
public class NotifyWaitDemo
{
  public static void main(String[] args)
  {
     ShareData sd = new ShareData();
     new Thread(() -> {
 
       for (int i = 1; i <= 10; i  ) {
          try {
            sd.increment();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
       }
     }, "A").start();
     
     new Thread(() -> {
 
       for (int i = 1; i <= 10; i  ) {
          try {
            sd.decrement();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
       }
     }, "B").start();
     new Thread(() -> {
 
       for (int i = 1; i <= 10; i  ) {
          try {
            sd.increment();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
       }
     }, "C").start();
     new Thread(() -> {
 
       for (int i = 1; i <= 10; i  ) {
          try {
            sd.decrement();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
       }
     }, "D").start();
     
  }
}
 
/*
 * * 
 * 2 多线程变成模板(套路)-----下
 *     2.1  判断
 *     2.2  干活
 *     2.3  通知
 * 3 防止虚假唤醒用while
 * 
 * */

线程间定制化调用通信

线程-调用-资源类

判断-干活-通知

1、有顺序通知,需要有标识位 2、有一个锁Lock,3把钥匙Condition 3、判断标志位 4、输出线程名 第几次 第几轮 5、修改标志位,通知下一个  代码:

代码语言:javascript复制
package com.atguigu.thread;
 
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
 
class ShareResource
{
  private int number = 1;//1:A 2:B 3:C 
  private Lock lock = new ReentrantLock();
  private Condition c1 = lock.newCondition();
  private Condition c2 = lock.newCondition();
  private Condition c3 = lock.newCondition();
 
  public void print5(int totalLoopNumber)
  {
     lock.lock();
     try 
     {
       //1 判断
       while(number != 1)
       {
          //A 就要停止
          c1.await();
       }
       //2 干活
       for (int i = 1; i <=5; i  ) 
       {
          System.out.println(Thread.currentThread().getName() "t" i "t totalLoopNumber: " totalLoopNumber);
       }
       //3 通知
       number = 2;
       c2.signal();
     } catch (Exception e) {
       e.printStackTrace();
     } finally {
       lock.unlock();
     }
  }
  public void print10(int totalLoopNumber)
  {
     lock.lock();
     try 
     {
       //1 判断
       while(number != 2)
       {
          //A 就要停止
          c2.await();
       }
       //2 干活
       for (int i = 1; i <=10; i  ) 
       {
          System.out.println(Thread.currentThread().getName() "t" i "t totalLoopNumber: " totalLoopNumber);
       }
       //3 通知
       number = 3;
       c3.signal();
     } catch (Exception e) {
       e.printStackTrace();
     } finally {
       lock.unlock();
     }
  }  
  
  public void print15(int totalLoopNumber)
  {
     lock.lock();
     try 
     {
       //1 判断
       while(number != 3)
       {
          //A 就要停止
          c3.await();
       }
       //2 干活
       for (int i = 1; i <=15; i  ) 
       {
          System.out.println(Thread.currentThread().getName() "t" i "t totalLoopNumber: " totalLoopNumber);
       }
       //3 通知
       number = 1;
       c1.signal();
     } catch (Exception e) {
       e.printStackTrace();
     } finally {
       lock.unlock();
     }
  }  
}
 
 
/**
 * 
 * @Description: 
 * 多线程之间按顺序调用,实现A->B->C
 * 三个线程启动,要求如下:
 * 
 * AA打印5次,BB打印10次,CC打印15次
 * 接着
 * AA打印5次,BB打印10次,CC打印15次
 * ......来10轮  
 * @author xialei
 *
 */
public class ThreadOrderAccess
{
  public static void main(String[] args)
  {
     ShareResource sr = new ShareResource();
     
     new Thread(() -> {
       for (int i = 1; i <=10; i  ) 
       {
          sr.print5(i);
       }
     }, "AA").start();
     new Thread(() -> {
       for (int i = 1; i <=10; i  ) 
       {
          sr.print10(i);
       }
     }, "BB").start();
     new Thread(() -> {
       for (int i = 1; i <=10; i  ) 
       {
          sr.print15(i);
       }
     }, "CC").start();   
     
     
  }
}

多线程锁

锁的8个问题

代码语言:javascript复制
1 标准访问,先打印短信还是邮件
2 停4秒在短信方法内,先打印短信还是邮件
3 普通的hello方法,是先打短信还是hello
4 现在有两部手机,先打印短信还是邮件
5 两个静态同步方法,1部手机,先打印短信还是邮件
6 两个静态同步方法,2部手机,先打印短信还是邮件
7 1个静态同步方法,1个普通同步方法,1部手机,先打印短信还是邮件
8 1个静态同步方法,1个普通同步方法,2部手机,先打印短信还是邮件
 
 
 
运行答案:
1、短信
2、短信
3、Hello
4、邮件
5、短信
6、短信
7、邮件
8、邮件

锁的分析:

代码语言:javascript复制
A 一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,
其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一一个线程去访问这些synchronized方法
锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法
 
加个普通方法后发现和同步锁无关
换成两个对象后,不是同一把锁了,情况立刻变化。
 
 
synchronized实现同步的基础:Java中的每一个对象都可以作为锁。
具体表现为以下3种形式。
对于普通同步方法,锁是当前实例对象。
对于静态同步方法,锁是当前类的Class对象。
对于同步方法块,锁是Synchonized括号里配置的对象
 
当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。
 
也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,
可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁,
所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。
 
所有的静态同步方法用的也是同一把锁——类对象本身,
这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。
但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,
而不管是同一个实例对象的静态同步方法之间,
还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象!

代码:

代码语言:javascript复制
package com.atguigu.thread;
 
import java.util.concurrent.TimeUnit;
 
 
class Phone
{
 
 public  synchronized void sendSMS() throws Exception
 {
   
   System.out.println("------sendSMS");
 }
 public synchronized void sendEmail() throws Exception
 {
   System.out.println("------sendEmail");
 }
 
 public void getHello() 
 {
   System.out.println("------getHello");
 }
 
}
 
/**
 * 
 * @Description: 8锁
 * @author xialei
 * 
 1 标准访问,先打印短信还是邮件
 2 停4秒在短信方法内,先打印短信还是邮件
 3 新增普通的hello方法,是先打短信还是hello
 4 现在有两部手机,先打印短信还是邮件
 5 两个静态同步方法,1部手机,先打印短信还是邮件
 6 两个静态同步方法,2部手机,先打印短信还是邮件
 7 1个静态同步方法,1个普通同步方法,1部手机,先打印短信还是邮件
 8 1个静态同步方法,1个普通同步方法,2部手机,先打印短信还是邮件
 * ---------------------------------
 * 
 */
public class Lock_8
{
 public static void main(String[] args) throws Exception
 {
 
   Phone phone = new Phone();
   Phone phone2 = new Phone();
   
   new Thread(() -> {
    try {
     phone.sendSMS();
    } catch (Exception e) {
     e.printStackTrace();
    }
   }, "AA").start();
   
   Thread.sleep(100);
   
   new Thread(() -> {
    try {
     phone.sendEmail();
     //phone.getHello();
     //phone2.sendEmail();
    } catch (Exception e) {
     e.printStackTrace();
    }
   }, "BB").start();
 }
}

JUC强大的辅助类讲解

ReentrantReadWriteLock读写锁

代码语言:javascript复制
package com.atguigu.thread;
 
import java.util.concurrent.locks.ReentrantReadWriteLock;
 
class MyQueue
{
 
 
 private Object obj;
 private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
 
 public void readObj()
 {
   rwLock.readLock().lock();
   try 
   {
    System.out.println(Thread.currentThread().getName() "t" obj);
   } catch (Exception e) {
    e.printStackTrace();
   } finally {
    rwLock.readLock().unlock();
   }
 }
 
 public void writeObj(Object obj)
 {
   rwLock.writeLock().lock();
   try 
   {
    this.obj = obj;
    System.out.println(Thread.currentThread().getName() "writeThread:t" obj);
   } catch (Exception e) {
    e.printStackTrace();
   } finally {
    rwLock.writeLock().unlock();
   }
 }
 
}
 
/**
 * 
 * @Description: 一个线程写入,100个线程读取
 * @author xialei
 * 
 */
public class ReadWriteLockDemo
{
 public static void main(String[] args) throws InterruptedException
 {
   MyQueue q = new MyQueue();
   
   new Thread(() -> {
    q.writeObj("ClassName1221");
   }, "AAAAA").start();
   
   
   for (int i = 1; i <=100; i  ) 
   {
    new Thread(() -> {
     q.readObj();
    },String.valueOf(i)).start();
   }
   
   
 }
}

CountDownLatch减少计数

原理:

 * CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,这些线程会阻塞。  * 其它线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞),  * 当计数器的值变为0时,因await方法阻塞的线程会被唤醒,继续执行。

代码:

代码语言:javascript复制
package com.atguigu.thread;
 
import java.util.concurrent.CountDownLatch;
 
 
/**
 * 
 * @Description:
 *  *让一些线程阻塞直到另一些线程完成一系列操作后才被唤醒。
 * 
 * CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,这些线程会阻塞。
 * 其它线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞),
 * 当计数器的值变为0时,因await方法阻塞的线程会被唤醒,继续执行。
 * 
 * 解释:6个同学陆续离开教室后值班同学才可以关门。
 * 
 * main主线程必须要等前面6个线程完成全部工作后,自己才能开干 
 */
public class CountDownLatchDemo
{
   public static void main(String[] args) throws InterruptedException
   {
         CountDownLatch countDownLatch = new CountDownLatch(6);
       
       for (int i = 1; i <=6; i  ) //6个上自习的同学,各自离开教室的时间不一致
       {
          new Thread(() -> {
              System.out.println(Thread.currentThread().getName() "t 号同学离开教室");
              countDownLatch.countDown();
          }, String.valueOf(i)).start();
       }
       countDownLatch.await();
       System.out.println(Thread.currentThread().getName() "t****** 班长关门走人,main线程是班长");
          
   }
 
 
}

CyclicBarrier循环栅栏

原理:

代码语言:javascript复制
 * CyclicBarrier
 * 的字面意思是可循环(Cyclic)使用的屏障(Barrier)。它要做的事情是,
 * 让一组线程到达一个屏障(也可以叫同步点)时被阻塞,
 * 直到最后一个线程到达屏障时,屏障才会开门,所有
 * 被屏障拦截的线程才会继续干活。
 * 线程进入屏障通过CyclicBarrier的await()方法。

代码:

代码语言:javascript复制
package com.atguigu.thread;
 
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
 
/**
 * 
 * @Description: TODO(这里用一句话描述这个类的作用)  
 *
 * CyclicBarrier
 * 的字面意思是可循环(Cyclic)使用的屏障(Barrier)。它要做的事情是,
 * 让一组线程到达一个屏障(也可以叫同步点)时被阻塞,
 * 直到最后一个线程到达屏障时,屏障才会开门,所有
 * 被屏障拦截的线程才会继续干活。
 * 线程进入屏障通过CyclicBarrier的await()方法。
 * 
 * 集齐7颗龙珠就可以召唤神龙
 */
public class CyclicBarrierDemo
{
  private static final int NUMBER = 7;
  
  public static void main(String[] args)
  {
     //CyclicBarrier(int parties, Runnable barrierAction) 
     
     CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, ()->{System.out.println("*****集齐7颗龙珠就可以召唤神龙");}) ;
     
     for (int i = 1; i <= 7; i  ) {
       new Thread(() -> {
          try {
            System.out.println(Thread.currentThread().getName() "t 星龙珠被收集 ");
            cyclicBarrier.await();
          } catch (InterruptedException | BrokenBarrierException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }
       
       }, String.valueOf(i)).start();
     }
     
 
  }
}

Semaphore信号灯

原理:

 在信号量上我们定义两种操作:  * acquire(获取) 当一个线程调用acquire操作时,它要么通过成功获取信号量(信号量减1),  * 要么一直等下去,直到有线程释放信号量,或超时。  * release(释放)实际上会将信号量的值加1,然后唤醒等待的线程。  * 信号量主要用于两个目的,一个是用于多个共享资源的互斥使用,另一个用于并发线程数的控制。

代码:

代码语言:javascript复制
package com.atguigu.thread;
 
import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
 
/**
 * 
 * @Description: TODO(这里用一句话描述这个类的作用)  
 * @author xialei
 * 
 * 在信号量上我们定义两种操作:
 * acquire(获取) 当一个线程调用acquire操作时,它要么通过成功获取信号量(信号量减1),
 *             要么一直等下去,直到有线程释放信号量,或超时。
 * release(释放)实际上会将信号量的值加1,然后唤醒等待的线程。
 * 
 * 信号量主要用于两个目的,一个是用于多个共享资源的互斥使用,另一个用于并发线程数的控制。
 */
public class SemaphoreDemo
{
  public static void main(String[] args)
  {
     Semaphore semaphore = new Semaphore(3);//模拟3个停车位
     
     for (int i = 1; i <=6; i  ) //模拟6部汽车
     {
       new Thread(() -> {
          try 
          {
            semaphore.acquire();
            System.out.println(Thread.currentThread().getName() "t 抢到了车位");
            TimeUnit.SECONDS.sleep(new Random().nextInt(5));
            System.out.println(Thread.currentThread().getName() "t------- 离开");
          } catch (InterruptedException e) {
            e.printStackTrace();
          }finally {
            semaphore.release();
          }
       }, String.valueOf(i)).start();
     }
     
  }
}

0 人点赞