面试中,多线程和并发编程已经是必不可少的了,我经常看到此类问题,当时也简单了解过,什么继承Thread类,实现Runnable接口,这些都被说烂了,知道这些当然是远远不够的,于是这几天搜索相关资料恶补了一下,为了方便后期复习,在此做个总结。 这个可以说是很原始的方式,就是继承Thread类,重写run方法。 由于Java是单继承的,所以这种方法用的比较少,一般都是用Runnable接口 这里给出几个常用的构造方法 我们也可以通过匿名内部类实现 还可以通过Lamata表达式实现 Callable接口可以接收返回值,可以抛出异常,重写的是call方法 另外还可以通过FutureTask适配器创建 当多个线程操作同一个资源的时候,就会出现线程安全问题。假设我们有100张票,在三个窗口同时卖,也就是我们有三个线程去买票。 同步代码块锁的是一个对象,即是需要变化的量 所谓死锁,也就是A拥有A资源的同时想要B的资源,B拥有B资源的同时想要A的资源。 可以看出,A需要B资源,B需要A资源,出现了死锁的状态。 Lock是一个接口,而不是一个关键字,他是一个显示锁,只能锁同步代码块,不能锁方法,可以显示加锁,释放锁,可以指定唤醒某一个线程,Lock锁有一个实现类是ReentrantLock,可重入锁(递归锁),在这里说一下,所有的锁都是可重入锁,也就是如果我们获得了外面的锁之后,会自动获取里面的锁。 比ReentrantLock更细的锁是,ReentrantReadWriteLock,这里有一个读锁,一个写锁, 如果不加锁的话,在写入完成之前会被其他线程插入 可以看出,在写入的时候,是一个写入,一个写入完成,在读取的时候,可能会有多个读取。 这是一个经典的线程通信问题,也就是不同线程之间有联系,生产者生产的东西放到缓冲区,如果缓冲区满了,生产者进入堵塞,缓冲区空了,消费者堵塞。 学习了Lock锁,这里的生产者消费者问题,可以通过Lock锁实现,可以指定唤醒某个线程,常用的方法是await,signal。 其中这里引入了Condition,他可以指定唤醒某个线程,这里我们演示三个线程ABC。 结果都是ABC、ABC… 一 先输出sendMsg,不要理解为是先调用了sendMsg,而是因为A线程先获得了锁。 这个例子,再次解释了是因为sendMsg先获得的锁,这里的synchronized锁的对象是调用者,这两个方法用的是同一把锁,谁先拿到,谁先执行 这里把call方法的synchronized去掉了,会先输出哪个呢,答案是先输出call,因为他不去获取锁资源,所以直接输出了 这里用两个phone对象,分别调用sendMsg和call方法,会先执行哪个,答案是先执行call,因为这里的锁锁的是对象,而他们不是同一个对象,所以资源不受影响。 这里是通过一个对象,去调用静态的同步方法,看看是先sendMsg还是call,答案是sentMsg,因为这里锁的是Class对象(只有一个) 这里是通过两个对象去调用sentMsg和call,结果是什么呢,答案还是先执行sentMsg,因为锁的是Class对象,所以不管是phone1还是phone2,都是属于Phone的Class对象。 这里锁的一个是静态同步方法,一个是普通同步方法,用一个对象对调用,结果是什么呢,答案是先call,因为静态同步方法锁的是Class对象,而普通同步方法锁的是调用者,锁的不是同一个东西。 这里用两个对象去调用静态同步方法sendMsg和普通同步方法call,会先输出哪个,答案是call,因为sendMsg锁的是Class,而call锁的是调用者,不是同一个一个东西 线程的创建跟Callable差不多,也是用ExecutorService 可以看出,在线程池中拿了三个线程 在定义线程池最大大小的时候,一般有两种策略CPU密集型和IO密集型,所谓CPU密集型,也就是,几核的CPU就定义为几,我的是八核,所以定义为8, 说起volatile,不难想出三大特性保证可见性,不保证原子性,禁止指令重排。 还有一个特性就是禁止指令重排,既然要禁止他,那么就得知道什么是指令重排,所谓指令重排,也就是,为了提高程序执行效率,寄存器对代码的优化,如果我们有一段代码如下 所谓指令重排,也就是在不影响单线程程序程序结果的情况下进行最优执行排序,可以看出,语句1和语句2的执行顺序并不会影响程序的结果,最终a的值为2,b的值为3。下面看这个代码 如果我们对c操作进行了volatile修饰,那么a和b依然可以指令重排,d和e也可以指令重排,ab和de不能指令重排了,c把他们分开了(专业术语是给c加了个内存屏障)。概述
继承Thread类
package com.hzy; public class Main { public static void main(String[] args) { new MyThread().start(); } } class MyThread extends Thread { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } } }
实现Runnable接口
先给出一个传统的方法实现package com.hzy; public class Main { public static void main(String[] args) { new Thread(new MyThread()).start(); new Thread(new MyThread(),"贺志营").start(); } } class MyThread implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } } }
package com.hzy; public class Main { public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } } }).start(); } }
package com.hzy; public class Main { public static void main(String[] args) { new Thread(()->{ for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } }).start(); } }
实现Callable接口
package com.hzy; import java.util.concurrent.*; public class Main { public static void main(String[] args) throws ExecutionException, InterruptedException { // 创建执行服务 ExecutorService service = Executors.newFixedThreadPool(3); // 提交执行 Future<Boolean> future1 = service.submit(new MyThread()); Future<Boolean> future2 = service.submit(new MyThread()); Future<Boolean> future3 = service.submit(new MyThread()); // 获取返回值 Boolean b1 = future1.get(); Boolean b2 = future2.get(); Boolean b3 = future3.get(); System.out.println(b1); System.out.println(b2); System.out.println(b3); // 关闭服务 service.shutdown(); } } class MyThread implements Callable<Boolean> { @Override public Boolean call() throws Exception { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } return true; } }
package com.hzy; import java.util.concurrent.*; public class Main { public static void main(String[] args) throws ExecutionException, InterruptedException { // 创建一个适配器 FutureTask futureTask = new FutureTask(new MyThread()); new Thread(futureTask,"A").start(); Boolean o = (Boolean) futureTask.get(); System.out.println(o); } } class MyThread implements Callable<Boolean> { @Override public Boolean call() throws Exception { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } return true; } }
线程的五大状态
创建状态
所谓的创建状态,也就是我们的new Thread();
就绪状态
所谓的就绪状态,就是我们myThread.start();
运行状态
运行状态就是我们的代码执行。
阻塞状态
当线程等待(wait)、同步(synchronized)、sleep和join的时候
死亡状态
run()结束、main()结束
线程常用方法
setPriority(int new Priority)
更改线程的优先级sleep(long millis)
让当前线程进入休眠join()
相当于插队,插入的线程执行结束后,被插队线程继续执行。yield()
线程礼让,暂停当前的线程,并把该线程状态转化为就绪状态interrupt()
中断线程(不建议用)isAlive()
判断线程是否处于存活状态多线程买票案例
package com.hzy; public class Main { public static void main(String[] args){ MyThread myThread = new MyThread(); new Thread(myThread,"窗口1").start(); new Thread(myThread,"窗口2").start(); new Thread(myThread,"窗口3").start(); new Thread(myThread,"窗口4").start(); new Thread(myThread,"窗口5").start(); } } class MyThread implements Runnable { private int ticket = 100; private boolean flag = true; @Override public void run() { while (flag) { buy(); } } public void buy() { if (ticket <= 0) { flag = false; return; } try { Thread.sleep(100);// 模拟买票延迟100ms } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":"+ ticket--); } }
可以看出,出现了线程安全问题,原因是在对ticket变为0之前,有多个线程同时进来。
解决办法,可以通过synchronized线程同步,可以使用同步方法和同步代码块进行解决。同步方法,也就是在方法上加一个synchronized关键字。package com.hzy; public class Main { public static void main(String[] args){ MyThread myThread = new MyThread(); new Thread(myThread,"窗口1").start(); new Thread(myThread,"窗口2").start(); new Thread(myThread,"窗口3").start(); } } class MyThread implements Runnable { private int ticket = 100; private boolean flag = true; @Override public void run() { while (flag) { buy(); } } public synchronized void buy() { if (ticket <= 0) { flag = false; return; } try { Thread.sleep(100);// 模拟买票延迟100ms } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":"+ ticket--); } }
package com.hzy; public class Main { public static void main(String[] args){ MyThread myThread = new MyThread(); new Thread(myThread,"窗口1").start(); new Thread(myThread,"窗口2").start(); new Thread(myThread,"窗口3").start(); } } class MyTicket { int ticket; public MyTicket(int ticket) { this.ticket = ticket; } } class MyThread implements Runnable { MyTicket myTicket = new MyTicket(100); private boolean flag = true; @Override public void run() { while (flag) { buy(); } } public void buy() { synchronized (myTicket) { if (myTicket.ticket <= 0) { flag = false; return; } try { Thread.sleep(100);// 模拟买票延迟100ms } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":"+ myTicket.ticket--); } } }
死锁
package com.hzy; public class Main { public static void main(String[] args){ A a = new A(); B b = new B(); new Thread(new MyThread(a,b,0)).start(); new Thread(new MyThread(a,b,1)).start(); } } class A { } class B { } class MyThread implements Runnable { private A a; private B b; private int choice; public MyThread(A a,B b,int choice) { this.a = a; this.b = b; this.choice = choice; } @Override public void run() { if (choice == 0) { synchronized (a) { System.out.println(Thread.currentThread().getName() + "获得" + a); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (b) { System.out.println(Thread.currentThread().getName() + "获得" + b); } } }else { synchronized (b) { System.out.println(Thread.currentThread().getName() + "获得" + b); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (a) { System.out.println(Thread.currentThread().getName() + "获得" + a); } } } } }
Lock锁
正常的,如果我们不加锁,会出现线程安全问题package com.hzy; public class Main { public static void main(String[] args){ MyThread myThread = new MyThread(); new Thread(myThread).start(); new Thread(myThread).start(); new Thread(myThread).start(); } } class MyThread implements Runnable { private int ticket = 100; @Override public void run() { while (ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(ticket --); } } }
可以通过RenntrantLock进行加锁,显示锁,记得解锁。package com.hzy; import java.util.concurrent.locks.ReentrantLock; public class Main { public static void main(String[] args){ MyThread myThread = new MyThread(); new Thread(myThread).start(); new Thread(myThread).start(); new Thread(myThread).start(); } } class MyThread implements Runnable { private int ticket = 100; // 定义lock锁 ReentrantLock lock = new ReentrantLock(); @Override public void run() { while (true) { lock.lock(); if (ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(ticket --); }else { break; } lock.unlock(); } } }
package com.hzy; import java.util.HashMap; import java.util.Map; public class Main { public static void main(String[] args) { MyReadWriteLock myReadWriteLock = new MyReadWriteLock(); for (int i = 0; i < 5; i++) { int temp = i; new Thread(()->{ myReadWriteLock.put(temp + "",temp + ""); },temp + "").start(); } for (int i = 0; i < 5; i++) { int temp = i; new Thread(()->{ myReadWriteLock.get(temp + ""); },temp + "").start(); } } } class MyReadWriteLock { private volatile Map<String,String> map = new HashMap<>(); public void put(String key,String value) { System.out.println(Thread.currentThread().getName() + "写入" + key); map.put(key,value); System.out.println(Thread.currentThread().getName() + "写入完成"); } public void get(String key) { System.out.println(Thread.currentThread().getName() + "读取" + key); map.get(key); System.out.println(Thread.currentThread().getName() + "读取完成"); } }
而我们想要的是,在写入的时候,只能是一个线程,而读取的时候,可以是多个线程。package com.hzy; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReentrantReadWriteLock; public class Main { public static void main(String[] args) { MyReadWriteLock myReadWriteLock = new MyReadWriteLock(); for (int i = 0; i < 5; i++) { int temp = i; new Thread(()->{ myReadWriteLock.put(temp + "",temp + ""); },temp + "").start(); } for (int i = 0; i < 5; i++) { int temp = i; new Thread(()->{ myReadWriteLock.get(temp + ""); },temp + "").start(); } } } class MyReadWriteLock { private volatile Map<String,String> map = new HashMap<>(); ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); public void put(String key,String value) { readWriteLock.writeLock().lock(); System.out.println(Thread.currentThread().getName() + "写入" + key); map.put(key,value); System.out.println(Thread.currentThread().getName() + "写入完成"); readWriteLock.writeLock().unlock(); } public void get(String key) { readWriteLock.readLock().lock(); System.out.println(Thread.currentThread().getName() + "读取" + key); map.get(key); System.out.println(Thread.currentThread().getName() + "读取完成"); readWriteLock.readLock().unlock(); } }
synchronized和Lock的区别
生产者消费者问题
常用的方法有wait(),notify(),notifyAll()
package com.hzy; import java.util.concurrent.locks.ReentrantLock; public class Main { public static void main(String[] args){ Buffer buffer = new Buffer(); new Thread(()->{ for (int i = 0; i < 100; i++) { System.out.println("生产者生产了" + i); buffer.put(); } },"生产者").start(); new Thread(()->{ for (int i = 0; i < 100; i++) { System.out.println("消费者消费了" + i); buffer.get(); } },"消费者").start(); } } // 缓冲区 class Buffer { private int len = 0; public synchronized void put() { if (len < 10) { len ++; } else { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } notifyAll(); } public synchronized void get() { if (len > 0) { len --; } else { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } notifyAll(); } }
这里给出缓冲区// 缓冲区 class Buffer { private int len = 0; Lock lock = new ReentrantLock(); Condition condition = lock.newCondition();// 该对象可以设置一些条件 public void put() { lock.lock(); if (len < 10) { len ++; } else { try { condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } } condition.signalAll(); lock.unlock(); } public void get() { lock.lock(); if (len > 0) { len --; } else { try { condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } } condition.signalAll(); lock.unlock(); } }
package com.hzy; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Main { public static void main(String[] args){ Buffer buffer = new Buffer(); new Thread(()->{ for (int i = 0; i < 10; i++) { buffer.a(); } },"A").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { buffer.b(); } },"B").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { buffer.c(); } },"C").start(); } } // 缓冲区 class Buffer { private int flag = 1;// 1执行A,2执行B,3执行C Lock lock = new ReentrantLock(); Condition condition1 = lock.newCondition();// 该对象可以设置一些条件 Condition condition2 = lock.newCondition();// 该对象可以设置一些条件 Condition condition3 = lock.newCondition();// 该对象可以设置一些条件 public void a() { lock.lock(); try { while (flag != 1) { condition1.await(); } System.out.println("A"); flag = 2; condition2.signal();// 指定唤醒B } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public void b() { lock.lock(); try { while (flag != 2) { condition2.await(); } System.out.println("B"); flag = 3; condition3.signal();// 指定唤醒C } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public void c() { lock.lock(); try { while (flag != 3) { condition3.await(); } System.out.println("C"); flag = 1; condition1.signal();// 指定唤醒A } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }
八锁问题
package com.hzy; import java.util.concurrent.TimeUnit; public class Main { public static void main(String[] args){ Phone phone = new Phone(); new Thread(()->{ phone.sendMsg(); }, "A").start(); try { TimeUnit.SECONDS.sleep(1);// 休息一秒 } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{ phone.call(); }, "B").start(); } } class Phone { public synchronized void sendMsg() { System.out.println("sendMsg"); } public synchronized void call() { System.out.println("call"); } }
二package com.hzy; import java.util.concurrent.TimeUnit; public class Main { public static void main(String[] args){ Phone phone = new Phone(); new Thread(()->{ phone.sendMsg(); }, "A").start(); try { TimeUnit.SECONDS.sleep(1);// 休息一秒 } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{ phone.call(); }, "B").start(); } } class Phone { public synchronized void sendMsg() { try { TimeUnit.SECONDS.sleep(3);// 休息一秒 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("sendMsg"); } public synchronized void call() { System.out.println("call"); } }
三package com.hzy; import java.util.concurrent.TimeUnit; public class Main { public static void main(String[] args){ Phone phone = new Phone(); new Thread(()->{ phone.sendMsg(); }, "A").start(); try { TimeUnit.SECONDS.sleep(1);// 休息一秒 } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{ phone.call(); }, "B").start(); } } class Phone { public synchronized void sendMsg() { try { TimeUnit.SECONDS.sleep(3);// 休息一秒 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("sendMsg"); } // public synchronized void call() { // System.out.println("call"); // } public void call() { System.out.println("call"); } }
四package com.hzy; import java.util.concurrent.TimeUnit; public class Main { public static void main(String[] args){ Phone phone1 = new Phone(); Phone phone2 = new Phone(); new Thread(()->{ phone1.sendMsg(); }, "A").start(); try { TimeUnit.SECONDS.sleep(1);// 休息一秒 } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{ phone2.call(); }, "B").start(); } } class Phone { public synchronized void sendMsg() { try { TimeUnit.SECONDS.sleep(3);// 休息一秒 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("sendMsg"); } public synchronized void call() { System.out.println("call"); } }
五package com.hzy; import java.util.concurrent.TimeUnit; public class Main { public static void main(String[] args){ Phone phone = new Phone(); new Thread(()->{ phone.sendMsg(); }, "A").start(); try { TimeUnit.SECONDS.sleep(1);// 休息一秒 } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{ phone.call(); }, "B").start(); } } class Phone { public static synchronized void sendMsg() { try { TimeUnit.SECONDS.sleep(3);// 休息一秒 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("sendMsg"); } public static synchronized void call() { System.out.println("call"); } }
六package com.hzy; import java.util.concurrent.TimeUnit; public class Main { public static void main(String[] args){ Phone phone1 = new Phone(); Phone phone2 = new Phone(); new Thread(()->{ phone1.sendMsg(); }, "A").start(); try { TimeUnit.SECONDS.sleep(1);// 休息一秒 } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{ phone2.call(); }, "B").start(); } } class Phone { public static synchronized void sendMsg() { try { TimeUnit.SECONDS.sleep(3);// 休息一秒 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("sendMsg"); } public static synchronized void call() { System.out.println("call"); } }
七 package com.hzy; import java.util.concurrent.TimeUnit; public class Main { public static void main(String[] args){ Phone phone = new Phone(); new Thread(()->{ phone.sendMsg(); }, "A").start(); try { TimeUnit.SECONDS.sleep(1);// 休息一秒 } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{ phone.call(); }, "B").start(); } } class Phone { public static synchronized void sendMsg() { try { TimeUnit.SECONDS.sleep(3);// 休息一秒 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("sendMsg"); } public synchronized void call() { System.out.println("call"); } }
八package com.hzy; import java.util.concurrent.TimeUnit; public class Main { public static void main(String[] args){ Phone phone1 = new Phone(); Phone phone2 = new Phone(); new Thread(()->{ phone1.sendMsg(); }, "A").start(); try { TimeUnit.SECONDS.sleep(1);// 休息一秒 } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{ phone2.call(); }, "B").start(); } } class Phone { public static synchronized void sendMsg() { try { TimeUnit.SECONDS.sleep(3);// 休息一秒 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("sendMsg"); } public synchronized void call() { System.out.println("call"); } }
线程池
package com.hzy; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Main { public static void main(String[] args){ ExecutorService service = Executors.newFixedThreadPool(10);// 创建固定个数线程 // ExecutorService service2 = Executors.newCachedThreadPool();// 创建动态线程(自适应大小) // ExecutorService service1 = Executors.newSingleThreadExecutor();// 创建单个线程 // 执行 service.execute(new MyThread()); service.execute(new MyThread()); service.execute(new MyThread()); // 关闭连接 service.shutdown(); } } class MyThread implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }
在阿里巴巴开发手册中有规定,创建线程池要用ThreadPoolExecutor
对于ThreadPoolExecutor的学习,就从七大参数,四种拒绝策略
int corePoolSize// 核心线程池大小 int maximumPoolSize// 最大核心线程池大小 long keepAliveTime// 超时存活时间 TimeUnit unit// 超时单位 BlockingQueue<Runnable> workQueue// 阻塞队列 ThreadFactory threadFactory// 线程工厂,用于创建线程 RejectedExecutionHandler handler// 拒绝策略
AbortPolicy());// 银行满了还有人进来,不处理,抛出异常(默认) CallerRunsPolicy();// 银行满了,不处理,哪里来的去哪里,一般抛给main线程 DiscardPolicy();// 银行满了,把该线程丢掉,不抛异常 DiscardOldestPolicy();// 银行满了,会和先来的线程竞争,不抛异常
package com.hzy; import java.util.concurrent.*; public class Main { public static void main(String[] args){ /** * 用一个银行的例子进行讲解这七大参数 */ ExecutorService threadPool = new ThreadPoolExecutor( 2,// 两个常开营业窗口 5,// 五个窗口,其中三个应急用 3,// 超时存货时间 TimeUnit.SECONDS,// 超时时间单位 new LinkedBlockingDeque<>(3),// 银行候客区大小 Executors.defaultThreadFactory(),// 默认线程池工厂 new ThreadPoolExecutor.AbortPolicy());// 银行满了还有人进来,不处理,抛出异常(默认) // new ThreadPoolExecutor.CallerRunsPolicy();// 银行满了,不处理,哪里来的去哪里,一般抛给main线程 // new ThreadPoolExecutor.DiscardPolicy();// 银行满了,把该线程丢掉,不抛异常 // new ThreadPoolExecutor.DiscardOldestPolicy();// 银行满了,会和先来的线程竞争,不抛异常 for (int i = 0; i < 8; i++) { threadPool.execute(()->{ System.out.println(Thread.currentThread().getName()); }); } // 关闭连接 threadPool.shutdown(); } }
Runtime.getRuntime().availableProcessors();// 获取CPU的核数
,IO密集型,就是判断程序中有多少个非常耗IO线程的程序,最大线程池的大小要大于这个值即可。volatile
在讲解volatile之前,需要讲解一个东西,Java内存模型(Java Memory Model,JMM),当线程读取内存中的一个变量时,会先把这个变量拷贝到CPU的高速缓存区,然后对其进行操作,操作完成后,会把该变量写入到内存中。在单线程中是不会出现任何问题的,但是在多线程中就会有问题,当线程1读取了该变量a=1到缓存区进行了加1操作,还没写到内存中,线程2读取了内存中的变量a=1也进行加1操作,然后线程1写入内存a=2,线程2也写入a=2到内存,那么最后,该变量的值是2,而不是3(出现了线程安全问题)。我们想要当线程1进行了加1操作之后,让线程2知道,这就是volatile的作用了,可以保证可见性,也就是,当线程1对a变量进行了加1操作,会直接写入到内存中(立即马上),并且通知线程2,变量被修改了,要求线程2缓冲区的值去内存中重新读取。但是,加1操作不是原子性的(三步,首先读取a变量的值,然后对其进行加1操作,然后赋值给a),也就是说,当线程1读取a变量到缓冲区后,还没有修改a的值,此时线程2进来了,读取了a的值,并且对其进行了加1操作,由于可见性,会把线程1缓冲区的值进行修改,但是,线程1中的CPU已经读取了缓冲区的值,而且是更新前的值,所以出现了线程安全问题,也是volatile不保证原子性的问题。于是就需要加1操作是原子性操作,于是就有了一个automic包,通过该包下的方法,可以实现加1的原子性操作(还有其他原子性操作)。
在原子操作里的自增,其实用的是自旋锁,也就是一直进行比较并交换。
说起原子性操作,其实得聊聊CAS(Compare And Swap,比较并交换),如果我们想要修改某个值num,那么我们可以通过一个方法compareAndSet(5,6)
意思是,我们期望num的值是5,如果是,就修改为6。但是这就会有一个问题,我们期望的num是5,如果有其他线程把5修改为了8,然后又修改为了5,最终是5,但是已经被修改过一次了,这就是ABA问题。我们可以通过AtomicStampedReference,也就是原子引用,在创建的时候,有一个印记,相当于版本号,每被修改一次,版本号都被更新,所以,当出现ABA问题的时候,我们就可以清楚的知道,被修改了多少次。
a = 1;// 语句1 b = 2;// 语句2 a = a + 1;// 语句3 b = a + 1;// 语句4
Instance instance = new Instance();
从微观的角度看,这条语句可以分解为三步,一是分配内存空间,二是初始化对象三是指向该内存空间,其中二和三会发生指令重排,如果在多线程的情况下,线程A分配了内存空间,并且执行了该内存空间(没有初始化对象),线程B进行了访问该内存空间,这就会出错了。而volatile就是禁止指令重排的,也并非绝对的禁止,假设我们有1、2、3、4、5条语句a = 1; b = 2; c = 3; d = 4; e = 5;
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算