文章目录

  • 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多线程 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秒执行一次



    }
}