最新网站开发建设教材,重庆seo公司,数据库与网站建设的关系,腾讯云 个人网站1 概念介绍并发#xff1a;同一个对象被多个线程同时操作#x1f4cc; 线程同步现实生活中#xff0c;我们会遇到“同一个资源#xff0c;多个人都想使用”的问题#xff0c;比如#xff0c;食堂排队打饭,每个人都想吃饭#xff0c;最天然的解决办法就是#xff0c;排队…1 概念介绍并发同一个对象被多个线程同时操作 线程同步现实生活中我们会遇到“同一个资源多个人都想使用”的问题比如食堂排队打饭,每个人都想吃饭最天然的解决办法就是排队一个个来。处理多线程问题时多个线程访问同一个对象并且某些线程还想修改这个对象这时候我们就需要线程同步线程同步其实就是一种等待机制多个需要同时访问此对象的线程进入这个对象的等待池形成队列等待前面线程使用完毕下一个线程再使用。 队列和锁队列队列等同于对象的等待池各线程于队列中进行等待锁保证队列的安全保证同一时间只有一个线程操作该对象。 Synchronized 方法/同步方法Sybchronized 机制控制对对象的访问每个对象对应一把锁每个 synchronized 方法都必须获取调用方法的对象的锁才能执行否则线程会阻塞方法一旦执行就独占该锁知道方法返回才释放锁后面被阻塞的线程才能获得锁继续执行存在以下问题一个线程持有锁会导致其他所有需要此锁的线程挂起在多线程竞争下加锁释放锁会导致比较多的上下文切换和调度延时引起性能问题如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置引起性能问题。2 线程不安全及解决2.1 同步方法 要点由于我们可以通过private关键字来保证数据对象只能被方法访问所以我们只需要针对方法提出一套机制这套机制就是synchronized关键字它包括两种用法synchronized方法 和synchronized块.同步方法 public synchronized void method(int args)同步代码块synchronizedobj{}Obj 称之为同步监视器obj 可以是任何对象但是推荐使用共享资源作为同步监视器同步方法中无需指定同步监视器因为同步方法的同步监视器就是 this这个对象本身同步监视器的执行过程第一个线程访问锁定同步监视器执行其中的代码第二个线程访问发现同步监视器被锁定无法访问第一个线程访问完毕解锁同步监视器第二个线程访问发现同步监视器没有锁然后锁定并访问2.1 多人购票 不安全代码public class UnsafeBuyTicket{public static void main(String[] args) {BuyTicket buyTicket new BuyTicket();new Thread(buyTicket, A).start();new Thread(buyTicket, B).start();new Thread(buyTicket, C).start();}
}class BuyTicket implements Runnable{//票数private int ticketNums 10;//外部停止方式boolean flag true;Overridepublic void run() {while (flag){buy();}}private void buy(){//判断是否有票if (ticketNums 0){flag false;return;}System.out.println(Thread.currentThread().getName()买到了第ticketNums--张票;);}
}控制台输出 可以看到输出混乱C和A拿到了同一张票甚至有时会出现负数这就是线程不安全。这是因为同一时刻两个线程进行统一操作内存控制不当会导致数据不一致 同步方法给buy方法加上了锁~控制台输出 可以看到很有顺序。2.2 银行取钱 不安全代码public class UnsafeBank {public static void main(String[] args) {Account account new Account(100,建设银行共同);new Thread(new Drawing(account, 50),A).start();new Thread(new Drawing(account, 100),B).start();}
}//1.创建账户信息
class Account {//余额int money;//卡名String name;public Account(int money,String name){this.money money;this.name name;}
}class Drawing implements Runnable{Account account;//取出的钱int drawingMoney;//现有的钱int nowMoney;public Drawing(Account account, int drawingMoney){this.account account;this.drawingMoney drawingMoney;}Overridepublic void run() {//判断有没有钱if (account.money - drawingMoney 0){System.out.println(Thread.currentThread().getName()操作:account.name账户没有那么多钱);return;}//延时保证两个线程都能抵达这try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//卡内余额 余额 - 取出的钱account.money account.money - drawingMoney;//现有的钱nowMoney nowMoney drawingMoney;System.out.println(Thread.currentThread().getName()操作:account.name账户余额为account.money);System.out.println(Thread.currentThread().getName()手里的钱为nowMoney);}
}控制台输出 可以看到两个线程都取了钱超出了限制条件不够取的话不能取线程不安全 同步代码块因为同步方法默认是锁this对象但是这里需要锁这个账户因此同步代码块记住锁的对象是变化的量即可。控制台输出 上锁成功2.3 ArrayList 不安全代码public class UnsafeArrayList {public static void main(String[] args) {ArrayListString arrayList new ArrayList();for (int i 0; i 10000; i) {new Thread(()- arrayList.add(Thread.currentThread().getName())).start();}System.out.println(arrayList.size());}
}控制台输出 与10000对不上说明线程名称加到集合中去的时候有冲突也是线程不安全。 同步代码块在lambda表达式中给ArrayList对象添加锁~控制台输出 上锁成功2.4 线程安全的集合这是Juc里边的集合自带线程安全功能底层也是用了锁。import java.util.concurrent.CopyOnWriteArrayList;public class JucList {public static void main(String[] args) {CopyOnWriteArrayListString list new CopyOnWriteArrayList();for (int i 0; i 10000; i) {new Thread(()-list.add(Thread.currentThread().getName())).start();}//增加延时方式main线程先结束不然输出不准确try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(list.size());}
}3 死锁和Lock锁 要点多个线程各自占有一些共享资源并且相互等待其他线程占用的资源才能运行而导致两个或两个以上的线程都在等待对方释放资源都停止执行的情形某一个同步块同时拥有两个对象以上的锁就有可能发生死锁 产生死锁的必要条件互斥条件一个线程只能被一个人使用请求与保持条件一个进程因请求资源而阻塞时对已获得的资源保持不放不剥夺条件: 进程已获得的资源在未使用完之前不能剥夺循环等待条件若干进程之间形成一种头尾相接的循环等待资源的关系3.1 死锁简单来说就是A线程拿到一个口号B线程拿到一个镜子然后他们都想再拿到对方的东西但是无法成功因为口号和镜子都被加锁了只有一个线程能用。//死锁多个线程互相抱着对方需要的资源然后形成僵持
public class DeadLock {public static void main(String[] args) {Thread t1 new Thread(new MakeUp(0,A));Thread t2 new Thread(new MakeUp(1,B));t1.start();t2.start();}
}//口红
class Lipstick{
}//镜子
class Mirror{
}class MakeUp implements Runnable{//需要的资源只有一份static Lipstick lipstick new Lipstick();static Mirror mirror new Mirror();int choice;//选择String name;//使用人public MakeUp(int choice, String name) {this.choice choice;this.name name;}Overridepublic void run() {try {makeup();} catch (InterruptedException e) {e.printStackTrace();}}//化妆互相持有对方的锁拿到对方的资源private void makeup() throws InterruptedException {if(choice0){synchronized (lipstick){System.out.println(this.name获取口红的锁);Thread.sleep(1000);synchronized (mirror){//一秒钟后获得镜子System.out.println(this.name获取镜子的锁);}}}else{synchronized (mirror){System.out.println(this.name获取镜子的锁);Thread.sleep(1000);synchronized (lipstick){//一秒钟后获得口红System.out.println(this.name获取口红的锁);}}}}
}控制台输出 程序无法结束这就是死锁。 解决死锁把同步代码块上锁的分开这样锁就能得到释放。3.2 Lock锁 要点Java 提供了更强大的线程同步机制—通过显式定义同步锁对象来实现同步同步锁使用 Lock 对象充当Lock 接口时控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问每次只能有一个线程对 Lock 对象加锁线程开始访问共享资源之前应先获得 Lokc 对象ReentrantLock 类可重用锁实现了 Lock, 他拥有 synchronized 相同的并发性和内存语义在实现线程安全的控制中比较常用的时 ReentrantLock可以显示枷锁释放锁 未加Lock锁时public class TestLock {public static void main(String[] args) {BuyTicket buyTicket new BuyTicket();new Thread(buyTicket).start();new Thread(buyTicket).start();new Thread(buyTicket).start();}
}class BuyTicket implements Runnable{int ticketNums10;Overridepublic void run() {while (true){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}if(ticketNums0){System.out.println(Thread.currentThread().getName()买到了票ticketNums--);}else {break;}}}
}控制台输出 加Lock锁后需要先定义ReentrantLock然后再try{}finally{}中分别加锁和解锁。public class TestLock {public static void main(String[] args) {BuyTicket buyTicket new BuyTicket();new Thread(buyTicket).start();new Thread(buyTicket).start();new Thread(buyTicket).start();}
}class BuyTicket implements Runnable{int ticketNums10;//定义lock锁private final ReentrantLock lock new ReentrantLock();Overridepublic void run() {while (true){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}try {lock.lock();//加锁if(ticketNums0){System.out.println(Thread.currentThread().getName()买到了票ticketNums--);}else {break;}} finally {lock.unlock();//解锁}}}
}控制台输出3.3 synchronized和Lock对比 要点Lock是显式锁手动开启和关闭锁别忘记关闭锁synchronized是隐式锁出了作用域自动释放Lock只有代码块锁synchronized有代码块锁和方法锁使用Lock锁JVM将花费较少的时间来调度线程性能更好。并且具有更好的扩展性提供更多的子类优先使用顺序Lock同步代码块已经进入了方法体分配了相应资源同步方法在方法体之外)