> 文档中心 > Java多线程教程(作者原创)

Java多线程教程(作者原创)


个人简介

作者是一个来自河源的大三在校生,以下笔记都是作者自学之路的一些浅薄经验,如有错误请指正,将来会不断的完善笔记,帮助更多的Java爱好者入门。

文章目录

    • 个人简介
    • Java并发编程(多线程高并发)
      • 创建线程的三种方式
        • 继承于Thread类
        • 实现Runnable接口(推荐)
        • 实现Callable接口
      • Thread常用方法
        • join方法
      • 计数器
      • 模拟并发(多线程)抢票=>超卖问题
        • 单线程抢票,没有安全问题
        • 多线程抢票出现安全问题
        • 解决多线程抢票线程不安全问题
      • 多线程的原子性、可见性、有序性
        • 原子性
        • 可见性(演示不出来)
        • 有序性
      • 多线程锁问题
        • 多线程出现异常自动释放锁
        • 死锁(重要)
      • 原子类AtomicXXX
        • 原子类(AtomicInteger/AtomicLong)
        • 多线程操作数组线程不安全
          • 解决方案(加锁和原子数组)
      • 线程通信
        • wait()/notify()机制实现线程通信
        • 生产者消费者模式
          • 一个生产者和一个消费者操作值
          • 多个生产者和多个消费者操作值
          • 一个生产者和一个消费者操作栈(用List集合去模拟)
          • 多个生产者和多个消费者操作栈(用List集合去模拟)
        • 利用管道流通信
        • Condition通信(用到了显式锁)
      • ThreadLocal
      • Lock显式锁
        • ReentrantLock的使用
        • 可重入锁特性
        • tryLock方法(解决死锁)
        • 读写锁(ReadWriteLock)
          • 读读共享
          • 写写互斥
          • 读写互斥
      • 线程池ThreadPool
        • 线程池的简单使用

Java并发编程(多线程高并发)

创建线程的三种方式

继承于Thread类

public class createThreadTest1 {    public static void main(String[] args) { thread01 thread01 = new thread01(); thread01.start(); //调用start方法开启线程     }}class thread01 extends Thread{    @Override    public void run() { System.out.println(Thread.currentThread().getName()+"===>正在运行"); try {     Thread.sleep(500); } catch (InterruptedException e) {     e.printStackTrace(); }    }}

实现Runnable接口(推荐)

因为Java不支持多继承,所以用实现接口的方法可扩展性会更高,让唯一的继承留个更加有用的类

public class createThreadTest1 {    public static void main(String[] args) {  Thread thread02 = new Thread(new thread02());  thread02.start();    }}class thread02 implements Runnable{    @Override    public void run() { System.out.println(Thread.currentThread().getName()+"===>正在运行"); try {     Thread.sleep(500); } catch (InterruptedException e) {     e.printStackTrace(); }    }}

也可以这样:

  //方法3:匿名开启线程 new Thread(new Runnable() {     @Override     public void run() {  System.out.println("匿名开启线程");} }).start();

可以用lambda表达式

   new Thread(() ->{     System.out.println("666");      }).start();

实现Callable接口

callable实现比较复杂

public class createThreadTest1 {    public static void main(String[] args) throws ExecutionException, InterruptedException { thread03 thread03 = new thread03(); FutureTask<String> futureTask=new FutureTask<>(thread03); new Thread(futureTask).start(); String getMsg = futureTask.get(); //这段代码必须在开启线程之后写 System.out.println(getMsg);    }class thread03 implements Callable<String>{    @Override    public String call() throws Exception { return "callable";    }}

Thread常用方法

public class threadMethod {    public static void main(String[] args) { thread01 thread01=new thread01(); Thread thread = new Thread(thread01); thread.setName("t1");//1.设置线程名字 thread.setPriority(5);//2.设置线程优先级(一般不建议设置),优先级高不一定先执行。。。。 System.out.println("thread.getState()==>"+thread.getState());//3.获取当前线程状态 boolean alive1 = thread.isAlive();//4.查看当前线程是否活着 System.out.println("alive==>"+alive1); thread.start(); //开启线程 try {     Thread.sleep(500);//5.线程休眠500ms     boolean alive = thread.isAlive();//查看当前线程是否活着     System.out.println("alive==>"+alive); } catch (InterruptedException e) {     e.printStackTrace(); }    }}class thread01 implements Runnable{    @Override    public void run() { System.out.println("id==>"+Thread.currentThread().getId()); //获取当前线程id System.out.println("name==>"+Thread.currentThread().getName());//获取当前线程名 System.out.println("Priority===>"+Thread.currentThread().getPriority());//获取线程优先级 System.out.println("state==>"+Thread.currentThread().getState());//获取当前线程状态 System.out.println("alive==>"+Thread.currentThread().isAlive());    }}

join方法

注意:join方法只能在开启线程之后在使用,不然就会无效,也就是先start()再join()

join方法的用处,可以让子线程去处理或者计算一些值,计算完之后main线程可以使用计算完的值

public class threadJoinTest {    private static int count=0;    public static void main(String[] args) { System.out.println("main========="); Thread t1 = new Thread(new Runnable() {     @Override     public void run() {  System.out.println("进入run方法");  for (int i = 0; i < 5; i++) {count++;      System.out.println("子线程==="+count);  }     } }); t1.start(); try {     //注意:join方法必须要在start方法后面,不然不生效,因为调用了start方法才会创建线程。然后再线程插队     t1.join(); //线程插队。也就是只有t1线程执行完,join方法后面的代码才能执行 } catch (InterruptedException e) {     e.printStackTrace(); } System.out.println("main线程==="+count);    }}

计数器

public class threadTimer {    public static void main(String[] args) { timer timer = new timer(); timer.setNum(10); new Thread(timer).start();    }}class timer implements Runnable{    private int num;    private boolean isRun=true;    public void setNum(int num) { this.num = num;    }    @Override    public  void run() {     while (isRun){  if(num<=0){      isRun=false;      System.out.println("程序结束!");      return;  }  System.out.println("倒计时,还剩"+num+"秒");  try {      Thread.sleep(1000);      num--;  } catch (InterruptedException e) {      e.printStackTrace();  }     }    }}

模拟并发(多线程)抢票=>超卖问题

单线程抢票,没有安全问题

public class threadTicket {    /     * 模拟高并发抢票线程不安全问题     */    public static void main(String[] args) { ticket ticket = new ticket(); new Thread(ticket).start();    }}class ticket implements Runnable{    private int ticket=100; //抢100张票    @Override    public void run() { while (true){     if(ticket<=0){  System.out.println("票被抢完了");  return;     }     ticket--;     System.out.println(Thread.currentThread().getName()+"==>抢到票了,还剩"+ticket+"张票");     try {  Thread.sleep(200);     } catch (InterruptedException e) {  e.printStackTrace();     } }    }}

多线程抢票出现安全问题

public class threadTicket {    /     * 模拟高并发抢票线程不安全问题     */    public static void main(String[] args) { ticket ticket = new ticket(); for (int i = 0; i < 10; i++) { //开启10个线程抢票     new Thread(ticket).start(); }    }}class ticket implements Runnable{    private int ticket=100; //抢100张票    @Override    public void run() { while (true){     if(ticket<=0){  System.out.println("票被抢完了");  return;     }     ticket--;     System.out.println(Thread.currentThread().getName()+"==>抢到票了,还剩"+ticket+"张票");     try {  Thread.sleep(200);     } catch (InterruptedException e) {  e.printStackTrace();     } }    }}

Java多线程教程(作者原创)

出现很多人抢到同一张票,出现了线程不安全问题

解决多线程抢票线程不安全问题

其实多线程抢票为啥会出现线程不安全问题,原因就是’ i-- ’ 语句。i–不是原子操作,肯定会出线程安全问题

i-- ,其实有几个过程 == 1.先读取i的值 2.让i-1 3.赋值给i 4.保存到主内存

方法一:加锁,使用synchronized关键字

缺点:synchronized关键字和Lock显式锁是JVM级别的,对于同一个JVM进程中有效,但是对于分布式环境的多进程是无效的,因为分布式环境是多进程的,也就是不属于同一个JVM进程,这时候只能采用分布式锁了,比如Redis分布式锁

同步语句块。

public class threadTicket {    /     * 模拟高并发抢票线程不安全问题     */    public static void main(String[] args) { ticket ticket = new ticket(); for (int i = 0; i < 15; i++) {     new Thread(ticket).start(); }    }}class ticket implements Runnable{    private int ticket=10; //抢10张票    @Override    public void run() { synchronized (this){  if(ticket<=0){      System.out.println("票被抢完了");      return;  }  ticket--;  System.out.println(Thread.currentThread().getName()+"==>抢到票了,还剩"+ticket+"张票");  try {      Thread.sleep(200);  } catch (InterruptedException e) {      e.printStackTrace();  } }    }}

和上面一样

public class threadTicket {    /     * 模拟高并发抢票线程不安全问题     */    public static void main(String[] args) { ticket ticket = new ticket(); for (int i = 0; i < 15; i++) {     new Thread(ticket).start(); }    }}class ticket implements Runnable {    private int ticket = 30; //抢10张票    private static final Object obj = new Object(); //加上static,不管是什么对象,只要是这个类的,就只有一个obj    @Override    public void run() { while (true) {     synchronized (obj) {  if (ticket <= 0) {      System.out.println("票被抢完了");      return;  }  ticket--;  System.out.println(Thread.currentThread().getName() + "==>抢到票了,还剩" + ticket + "张票");  try {      Thread.sleep(200);  } catch (InterruptedException e) {      e.printStackTrace();  }     } }    }}

方式二:使用并发包下的原子类Atomicxxxx

Atomic原子类,保证了线程中变量的原子性,又因为源码里面有volatile关键字,使得这个value对于线程是可见的,保证了变量的可见性。

public class threadTicket {    /     * 模拟高并发抢票线程不安全问题     */    public static void main(String[] args) { ticket ticket = new ticket(); for (int i = 0; i < 10; i++) { //开启10个线程抢票     new Thread(ticket).start(); }    }}class ticket implements Runnable{//    private  int ticket=100; //抢100张票    private AtomicInteger atomicInteger=new AtomicInteger(5);    @Override    public void run() { while (true){     if(atomicInteger.get()<=0){  System.out.println("票被抢完了");  return;     }//     ticket--;     int andDecrement = atomicInteger.getAndDecrement();     System.out.println(Thread.currentThread().getName()+"==>抢到票了,还剩"+andDecrement+"张票");     try {  Thread.sleep(200);     } catch (InterruptedException e) {  e.printStackTrace();     } }    }

多线程的原子性、可见性、有序性

原子性

保证原子性有两个方法:1.使用锁 2.CAS指令

众所周知,i++ i-- 不是原子操作,那么也就不具有原子性,在多线程环境下,面对多个线程并发访问是线程不安全的,容易出问题的

方法一:synchronized关键字或者lock显式锁

public class threadTicket {    /     * 模拟高并发抢票线程不安全问题     */    public static void main(String[] args) { ticket ticket = new ticket(); for (int i = 0; i < 15; i++) {     new Thread(ticket).start(); }    }}class ticket implements Runnable {    private int ticket = 30; //抢10张票    private static final Object obj = new Object(); //加上static,不管是什么对象,只要是这个类的,就只有一个obj    @Override    public void run() { while (true) {     synchronized (obj) {  if (ticket <= 0) {      System.out.println("票被抢完了");      return;  }  ticket--;  System.out.println(Thread.currentThread().getName() + "==>抢到票了,还剩" + ticket + "张票");  try {      Thread.sleep(200);  } catch (InterruptedException e) {      e.printStackTrace();  }     } }    }}

方法二:用CAS指令,不过java并发包提供了实现CAS指令的工具,也就是AtomicXXX,底层使用了volatile,保证了变量对于线程的可见性

public class threadTicket {    /     * 模拟高并发抢票线程不安全问题     */    public static void main(String[] args) { ticket ticket = new ticket(); for (int i = 0; i < 10; i++) { //开启10个线程抢票     new Thread(ticket).start(); }    }}class ticket implements Runnable{//    private  int ticket=100; //抢100张票    private AtomicInteger atomicInteger=new AtomicInteger(5);    @Override    public void run() { while (true){     if(atomicInteger.get()<=0){  System.out.println("票被抢完了");  return;     }//     ticket--;     int andDecrement = atomicInteger.getAndDecrement();     System.out.println(Thread.currentThread().getName()+"==>抢到票了,还剩"+andDecrement+"张票");     try {  Thread.sleep(200);     } catch (InterruptedException e) {  e.printStackTrace();     } }    }

可见性(演示不出来)

有序性

多线程锁问题

锁对象不同不能同步

public class syncLockTest {    //创建两个线程对象,synchronized(this)就无法发挥作用了,因为subthread1和subthread2是两个不同的对象    private static subThread1 subThread1=new subThread1();    private static subThread1 subThread2=new subThread1();    public static void main(String[] args) { Thread t1 = new Thread(subThread1); //传入两个不同的对象 Thread t2 = new Thread(subThread2); t1.setName("111"); t2.setName("222"); t1.start(); t2.start();    }}class subThread1 implements Runnable{    private  int count=10;    @Override    public void run() { sm1();    }    public synchronized void sm1(){ while (true){  if(count>0){      count--;      System.out.println(Thread.currentThread().getName()+"===>还剩"+count);  }else {      break;  } }    }}

这段代码虽然加了锁,但是也是有线程安全问题的

因为subthread1和subthread2是不同的对象,所以synchronized(this)就会无效,锁不住

解决方法:把sm1()方法定义为static方法,这样synchronized就会变成锁住这个线程类,而不是当前对象,不管是什么对象,只要是这个线程类创建的对象就共用一把锁,达到线程安全

public class syncLockTest {    //创建两个线程对象,synchronized(this)就无法发挥作用了,因为subthread1和subthread2是两个不同的对象    private static subThread1 subThread1=new subThread1();    private static subThread1 subThread2=new subThread1();    public static void main(String[] args) { Thread t1 = new Thread(subThread1); //传入两个不同的对象 Thread t2 = new Thread(subThread2); t1.setName("111"); t2.setName("222"); t1.start(); t2.start();    }}class subThread1 implements Runnable{    private static   int count=10;    @Override    public void run() { sm1();    }    //相当于锁住类    public synchronized static void sm1(){ while (true){  if(count>0){      count--;      System.out.println(Thread.currentThread().getName()+"===>还剩"+count);  }else {      break;  } }    }    }

多线程出现异常自动释放锁

public class lockException {    //出现异常自动释放锁    private static subThread subThread=new subThread();    public static void main(String[] args) { for (int i = 0; i < 10; i++) {     new Thread(subThread).start(); }    }}class subThread implements Runnable{@Override    public void run() {     synchronized (this){  while (true){      try {   Thread.sleep(200);      } catch (InterruptedException e) {   e.printStackTrace();      }      throw new RuntimeException();  }     }    }}

死锁(重要)

死锁的原理:有两个线程分别是线程A、B ,两把锁1、2 ,线程A拿到了锁1,线程B拿到了锁2,线程A没有释放锁1,且必须要拿到锁2才能释放锁1(也就是执行完线程A所有代码),反之,线程B没有释放锁2,但是有一定要获得锁1才能执行完线程B的代码,然后两个线程为了对方的锁一直在僵持,互不相让,这就造成了死锁

tryLock解决死锁

public class dieLock {    //死锁    private static subThread3 subThread3=new subThread3();    public static void main(String[] args) { Thread t1 = new Thread(subThread3); Thread t2 = new Thread(subThread3); t1.setName("t1"); t2.setName("t2"); t1.start(); t2.start();    }}class subThread3 implements Runnable{    private static final Object obj1=new Object();    private static final Object obj2=new Object();    @Override    public void run() { if(Thread.currentThread().getName().equals("t1")){     synchronized (obj1){  System.out.println(Thread.currentThread().getName()+"获得了锁1,想去获得锁2");  synchronized (obj2){      System.out.println(Thread.currentThread().getName()+"获得了锁2");  }     } }else {     synchronized (obj2){  System.out.println(Thread.currentThread().getName()+"获得了锁2,想去获得锁1");  synchronized (obj1){      System.out.println(Thread.currentThread().getName()+"获得了锁1");  }     } }    }}

原子类AtomicXXX

让两个子线程和一个main线程去共同减少count

方式一:

输出的结果可能是子线程1一直在减少count,其他的线程在原地等待,其实这是正常现象,因为synchronized关键字是非公平锁,为了保证效率,它会让拿到锁的那个线程更容易再次拿到锁,只有公平锁才会让这些线程都拿到锁,Lock实现类可以通过构造方法去让锁变成公平锁

public class atomicIntegerTest {    private static int count=20;    private static final Object lock=new Object();    public static void main(String[] args) { Thread t1 = new Thread(new Runnable() {     @Override     public void run() {  while (true){      synchronized (lock){   if(count<=0){System.out.println("子线程没有count了");break;   }   count--;   System.out.println("子线程减值==还剩="+count);      }  }     } }); t1.start(); Thread t2 = new Thread(new Runnable() {     @Override     public void run() {  while (true){      synchronized (lock){   if(count<=0){System.out.println("子线程没有count了");break;   }   count--;   System.out.println("子线程减值==还剩="+count);      }  }     } }); t2.start();   while (true){synchronized (lock){    if(count<=0){ System.out.println("主线程没有count了"); break;    }    count--;    System.out.println("主线程减值==还剩="+count);}   }    }}

方式二:

原子类(AtomicInteger/AtomicLong)

public class atomicIntegerTest1 {   private static  AtomicInteger count=new AtomicInteger(10);   public static void main(String[] args) {Thread t1 = new Thread(new Runnable() {    @Override    public void run() { while (true){  if(count.get()<=0){      System.out.println("子线程1没有count了");      break;  }  count.getAndDecrement();  System.out.println("子线程1减值==还剩="+count.get());     }    }});t1.start();Thread t2 = new Thread(new Runnable() {    @Override    public void run() { while (true){  if(count.get()<=0){      System.out.println("子线程2没有count了");      break;  }  count.getAndDecrement();  System.out.println("子线程2减值==还剩="+count.get());     }    }});t2.start();while (true){ if(count.get()<=0){     System.out.println("主线程没有count了");     break; } count.getAndDecrement(); System.out.println("主线程减值==还剩="+count.get());}   }}

多线程操作数组线程不安全

public class atomicIntegerArrayTest {    public static void main(String[] args) { atomicArrayThread atomicArrayThread = new atomicArrayThread(); Thread t1 = new Thread(atomicArrayThread); Thread t2 = new Thread(atomicArrayThread); t1.start(); t2.start();    }}class atomicArrayThread implements Runnable{    private int arr[]=new int[5];    @Override    public void run() { while (true){     if(arr[2]==10){  System.out.println("break");  break;     }     for (int i = 0; i < arr.length; i++) {  arr[i]++;     }     System.out.println(Arrays.toString(arr)); }    }}
解决方案(加锁和原子数组)

方法一:加锁

public class atomicIntegerArrayTest {    public static void main(String[] args) { atomicArrayThread atomicArrayThread = new atomicArrayThread(); Thread t1 = new Thread(atomicArrayThread); Thread t2 = new Thread(atomicArrayThread); t1.start(); t2.start();    }}class atomicArrayThread implements Runnable{    private int arr[]=new int[5];    @Override    public void run() { while (true){     synchronized (this){ //因为new Thread传入的对象是相同的,所以this调用的对象也是一样,那么synchronized(this)就有效  if(arr[2]==10){      System.out.println("break");      break;  }  for (int i = 0; i < arr.length; i++) {      arr[i]++;  }  System.out.println(Arrays.toString(arr));     } }    }}

方法二:采用原子数组(弄不出)

线程通信

线程通信有很多种,比如wait()/notify()机制,管道流(pipeXXX)通信,可重入锁的Condition对象的await()/signal()方法等等

wait()/notify()机制实现线程通信

wait()方法和sleep()方法都是阻塞方法,但是也有区别,wait()方法会释放锁、sleep()方法不会释放锁,并且wait方法必须要有锁对象,由锁对象进行调用wait方法,而sleep方法不用锁对象,wait方法是Object类的,sleep方法是Thread类的。

注意:notify/notifyAll和wait方法都是要用同一个锁对象去调用才能唤醒对方。。。。。。

生产者消费者模式

一个生产者和一个消费者操作值

需求:我们要实现一个生产者生产好一份菜(value值)就去通知消费者,消费者去拿菜,如果生产者没有生产好,那消费者就等待

public class ThreadCommunicationTest1 {    /     * 线程通信     * 一生产者一消费者模式     */    private static final Object lock = new Object(); //虽然对象不同,不能用锁this,但是可以锁住一个final对象    private static String value = "";    public static void main(String[] args) { setThread1 setThread = new setThread1(); getThread1 getThread = new getThread1(); Thread t1 = new Thread(setThread); Thread t2 = new Thread(getThread); t1.start(); t2.start();    }    //生产者    static class setThread1 implements Runnable { @Override public void run() {     while (true){  synchronized (lock) {      if (value == null || value.equals("")) {   value = "菜品:" + System.currentTimeMillis();   System.out.println(value+"====做好了");   try {Thread.sleep(50); //做好之后,不马上去通知顾客,先缓一缓   } catch (InterruptedException e) {e.printStackTrace();   }   lock.notify();      } else {   System.out.println("生产者等待菜被拿走");   try {lock.wait();   } catch (InterruptedException e) {e.printStackTrace();   }      }  }     } }    }    //消费者    static class getThread1 implements Runnable {  @Override public void run() {     while (true){  synchronized (lock) {      if (value == null || value.equals("")) {   System.out.println("消费者等待上菜");   try {lock.wait();   } catch (InterruptedException e) {e.printStackTrace();   }      } else {   System.out.println("我收下了=="+value);   value="";   try {Thread.sleep(50);//吃完之后,不马上点餐   } catch (InterruptedException e) {e.printStackTrace();   }   lock.notify();      }  }     } }    }}
多个生产者和多个消费者操作值

多个线程的生产者消费者模式必须不能用notify了,要用notifyAll

多生产者多消费者模式下,不用notify而用notifyAll原因是,如果有三个厨师分别为ABC,A厨师上了一道菜,顾客看到自己的菜到了,然后就把菜拿走了,如果用notify意思就是只告诉厨师A,其他厨师不知道这个顾客拿走了菜,以为自己没有上这道菜,便再次为这个顾客做这道菜,这样显然是不可能的,所以我们要notifyAll,通知所有厨师,说明这个顾客已经拿走菜了,不用再上他的菜,这样问题就解决了。

public class ThreadCommunicationTest2 {    private static String value="";    private static final Object lock=new Object();    public static void main(String[] args) { setValue2 setValue2 = new setValue2(); getValue2 getValue2 = new getValue2(); //生产者线程 Thread t1 = new Thread(setValue2); Thread t2 = new Thread(setValue2); Thread t3 = new Thread(setValue2); //消费者线程 Thread t4 = new Thread(getValue2); Thread t5 = new Thread(getValue2); Thread t6 = new Thread(getValue2); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); t6.start();    }    //生产者线程    static class setValue2 implements Runnable{ @Override public void run() {     while (true){ //这里用个死循环,让生产者消费者一直运作     synchronized (lock){      if(value==null||value.equals("")){ //值没有就生产   value="菜品:"+Thread.currentThread().getName()+"===>"+System.currentTimeMillis();   System.out.println(value+"=====>生产好了");   try {Thread.sleep(100);   } catch (InterruptedException e) {e.printStackTrace();   }   lock.notifyAll();      }else{   try {lock.wait(); //菜只能放一个,所以菜满了要等待顾客拿走   } catch (InterruptedException e) {e.printStackTrace();   }      }  }     } }    }    static class getValue2 implements Runnable{ @Override public void run() {     while (true){  synchronized (lock){      if(value==null||value.equals("")){   try {lock.wait();//如果没有菜就等待   } catch (InterruptedException e) {e.printStackTrace();   }      }else {   System.out.println("消费者拿走了"+value);   value=""; //菜拿走了,让value等于空字符串   try {Thread.sleep(100);   } catch (InterruptedException e) {e.printStackTrace();   }   lock.notifyAll();//通知所有厨师      }  }     } }    }}
一个生产者和一个消费者操作栈(用List集合去模拟)
public class ThreadCommunicationTest3 {    /     * 一个生产者和一个消费者操作栈(List去模拟)     */    private static List<String> list=new ArrayList<>();    private static final int MAX_SIZE=1; //指定list最大容量    private static final Object lock=new Object();    public static void main(String[] args) { setValue3 t1 = new setValue3(); getValue3 t2 = new getValue3(); new Thread(t1).start(); new Thread(t2).start();    }    static class setValue3 implements Runnable{ @Override public void run() {     while (true){  synchronized (lock){      if(list.size()<MAX_SIZE){   String value="(生产者)菜品:"+System.currentTimeMillis();   list.add(value);   System.out.println(Thread.currentThread().getName()+"==>"+value);   try {Thread.sleep(50);   } catch (InterruptedException e) {e.printStackTrace();   }   lock.notify();      }else {   try {lock.wait(); //菜到达指定数量是就不上菜了,等待客人拿走菜   } catch (InterruptedException e) {e.printStackTrace();   }      }  }     } }    }    static class getValue3 implements Runnable{ @Override public void run() {     while (true){  synchronized (lock){      if(list.size()<=0){   try {lock.wait();   } catch (InterruptedException e) {e.printStackTrace();   }      }else {   String rm = list.remove(0);   System.out.println(Thread.currentThread().getName()+"拿走了"+rm);   try {Thread.sleep(50);   } catch (InterruptedException e) {e.printStackTrace();   }   lock.notify();      }  }     } }    }}
多个生产者和多个消费者操作栈(用List集合去模拟)
public class ThreadCommunicationTest4 {    private static List<String> list=new ArrayList<>();    private static final Object lock=new Object();    private static final int MAX_SIZE=3;    public static void main(String[] args) { setValue4 setValue4 = new setValue4(); getValue4 getValue4 = new getValue4(); Thread t1 = new Thread(setValue4); Thread t2 = new Thread(setValue4); Thread t3 = new Thread(setValue4); Thread t4 = new Thread(getValue4); Thread t5 = new Thread(getValue4); Thread t6 = new Thread(getValue4); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); t6.start();    }    static class setValue4 implements Runnable{ @Override public void run() {     while (true){  synchronized (lock){      if(list.size()<MAX_SIZE){   String value=Thread.currentThread().getName()+"==>"+"菜品:"+System.currentTimeMillis();   list.add(value);   System.out.println(value+" 已经做好了");   try {Thread.sleep(50);   } catch (InterruptedException e) {e.printStackTrace();   }   lock.notifyAll();      }else {   try {lock.wait();   } catch (InterruptedException e) {e.printStackTrace();   }      }  }     } }    }    static class getValue4 implements Runnable{ @Override public void run() {     while (true) {  synchronized (lock){      if(list.size()==0){   try {lock.wait();   } catch (InterruptedException e) {e.printStackTrace();   }      }else {   String rm = list.remove(0);   System.out.println("消费者已经拿走了"+rm);   try {Thread.sleep(50);   } catch (InterruptedException e) {e.printStackTrace();   }   lock.notifyAll();      }  }     } }    }}

利用管道流通信

public class pipeStreamTest {    /     * 利用管道流通信     */    private static PipedInputStream inputStream=new PipedInputStream();    private static PipedOutputStream outputStream=new PipedOutputStream();    static { try {     inputStream.connect(outputStream); //建立连接 } catch (IOException e) {     e.printStackTrace(); }    }    public static void main(String[] args) throws IOException, InterruptedException { inputThread inputThread = new inputThread(); outputThread outputThread = new outputThread(); Thread t1 = new Thread(inputThread); Thread t2 = new Thread(outputThread); t2.start(); //执行写操作 Thread.sleep(10); t1.start();    }    static class inputThread implements Runnable{ @Override public void run() {     try {   byte bytes[]=new byte[inputStream.available()];   inputStream.read(bytes);   String str=new String(bytes,"UTF-8");  System.out.println("管道输入流已读取===>"+str);     } catch (IOException e) {  e.printStackTrace();     } }    }    static class outputThread implements Runnable{ @Override public void run() {     String msg="hello world";     try {  outputStream.write(msg.getBytes());  System.out.println("管道输出流已写入===>"+msg);     } catch (IOException e) {  e.printStackTrace();     } }    }}

Condition通信(用到了显式锁)

注意:======notify和signal其实差不多,都是用来通知正在等待的线程的,但是notify是随机通知,signal是定向通知

public class conditionTest {    /     * 利用lock锁里面的condition实现线程通信     *     */    private static ReentrantLock reentrantLock=new ReentrantLock();    public static void main(String[] args) throws InterruptedException { Condition condition = reentrantLock.newCondition(); //得到condition对象 new Thread(new Runnable() {     @Override     public void run() {  try {      reentrantLock.lock();      //使用await和signal必须要锁起来,和wait/notify一样      System.out.println("正在等待=========await");      condition.await();      System.out.println("await结束");  }catch (Exception e){  }finally {      if(reentrantLock.isHeldByCurrentThread()){   reentrantLock.unlock();      }  }     } }).start(); Thread.sleep(200); //让await先执行,这里模拟一下延时 new Thread(new Runnable() {     @Override     public void run() {  try {      reentrantLock.lock();      //使用await和signal必须要锁起来,和wait/notify一样     condition.signal();//相当于notify//      condition.signalAll(); //相当于notifyAll      System.out.println("已通知===");  }catch (Exception e){  }finally {      if(reentrantLock.isHeldByCurrentThread()){   reentrantLock.unlock();      }  }     } }).start();    }}

ThreadLocal

public class threadLocalTest {    /     * ThreadLocal(注意:只能存储一对键值对)     * 原理:创建一个ThreadLocal容器。当我们往里面set值,他会把当前线程作为key去设置值     * 当我们通过get获取值,ThreadLocal底层会通过key值=Thread.currentThread,去找对应的value     * ===================     * 总的来说:也就是每个线程之间的threadLocal互不干扰,里面的值也独立     *     *     */    private static ThreadLocal<String> threadLocal=new ThreadLocal<>();    public static void main(String[] args) throws InterruptedException { threadLocal.set("主线程=====hello"); new Thread(new Runnable() {     @Override     public void run() {  threadLocal.set("子线程====world");  System.out.println(threadLocal.get());     } }).start(); Thread.sleep(10); System.out.println(threadLocal.get());    }}

Lock显式锁

我们使用Lock锁都是使用它的实现类,常用的:可重入锁,可重入读写锁(读锁、写锁)

注意:======显式锁最好在finally进行释放锁

synchronized和ReentrantLock都是可重入锁

ReentrantLock的使用

1.创建一个锁对象

private static ReentrantLock lock=new ReentrantLock(); //创建一个锁对象

我们进入ReentrantLock源码里面看看。

 public ReentrantLock() { sync = new NonfairSync(); //说明这个ReentrantLock默认是非公平锁,和synchronized一样。原因:公平锁会牺牲性能    }    /     * Creates an instance of {@code ReentrantLock} with the     * given fairness policy.     *     * @param fair {@code true} if this lock should use a fair ordering policy     */    public ReentrantLock(boolean fair) { //说明显式锁Lock可以去指定锁的公平性。通过构造方法去传入true或者false sync = fair ? new FairSync() : new NonfairSync();    }

其实显式锁可以synchronized差不多,只是synchronized是自动释放锁,lock是手动释放,lock对我们技术水平要求的比较高而已,因为释放锁不是随便释放的,错误的释放锁会导致程序受到很大的影响

public class reentrantLockTest1 {    /     * 可重入锁ReentrantLock     * @param args     */    private static ReentrantLock lock=new ReentrantLock(); //创建一个锁对象    private static int count=10;    public static void main(String[] args) { lockThread1 lockThread1 = new lockThread1(); Thread t1 = new Thread(lockThread1); Thread t2 = new Thread(lockThread1); t1.start(); t2.start();    }    static class lockThread1 implements Runnable{ @Override public void run() {     while (true){  lock.lock(); //给代码上锁  try {      if(count<=0){   System.out.println("程序结束======");   break;      }      count--;      System.out.println(Thread.currentThread().getName()+",count="+count);  }catch (Exception e){  }finally {      if(lock.isHeldByCurrentThread()){ //如果这个锁被当前线程拥有   lock.unlock(); //释放锁      }  }     }  }    }}

可重入锁特性

public class reentrantLockTest2 {    /     * 可重入锁的特性:假如A线程获得了锁1,此时锁1还没有被释放,这个线程又可以继续去获得这个锁。这就是锁的可重入性     * =====获得了多少个锁就要释放多少次     */    private static ReentrantLock lock=new ReentrantLock();    public static void main(String[] args) { lockThread2 lockThread2 = new lockThread2(); Thread t1 = new Thread(lockThread2); Thread t2 = new Thread(lockThread2); t1.start(); t2.start();    }    static class lockThread2 implements Runnable{ @Override public void run() {     try {  lock.lock();  System.out.println("获得了锁lock");  lock.lock();  System.out.println("再次获得锁lock");     }catch (Exception e){     }finally {  if(lock.isHeldByCurrentThread()){      lock.unlock(); //获得了多少个锁就要释放多少次      lock.unlock();  }     } }    }}

tryLock方法(解决死锁)

tryLock方法可以有效的解决死锁,因为他会在得不到锁的时候放弃获取,死锁的原因就是互相持有对方想要的锁,而都不肯释放

进入reentrantLock找到trylock构造方法

public boolean tryLock() { return sync.nonfairTryAcquire(1);    }public boolean tryLock(long timeout, TimeUnit unit)     throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(timeout));    }

这两个方法的区别是,第一个不加参数的意思就是尝试获取锁,如果获取不到立刻放弃获取。第二个会等待指定时间,如果过了指定时间还是没有获取到这个锁的使用权,则放弃获取

public class trylockTest {    private static ReentrantLock lock=new ReentrantLock(false);    private static int count=20;    public static void main(String[] args) { tryLockThread tryLockThread = new tryLockThread(); Thread t1 = new Thread(tryLockThread); Thread t2 = new Thread(tryLockThread); t1.start(); t2.start();    }    static class tryLockThread implements Runnable{ @Override public void run() {     try { out:  while (true){      boolean flag = lock.tryLock(); //返回值就是获取到该锁没有     if(count<=0)  break ;if(flag){    count--;    System.out.println(Thread.currentThread().getName()+"获得锁了==="+count);}else {    System.out.println(Thread.currentThread().getName()+"没有获得锁");}  }     }catch (Exception e){     }finally {  if(lock.isHeldByCurrentThread()){      lock.unlock();  }     } }    }}
public class trylockTest {    private static ReentrantLock lock=new ReentrantLock(false);    public static void main(String[] args) { tryLockThread tryLockThread = new tryLockThread(); Thread t1 = new Thread(tryLockThread); Thread t2 = new Thread(tryLockThread); t1.start(); t2.start();    }    static class tryLockThread implements Runnable{ @Override public void run() {     try {  boolean flag = lock.tryLock(1, TimeUnit.SECONDS);  System.out.println(Thread.currentThread().getName()+"==="+flag);  Thread.sleep(1001);     }catch (Exception e){     }finally {  if(lock.isHeldByCurrentThread()){      lock.unlock();  }     } }    }}

读写锁(ReadWriteLock)

读读共享

只有读锁时,是共享的,也就是可以同时多个线程进入readLock内

public class readLockTest {    /     * 只有读锁时,是共享的,也就是可以同时多个线程进入readLock内     */    //获取读锁    private static ReentrantReadWriteLock.ReadLock readLock=new ReentrantReadWriteLock().readLock();    public static void main(String[] args) { readThread1 readThread1 = new readThread1(); Thread t1 = new Thread(readThread1); Thread t2 = new Thread(readThread1); Thread t3 = new Thread(readThread1); t1.start(); t2.start(); t3.start();    }    static class readThread1 implements Runnable{ @Override public void run() {     try {  readLock.lock();  System.out.println(Thread.currentThread().getName()+"===>"+System.currentTimeMillis());  Thread.sleep(1000);  System.out.println("睡眠结束");     }catch (Exception e){     }finally {  readLock.unlock();     } }    }}
写写互斥

写锁就相当于互斥锁(synchronized、lock)

public class writeLockTest {    /     * 写写互斥(写锁就相当于互斥锁(synchronized、lock))     */    //创建写锁    private static ReentrantReadWriteLock.WriteLock writeLock=new ReentrantReadWriteLock().writeLock();    public static void main(String[] args) { writeLockThread writeLockThread = new writeLockThread(); Thread t1 = new Thread(writeLockThread); Thread t2 = new Thread(writeLockThread); Thread t3 = new Thread(writeLockThread); t1.start(); t2.start(); t3.start();    }    static class writeLockThread implements Runnable{ @Override public void run() {     try {  writeLock.lock();  System.out.println(Thread.currentThread().getName()+"====>"+System.currentTimeMillis());  Thread.sleep(200);  System.out.println("睡眠结束");     } catch (Exception e) {  e.printStackTrace();     }finally {  if(writeLock.isHeldByCurrentThread()){      writeLock.unlock();  }     } }    }}
读写互斥

同时有读锁和写锁就会互斥

public class readWriteLockTest {    /     * 读写锁最好是一个线程用读锁,一个线程用写锁,要是多一个线程用锁,可能会失效,也就是不具有排他性     */    private static ReentrantReadWriteLock readWriteLock=new ReentrantReadWriteLock();    private static Lock readlock=null;    private static Lock writeLock=null;    static { readlock=readWriteLock.readLock(); writeLock=readWriteLock.writeLock();    }//    private static ReentrantReadWriteLock.ReadLock readlock=new ReentrantReadWriteLock().readLock();//    private static ReentrantReadWriteLock.WriteLock writeLock=new ReentrantReadWriteLock().writeLock();    public static void main(String[] args) { readWriteThread readWriteThread = new readWriteThread(); Thread t1 = new Thread(readWriteThread); t1.start(); try {     writeLock.lock();     System.out.println("===正在写===");     Thread.sleep(2000); } catch (Exception e) {     e.printStackTrace(); }finally {     System.out.println("===写完了===");     writeLock.unlock(); }    }    static class readWriteThread implements Runnable{ @Override public void run() {     try {  readlock.lock();  System.out.println(Thread.currentThread().getName()+"==读锁:"+System.currentTimeMillis());  Thread.sleep(2000);     } catch (Exception e) {  e.printStackTrace();     }finally {  System.out.println(Thread.currentThread().getName()+"===释放锁"+System.currentTimeMillis());  readlock.unlock();     } }    }    }

线程池ThreadPool

线程池的简单使用

固定大小的线程池

public class threadPoolTest01 {    /     * 线程池的作用:1.可以控制并发,通过设置线程池的线程数量     * 2.因为线程的创建和销毁是会耗性能的,线程池里面的线程是可以复用的,也就是假如线程池创建了1号2号线程。     * 里面有100个runnable方法,也就是要执行50次,一次2个线程去执行,在这过程中,线程1号2号不会被销毁     */    private static int i;    public static void main(String[] args) { //线程池的使用 //固定线程数量的线程池 ExecutorService executorService = Executors.newFixedThreadPool(2); for (int j = 0; j < 100; j++) {     executorService.execute(new Runnable() { //一个execute执行一个线程执行,所以我们为了演示,可以在外面用for  @Override  public void run() {      System.out.println(Thread.currentThread().getId()+"====>");      try {   Thread.sleep(500);      } catch (InterruptedException e) {   e.printStackTrace();      }  }     }); }  executorService.shutdown(); //关闭线程池    } }

缓存线程池和任务线程池

public class threadPoolTest02 {    public static void main(String[] args) {// ExecutorService executorService = Executors.newCachedThreadPool();// for (int i = 0; i < 20; i++) {//     executorService.execute(new Runnable() {//  @Override//  public void run() {//      System.out.println(Thread.currentThread().getId());//      try {//   Thread.sleep(100);//      } catch (InterruptedException e) {//   e.printStackTrace();//      }//  }//     });// }//// executorService.shutdown(); ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);// scheduledExecutorService.schedule(new Runnable() {//     @Override//     public void run() {//  System.out.println(Thread.currentThread().getId());////     }// },2, TimeUnit.SECONDS);    //推迟2秒执行线程方法 scheduledExecutorService.scheduleAtFixedRate(new Runnable() {     @Override     public void run() {  System.out.println(Thread.currentThread().getId());     } },5,1,TimeUnit.SECONDS); //这里的意思是,开始等5秒才会第一次执行这个方法,过后一直都是1秒执行一次    }}