【JUC】手写多线程的题目小结
简单的线程池,体现线程的复用
不使用Executors.newFixedThreadPool(int)、Executors.newSingleThreadExecutor()、Executors.newCachedThreadPool(),而是通过ThreadPoolExecutor的7个参数的构造函数来创建线程池。不使用的原因写在:https://www.cnblogs.com/xdcat/p/12981188.html。
1 public static void selfThreadPool() { 2 Runtime.getRuntime().availableProcessors(); 3 ExecutorService threadPool = new ThreadPoolExecutor( 4 2,//corePoolSize 5 5,//maximumPoolSize 6 1L,//keepAliveTime 7 TimeUnit.SECONDS,new LinkedBlockingDeque<>(3), 8 Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy()); 9 try { 10 for (int i = 1; i <= 10; i++) { 11 final int tmp = i; 12 threadPool.execute(()->{ 13 System.out.println(Thread.currentThread().getName()+"线程"+",执行任务"+tmp); 14 }); 15 } 16 }catch (Exception e){ 17 e.printStackTrace(); 18 }finally { 19 threadPool.shutdown(); 20 } 21 }
输出结果
pool-2-thread-1执行任务0 pool-2-thread-4执行任务3 pool-2-thread-1执行任务5 pool-2-thread-2执行任务1 pool-2-thread-3执行任务2 pool-2-thread-2执行任务8 pool-2-thread-1执行任务7 pool-2-thread-4执行任务6 pool-2-thread-5执行任务4 pool-2-thread-3执行任务9
消费者和生产者模式
使用传统的锁来实现,还可以通过阻塞队列实现,阻塞队列的实现见https://www.cnblogs.com/xdcat/p/12958057.html
1 /** 2 * 一个初始值为零的变量,两个线程对其交替操作,一个加1一个减1, 3 */ 4 class Cakes { 5 private int cakeNumber = 0; 6 private Lock lock = new ReentrantLock(); 7 private Condition condition = lock.newCondition(); 8 public void increment() throws InterruptedException { 9 lock.lock(); 10 try{ 11 //判断 (多线程判断用while) 12 while(cakeNumber != 0){ 13 //等待 不能生产 14 condition.await(); 15 } 16 //进行操作(生产蛋糕) 17 cakeNumber++; 18 System.out.println(Thread.currentThread().getName()+"烹饪" + cakeNumber+"个蛋糕"); 19 //通知唤醒 20 condition.signalAll(); 21 }catch(Exception e){ 22 e.printStackTrace(); 23 }finally{ 24 lock.unlock(); 25 } 26 } 27 28 public void decrement() throws InterruptedException { 29 lock.lock(); 30 try{ 31 //判断 (多线程判断用while) 32 while(cakeNumber ==0){ 33 //等待 不能消费 34 condition.await(); 35 } 36 //进行操作 37 cakeNumber--; 38 System.out.println(Thread.currentThread().getName()+"吃完蛋糕,还剩" + cakeNumber+"个蛋糕"); 39 //通知唤醒 40 condition.signal(); 41 }catch(Exception e){ 42 e.printStackTrace(); 43 }finally{ 44 lock.unlock(); 45 } 46 } 47 } 48 public class ProdConsumerTraditionDemo { 49 public static void main(String[] args) { 50 Cakes cake = new Cakes(); 51 new Thread(()->{ 52 for (int i = 0; i < 5; i++) { 53 try { 54 cake.increment(); 55 } catch (InterruptedException e) { 56 e.printStackTrace(); 57 } 58 } 59 },"厨师").start(); 60 new Thread(()->{ 61 for (int i = 0; i < 5; i++) { 62 try { 63 cake.decrement(); 64 } catch (InterruptedException e) { 65 e.printStackTrace(); 66 } 67 } 68 },"顾客").start(); 69 } 70 }
实现自旋锁
尝试获取锁的线程不会阻塞,而是采用循环的方式去获取锁,好处是减少了线程上下文切换的消耗,缺点是循环会消耗CPU。
1 public class SpinLockDemo { 2 //原子引用线程 3 AtomicReference<Thread> atomicReference = new AtomicReference<>(); 4 5 public void myLock(){ 6 Thread thread = Thread.currentThread();
8 while(!atomicReference.compareAndSet(null,thread)){ 9 //匹配则不进入循环 获取锁后!=null进入循环 释放锁后被set为null退出循环
16 }
18 } 19 20 public void myUnlock(){ 21 Thread thread = Thread.currentThread(); 22 atomicReference.compareAndSet(thread,null);
24 } 25 public static void main(String[] args) { 26 SpinLockDemo spinLockDemo = new SpinLockDemo(); 27 new Thread(()->{ 28 spinLockDemo.myLock();
35 spinLockDemo.myUnlock(); 36 },"A").start();
42 new Thread(()->{ 43 spinLockDemo.myLock();
50 spinLockDemo.myUnlock(); 51 },"B").start(); 52 } 53 }
实现阻塞队列
1 public class MyBlockQueue { 2 private List<Integer> queue = new ArrayList<>();//用数组实现的话定义索引变量就可以 3 private volatile int curSize; 4 private volatile int capacity; 5 private Lock lock = new ReentrantLock(); 6 private final Condition notFull = lock.newCondition();//不满 7 private final Condition notNull = lock.newCondition();//不空 8 9 public MyBlockQueue(int capacity) { 10 this.capacity = capacity; 11 } 12 13 public void add(int value){ 14 lock.lock(); 15 try{ 16 while(curSize == capacity){ 17 //等待队列不满 18 notFull.await(); 19 } 20 queue.add(value); 21 ++curSize; 22 // System.out.println("入队成功"); 23 notNull.signal();//通知队列不为空 24 }catch(Exception e){ 25 e.printStackTrace(); 26 }finally{ 27 lock.unlock(); 28 } 29 } 30 31 public Integer take(){ 32 lock.lock(); 33 Integer x = null; 34 try{ 35 while(curSize == 0){ 36 //等待队列不为空 37 notNull.await(); 38 } 39 x = queue.remove(0); 40 curSize--; 41 notFull.signal(); 42 }catch(Exception e){ 43 e.printStackTrace(); 44 }finally{ 45 lock.unlock(); 46 } 47 return x; 48 } 49 50 public static void main(String[] args) { 51 MyBlockQueue queue = new MyBlockQueue(5); 52 new Thread(()->{ 53 for (int i = 0; i < 10; i++) { 54 System.out.println(Thread.currentThread().getName()+"消费"+queue.take()); 55 } 56 }).start(); 57 new Thread(()->{ 58 for (int i = 0; i < 10; i++) { 59 queue.add(i); 60 System.out.println(Thread.currentThread().getName()+"生产"+i); 61 } 62 63 }).start(); 64 } 65 }
多线程交替打印ABC
1 import java.util.concurrent.locks.Condition; 2 import java.util.concurrent.locks.Lock; 3 import java.util.concurrent.locks.ReentrantLock; 4 10 class Plat{ 11 private int id = 1; // 编号 12 private Lock lock = new ReentrantLock(); 13 private Condition a = lock.newCondition(); 14 private Condition b = lock.newCondition(); 15 private Condition c = lock.newCondition(); 16 public void printA(){ 17 lock.lock(); 18 try{ 19 while (id != 1){ 20 a.await(); 21 } 22 System.out.println("A"); 23 id = 2; 24 b.signal(); 25 }catch(Exception e){ 26 e.printStackTrace(); 27 }finally{ 28 lock.unlock(); 29 } 30 } 31 public void printB(){ 32 lock.lock(); 33 try{ 34 while (id != 2){ 35 b.await(); 36 } 37 System.out.println("B"); 38 id = 3; 39 c.signal(); 40 }catch(Exception e){ 41 e.printStackTrace(); 42 }finally{ 43 lock.unlock(); 44 } 45 } 46 public void printC(){ 47 lock.lock(); 48 try{ 49 while (id != 3){ 50 c.await(); 51 } 52 System.out.println("C"); 53 id = 1; 54 a.signal(); 55 }catch(Exception e){ 56 e.printStackTrace(); 57 }finally{ 58 lock.unlock(); 59 } 60 } 61 62 } 63 public class PrintAbc { 64 public static void main(String[] args) throws InterruptedException { 65 Plat plat = new Plat(); 66 for (int i = 0; i < 3; i++) { 67 new Thread(()->{ 68 plat.printA(); 69 }).start(); 70 new Thread(()->{ 71 plat.printB(); 72 }).start(); 73 new Thread(()->{ 74 plat.printC(); 75 }).start(); 76 } 77 } 78 }
两个线程实现交叉打印1-10000
同步代码块实现
1 public class PrintOneThousand { 2 private static volatile Integer counter = 0; 3 private static Object monitor = new Object(); 4 5 public static void main(String[] args) { 6 new Thread(()->{ 7 while (true){ 8 synchronized (monitor){ 9 if (counter % 2 != 0){ 10 continue; 11 } 12 int i = ++counter; 13 if (i > 1000){ 14 return; 15 } 16 System.out.println("奇数线程:" + i); 17 try { 18 monitor.notify(); 19 monitor.wait(); 20 } catch (InterruptedException e) { 21 e.printStackTrace(); 22 } 23 } 24 } 25 }).start(); 26 27 new Thread(()->{ 28 while (true){ 29 synchronized (monitor){ 30 if (counter % 2 == 0){ 31 continue; 32 } 33 int i = ++counter; 34 if (i > 1000){ 35 return; 36 } 37 System.out.println("偶数线程:" + i); 38 try { 39 monitor.notify(); 40 monitor.wait(); 41 } catch (InterruptedException e) { 42 e.printStackTrace(); 43 } 44 } 45 } 46 }).start(); 47 } 48 }
volatitle实现
public class PrintOneThousand{ private static volatile boolean loopForOdd = true; private static volatile boolean loopForEven = true; private static volatile int counter = 1; private static boolean flag = true; public static void main(String[] args) throws InterruptedException { // 先启动奇数线程 loopForOdd = false; new Thread(()->{ while (flag) { while (loopForOdd) { } int counter = PrintOneThousand.counter; if (counter > 100) { flag=false; break; } System.out.println("奇数线程:" + counter); PrintOneThousand.counter++; // 修改volatile,通知偶数线程停止循环,同时,准备让自己陷入循环 loopForEven = false; loopForOdd = true; } }).start(); new Thread(()->{ while (flag) { while (loopForEven) { } int counter = PrintOneThousand.counter; if (counter > 100) { flag=false; break; } System.out.println("偶数线程:" + counter); PrintOneThousand.counter++; // 修改volatile,通知奇数线程停止循环,同时,准备让自己陷入循环 loopForOdd = false; loopForEven = true; } }).start(); } }