一、交替执行
建立3个线程,完成交替执行,按照123123123…打印
1.1 使用Lock的ReentrantLock实现
public class ThreadPrint { public static void main(String[] args) { Print123 print123 = new Print123(); new Thread(()->{ while (true){ print123.print1(); } }).start(); new Thread(()->{ while (true){ print123.print2(); } }).start(); new Thread(()->{ while (true){ print123.print3(); } }).start(); } } // 线程资源类 class Print123{ private int number = 1; private Lock lock = new ReentrantLock(); private Condition con1 = lock.newCondition(); private Condition con2 = lock.newCondition(); private Condition con3 = lock.newCondition(); public void print1(){ lock.lock(); try{ if (number!=1){ con1.await(); } System.out.println(1); Thread.sleep(1000); number = 2; con2.signal(); }catch (Exception e){ System.out.println("1发生异常!"); }finally { lock.unlock(); } } public void print2(){ lock.lock(); try{ if (number!=2){ con2.await(); } System.out.println(2); Thread.sleep(1000); number = 3; con3.signal(); }catch (Exception e){ System.out.println("2发生异常!"); }finally { lock.unlock(); } } public void print3(){ lock.lock(); try{ if (number!=3){ con3.await(); } System.out.println(3); Thread.sleep(1000); number = 1; con1.signal(); }catch (Exception e){ System.out.println("3发生异常!"); }finally { lock.unlock(); } } }
1.2 使用Semaphore交替执行1
每次个线程执行时,先获取唯一的许可,继而打印自己的value值;之后再将许可释放给下一个信号量,让下一个信号量打印value……
public class Print123WithSemaphore { public static void main(String[] args) { Print123With pring = new Print123With(); new Thread(() -> pring.print1()).start(); new Thread(() -> pring.pring2()).start(); new Thread(() -> pring.print3()).start(); } } class Print123With { //定义三个信号量,并且这三个信号量一共只有1个许可证 private Semaphore semaphore1 = new Semaphore(1); private Semaphore semaphore2 = new Semaphore(0); private Semaphore semaphore3 = new Semaphore(0); public void print1() { print("1", semaphore1, semaphore2); } public void pring2() { print("2", semaphore2, semaphore3); } public void print3() { print("3", semaphore3, semaphore1); } /* value:打印的字符 currentSemaphore:当前信号量 nextSemaphore:下一个信号量 */ private void print(String value, Semaphore currentSemaphore, Semaphore nextSemaphore) { for (int i = 0; i < 10; ) { try { currentSemaphore.acquire(); System.out.println(Thread.currentThread().getName() +" print "+ value); Thread.sleep(1000); i++; nextSemaphore.release(); } catch (InterruptedException e) { e.printStackTrace(); } } } }
1.3 Semaphore交替执行2
public class SemaphorePrint { private static Semaphore s1 = new Semaphore(1); private static Semaphore s2 = new Semaphore(0); private static Semaphore s3 = new Semaphore(0); public static void main(String[] args) { new Thread(()->{ while (true){ try{ s1.acquire(); System.out.println("A"); Thread.sleep(1000); }catch (Exception e){ System.out.println("异常"); }finally { s2.release(); } } }).start(); new Thread(()->{ while (true){ try{ s2.acquire(); System.out.println("B"); Thread.sleep(1000); }catch (Exception e){ System.out.println("异常"); }finally { s3.release(); } } }).start(); new Thread(()->{ while (true){ try{ s3.acquire(); System.out.println("C"); Thread.sleep(1000); }catch (Exception e){ System.out.println("异常"); }finally { s1.release(); } } }).start(); } }
二、同时并发执行
2.1 Semaphore实现
默认所有的线程都是阻塞的,调用semaphore构造方法,设置线程的并发数,然后通过acquire()方法允许同一时间只能有三个线程来执行,三个线程执行完毕后,就可以调用release()来释放一次可执行的机会,然后从其他的线程里面随机挑选一个来执行。
public class SemaphoreWithBingfa { public static void main(String[] args) { ExecutorService executor = Executors.newCachedThreadPool(); // 同一时间,只允许3个线程并发访问 Semaphore semp = new Semaphore(3); // 创建10个线程 for (int i = 0; i < 8; i++) { final int threadNo = i; //execute()方法的参数:重写了run()方法的Runnable对象 executor.execute(() -> { try { //同一时间,只能有3个线程获取许可去执行 semp.acquire(); System.out.println("得到许可并执行的线程: " + threadNo); Thread.sleep(1000); // 得到许可的线程执行完毕后,将许可转让给其他线程 semp.release(); } catch (InterruptedException e) { } } ); } // executor.shutdown(); } }
2.2 CyclicBarrier实现状态同时执行
使用这个CyclicBarrier,可以实现A,B,C全部就绪后,await()表示就绪,然后三者开始同时执行。
例如:当三个人的会议,必须等到三个人到齐后,再能开始进行执行开始。
public class CyclicBarrierTest { public static void main(String[] args) { ExecutorService executor = Executors.newCachedThreadPool(); //等待3个人全部到齐 才能开会 CyclicBarrier barrier = new CyclicBarrier(3); for (int i = 0; i < 3; i++) { //设置人员的编号 final int person = i; executor.execute(() -> { try { System.out.println(Thread.currentThread().getName() + "用户" + person + "到达会议室了。"); barrier.await(); //3个人都到达 线程全部开始执行 System.out.println("三个人到齐," + Thread.currentThread().getName() + "开始开会干活!!!"); } catch (Exception e) { } }); } executor.shutdown(); } }
2.3 CountDownLatch 实现倒数计时【闭锁】
主要用于确保,某个计算,某个任务,某个服务线程,等到以来的所有线程执行完毕后,再去执行。
例如:等所有人走后,班长再走去关灯。
public class CountDownLatchTest { public static void main(String[] args) { CountDownLatch countDownLatch = new CountDownLatch(10); for (int i = 0; i < 10; i++) { new Thread(()->{ try{ System.out.println(Thread.currentThread().getName()+"正在执行..."); }catch (Exception e){ }finally { countDownLatch.countDown(); } }).start(); } try{ countDownLatch.await(); }catch (Exception e){ } System.out.println("主线程执行..."); } }
三、ReadWriteLock读写锁实现
为了更好地解决多线程读写带来的并发问题,JUC提供了ReadWriteLock,分别为读和写进行加锁的操作。
例如:
t1线程获取读锁,t2也可以获取读锁中的内容
如果t1线程获得了写锁,其他线程不能申请写锁或者读锁,必须等待t1释放锁资源。
这一点有些像mysql中的X锁与S锁,排他锁与共享锁。
3.1 ReentrantReadWriteLock 实现读写分离
public class ReadWriteLockTest { private static ReentrantReadWriteLock readwrite = new ReentrantReadWriteLock(); public static void main(String[] args) { // 1.开启两个线程【同时进行读和写】 new Thread(()->{ myRead(Thread.currentThread()); mtWrite(Thread.currentThread()); },"t1").start(); new Thread(()->{ myRead(Thread.currentThread()); mtWrite(Thread.currentThread()); },"t2").start(); } // 1.读锁来锁定操作 static void myRead(Thread thread){ readwrite.readLock().lock(); try{ for (int i = 0; i < 10000; i++) { System.out.println(thread.getName()+"正在进行读操作"); } System.out.println(thread.getName()+"读操作完成"); }finally { readwrite.readLock().unlock(); } } // 2.写操作来进行锁定 static void mtWrite(Thread thread){ readwrite.writeLock().lock(); try{ for (int i = 0; i < 10000; i++) { System.out.println(thread.getName()+"正在进行写操作"); } System.out.println(thread.getName()+"写操作完成"); }finally { readwrite.writeLock().unlock(); } } }
3.2 CopyOnWrite 读写分离并发容器
同步容器 :Hashtable/Vector/Stack,早期设计中,并没有考虑并发的问题,因此在多出线程的情况下,是会出现ConcurrentModificationException异常的。
除了ConcurrnetHashMap,CopyOnWriteArrayList之外,JUC还提供了CopyOnWriteArraySet,ConcurrentLinkedQueue,PriorityBlockingQueue等并发容器类。
当向一个CopyOnWrite 容器中增加元素的时候,就会将容器复制一份。如:
1.容器复制一份,然后新的容器添加元素
2.增加元素后,在将引用指向新的容器,原容器被GC回收
CopyOnWrite 利用冗余实现了读写分离,适合用于【读多写少】的场景,复制比较消耗性能
四、生产者消费者
文中采用加锁的方式来实现生产者和消费者,如synchronized和Lock,
也可以【共享缓冲区】采用阻塞队列来实现BlockingQueue来实现,设置一个固定大小的缓冲区,达到最大值则无法生产,生产者进行阻塞,知道消费者消费后,小于阻塞队列的大小,则开始进行生产。
4.1 Synchronized来实现
//测试方法 public class ProducerWithConsumerWithSynchronized { public static void main(String[] args) { // 生产者和消费者共用同一个Car对象 Car car = new Car(); Producer pro = new Producer(car); Consumer con = new Consumer(car); //2个生产者 2个消费者 Thread pro1 = new Thread(pro); Thread pro2 = new Thread(pro); Thread con1 = new Thread(con); Thread con2 = new Thread(con); pro1.start(); pro2.start(); con1.start(); con2.start(); } } // 汽车资源类 class Car{ int cars; // 生产汽车的方法 public synchronized void productCar(){ try{ if (cars<20){ System.out.println("生产汽车:"+cars); cars++; notifyAll(); }else { wait(); } }catch (Exception e){} } // 消费汽车的方法 public synchronized void consumeCar(){ try{ if (cars>0){ System.out.println("销售汽车:"+cars); cars--; notifyAll(); }else { wait(); } }catch (Exception e){} } } //生产者 class Producer implements Runnable{ Car car; public Producer(Car car) { this.car = car; } @Override public void run() { while (true){ car.productCar(); } } } // 消费者 class Consumer implements Runnable{ Car car; public Consumer(Car car) { this.car = car; } @Override public void run() { while (true){ car.consumeCar(); } } }
4.2 Lock的ReentrantLock实现
class Cars{ int cars; private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); //生产车 public void productCar(){ lock.lock(); try{ if (cars<20){ System.out.println("生产车:"+cars); cars++; condition.signalAll(); }else { condition.await(); } }catch (Exception e){ }finally { lock.unlock(); } } // 消费车 public void consumeCar(){ lock.lock(); try{ if (cars>0){ System.out.println("消费车:"+cars); cars--; condition.signalAll(); }else { condition.await(); } }catch (Exception e){ }finally { lock.unlock(); } } }