Java高并发之同步异步
1、概念理解:
2、同步的解决方案:
1).基于代码
synchronized 关键字
修饰普通方法:作用于当前实例加锁,进入同步代码前要获得当前实例的锁。
修饰静态方法:作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁。
修饰代码块:指定加锁对象,对给定对象加锁,进入同步代码块前要获得给定对象的锁。
code1
package com.thread; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * 同步方法 * @author Administrator * */ public class SynchronizedMethod implements Runnable{ //静态共享变量 i static int i = 0; /** * 自增 */ public synchronized void increase(){ i++; } @Override public void run() { for (int j = 0; j < 100; j++) { increase(); } } public static void main(String[] args) throws InterruptedException { SynchronizedMethod instance = new SynchronizedMethod(); ExecutorService executorService = Executors.newFixedThreadPool(2); for (int i = 0; i < 3; i++) { //同一实例,线程共享静态变量i // executorService.execute(instance); //不同实例,线程单独享有变量i,达不到同步目的 executorService.execute(new SynchronizedMethod()); /** * 由于线程执行时间过短,在不同实例下,可能会得到类似于同步的结果。 */ Thread.sleep(100); } executorService.shutdown(); System.out.println(i); //300 } }
code2
package com.thread; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * 同步代码块 * @author Administrator * */ public class SynchronizedCodeBlock implements Runnable{ //静态共享变量 i static int i = 0; @Override public void run() { //同步进来的对象 synchronized(this){ //SynchronizedCodeBlock.class for (int j = 0; j < 100; j++) { i++; } } } public static void main(String[] args) throws InterruptedException { SynchronizedCodeBlock instance = new SynchronizedCodeBlock(); ExecutorService executorService = Executors.newFixedThreadPool(2); for (int i = 0; i < 3; i++) { // executorService.execute(instance); executorService.execute(new SynchronizedCodeBlock()); Thread.sleep(10); } executorService.shutdown(); System.out.println(i); //300 } }
wait与notify运用
wait():使一个线程处于等待状态,并且释放所持有的对象的lock。
sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。
notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
notifyAll():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。
code3
package com.test; /** * 多线程实现 生产者-消费者模式 * 关键点:wait和notifyAll或者notify时机的运用,确保先生产后消费 * @author Administrator * */ public class Test { private static Integer count = 0; //数据仓库计数 private final Integer FULL = 5; //数据仓库最大存储量 private static String lock = "lock"; //锁标识 public static void main(String[] args) { Test t = new Test(); new Thread(t.new Producer()).start(); new Thread(t.new Consumer()).start(); new Thread(t.new Producer()).start(); new Thread(t.new Consumer()).start(); } //生产者 class Producer implements Runnable { @Override public void run() { for (int i = 0; i < 5; i++) { try { Thread.sleep(1000); } catch (InterruptedException e1) { e1.printStackTrace(); } synchronized (lock) { while (count == FULL) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } count++; System.out.println(Thread.currentThread().getName() + "produce:: " + count); //唤醒lock锁上的所有线程 lock.notifyAll(); } } } } //消费者 class Consumer implements Runnable { @Override public void run() { for (int i = 0; i < 5; i++) { try { Thread.sleep(1000); } catch (InterruptedException e1) { e1.printStackTrace(); } synchronized (lock) { //如果首次消费者竞争得到锁,进入后等待 while (count == 0) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } count--; System.out.println(Thread.currentThread().getName()+ "consume:: " + count); lock.notifyAll(); } } } } }
volatile实现线程同步
原理:volatile保证不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,新值对其他线程来说是立即可见的,并且禁止进行指令重排序。
注意:volatile不保证原子性,凡是不是原子性的操作,都不能保证可见性,也即不能保证同步
应用:
1)对变量的写操作不依赖于当前值 类似 i++、i=j 等操作 不能对 i 用volatile。解决办法:类似操作增加 synchronized、Lock、AtomicInteger
保证原子性。
2)该变量没有包含在具有其他变量的不变式中
常用在多线程状态标志 flag、
ReentrantLock重入锁
重入锁:外层函数获取锁后,内层函数依然有获取该锁的代码,则重入锁无需再次获取锁,即可进入内层代码
code1
package com.lock; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.locks.ReentrantLock; /** * 重入锁 * 外层函数获取锁后,内层函数依然有获取该锁的代码,则重入锁无需再次获取锁,即可进入内层代码 * ReentrantLock 和synchronized 都是 可重入锁 * @author Administrator * */ public class ReentranLockTest implements Runnable{ public static ReentrantLock lock = new ReentrantLock(); public static int i = 0; @Override public void run() { for (int j = 0; j < 10; j++) { lock.lock(); //加锁 try { i++; } finally { lock.unlock(); //释放锁 } } } public static void main(String[] args) throws InterruptedException { ReentranLockTest test = new ReentranLockTest(); ExecutorService executorService = Executors.newFixedThreadPool(2); for (int i = 0; i < 2; i++) { executorService.execute(test); Thread.sleep(1000); } executorService.shutdown(); System.out.println(i); } }
code2
package com.lock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 可中断的重入锁 * 条件中断等待 * @author Administrator * */ public class InterruptiblyLockTest { public static Lock lock = new ReentrantLock(); public void function(){ String tName = Thread.currentThread().getName(); try { System.out.println(tName + "-开始获取锁......"); lock.lockInterruptibly(); System.out.println("获取到锁了......"); Thread.sleep(10000); System.out.println("睡眠10秒后,开始干活......"); for (int i = 0; i < 5; i++) { System.out.println(tName + ":" + i); } System.out.println("活干完了......"); } catch (Exception e) { System.out.println(tName + "-我好像被人中断了!"); e.printStackTrace(); }finally { lock.unlock(); System.out.println(tName + "-释放了锁"); } } public static void main(String[] args) throws InterruptedException { InterruptiblyLockTest test = new InterruptiblyLockTest(); //定义两个线程 Thread t0 = new Thread() { public void run() { test.function(); } }; Thread t1 = new Thread() { public void run() { test.function(); } }; String tName = Thread.currentThread().getName(); System.out.println(tName + "-启动t0"); t0.start(); System.out.println(tName + "-等5秒,再启动t1"); Thread.sleep(5000); System.out.println(tName + "-启动t1"); t1.start(); //t0先占据了锁还在睡眠 System.out.println(tName + "-不等了,把t1中断掉!"); t1.interrupt(); //中断:只能中断处于等待锁的线程,不能中断已经获取锁的线程 } }
code3
package com.lock; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; /** * 尝试型锁 * 拒绝阻塞 * @author Administrator * */ public class TryLockTest { public ReentrantLock lock = new ReentrantLock(); /** * tryLock * 当前资源没有被占用,则tryLock获取锁 * 当前资源被当前锁占用,则tryLock返回true * 当前资源被其他线程占用,则tryLock返回false * @throws InterruptedException */ public void tryLockFunction() throws InterruptedException{ String tName = Thread.currentThread().getName(); if(lock.tryLock()){ try { System.out.println(tName + "-获取到锁了"); Thread.sleep(3000); System.out.println(tName + "工作了3秒钟......"); } finally { lock.unlock(); System.out.println(tName + "-释放锁"); } }else{ System.out.println(tName + "-无法获取到锁"); } } /** * tryLock(long timeout, TimeUnit unit) * timeout时间内尝试请求锁,请求到了则返回true,可被中断 * * @throws InterruptedException */ public void tryLockInterruptFunction() throws InterruptedException{ String tName = Thread.currentThread().getName(); SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd:hh:mm:ss"); System.out.println(tName + "-开始尝试获取锁,当前时间:"+format.format(new Date())); if(lock.tryLock(3,TimeUnit.SECONDS)){ try { System.out.println(tName + "-获取到锁了"); Thread.sleep(5000); System.out.println(tName + "工作了3秒钟......"); } finally { lock.unlock(); System.out.println(tName + "-释放锁"); } }else{ System.out.println(tName + "-无法获取到锁"); System.out.println(tName + "-结束尝试获取锁,当前时间:"+format.format(new Date())); } } public static void main(String[] args) { TryLockTest test = new TryLockTest(); new Thread("Lock-Thread1") { public void run() { try { test.tryLockFunction(); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); new Thread("Lock-Thread2") { public void run() { try { test.tryLockFunction(); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); //Lock-Thread1-获取到锁了 //Lock-Thread2-无法获取到锁 //Lock-Thread1工作了3秒钟...... //Lock-Thread1-释放锁 new Thread("LockInterrupt-Thread1") { public void run() { try { test.tryLockInterruptFunction(); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); new Thread("LockInterrupt-Thread2") { public void run() { try { test.tryLockInterruptFunction(); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); //LockInterrupt-Thread2-开始尝试获取锁,当前时间:2019-01-17:05:12:32 //LockInterrupt-Thread1-开始尝试获取锁,当前时间:2019-01-17:05:12:32 //LockInterrupt-Thread2-获取到锁了 //LockInterrupt-Thread1-无法获取到锁 //LockInterrupt-Thread1-结束尝试获取锁,当前时间:2019-01-17:05:12:35 //LockInterrupt-Thread2工作了3秒钟...... //LockInterrupt-Thread2-释放锁 } }
code4
package com.lock; import java.util.concurrent.locks.ReentrantLock; /** * 公平锁 * 按时间先后获取锁 * @author Administrator * */ public class FairLockTest { //创建一个公平锁 public static ReentrantLock lock = new ReentrantLock(true); public void fairLockFunction(){ while(true){ try { lock.lock(); System.out.println(Thread.currentThread().getName()+"获取到锁......"); } finally { lock.unlock(); } } } public static void main(String[] args) { FairLockTest test = new FairLockTest(); Thread t1 = new Thread("线程1") { public void run() { test.fairLockFunction(); } }; Thread t2 = new Thread("线程2") { public void run() { test.fairLockFunction(); } }; t1.start(); t2.start(); } //线程1获取到锁...... //线程2获取到锁...... //线程1获取到锁...... //线程2获取到锁...... //线程1获取到锁...... //线程2获取到锁...... //线程1获取到锁...... //线程2获取到锁...... }
ThreadLocal创建线程间变量副本
参考博客:聊一聊Spring中的线程安全性
final实现volatile可见性
通过final不可变性的特点,替代volatile的可见性。
参考博客:
https://blog.csdn.net/javazejian/article/details/72828483
https://www.cnblogs.com/duanxz/p/3709608.html?utm_source=tuicool&utm_medium=referral
http://www.cnblogs.com/duanxz/p/5066726.html
2).基于数据库