java多线程:线程同步
线程同步
- 并发:同一个对象被多个线程同时操作
- 线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用
- 线程同步形成条件:队列+锁
- 锁机制synchronized:当一个线程获得对象的排它锁独占资源,其他线程必须等待,使用后释放锁即可
- 锁机制存在以下问题:
- 一个线程持有锁会导致其他所有需要此锁的线程挂起
- 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换 和 调度延时,引起性能问题
- 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题
三大不安全案例
-
不安全买票
package com.wu.多线程.线程同步; //不安全的买票 public class UnsafeBuyTicket { public static void main(String[] args) { BuyTicket station = new BuyTicket(); new Thread(station,"aa").start(); new Thread(station,"bb").start(); new Thread(station,"cc").start(); } } class BuyTicket implements Runnable { private int ticketNums=10; private boolean flag=true;//外部停止方式 @Override public void run() { while (flag) { try { buy(); } catch (InterruptedException e) { e.printStackTrace(); } } } private void buy() throws InterruptedException { if (ticketNums <= 0) { flag=false; return; } //模拟延时 Thread.sleep(100); //买票 System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--); } } -
不安全地取钱
//不安全的取钱 //两个人去银行取钱 public class UnsafeBank { public static void main(String[] args) { //账户 Account account = new Account(100, "aa"); Drawing you = new Drawing(account, 50, "你"); Drawing she = new Drawing(account, 100, "她"); you.start(); she.start(); } } //账户 class Account{ int money;//余额 String name;//卡名 public Account(int money, String name) { this.money = money; this.name = name; } } //银行 模拟取款 class Drawing extends Thread{ Account account; int drawingMoney; int nowMoney; public Drawing(Account account, int drawingMoney, String name) { super(name); this.account = account; this.drawingMoney = drawingMoney; } @Override public void run() { if (account.money - drawingMoney < 0) { System.out.println(Thread.currentThread().getName()+"钱不够,取不了"); return; } //sleep可以放大问题的发生性 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } account.money-=drawingMoney; nowMoney+=drawingMoney; System.out.println(account.name+"账户余额:"+account.money); System.out.println(this.getName()+"手里的钱:"+nowMoney); } } //aa账户余额:50 //你手里的钱:50 //aa账户余额:-50 //她手里的钱:100 -
线程不安全的集合
package com.wu.多线程.线程同步; import java.util.ArrayList; import java.util.List; public class UnsafeList { public static void main(String[] args) { List<String> list = new ArrayList<String>(); for (int i = 0; i < 10000; i++) { new Thread(()->{ list.add(Thread.currentThread().getName()); }).start(); } System.out.println(list.size());//9963 } }
同步方法及同步块
-
针对方法提出一套机制:synchronized 关键字,包括两种方法
- synchronized 方法 (默认锁的是this)
- synchronized 块 (可以锁任何对象)
-
synchronized方法控制对象的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行
-
缺陷:若将一个大的方法申明为synchronized将会影响效率
-
方法里面需要修改的内容才需要锁,锁的太多,浪费资源
-
同步块:
synchronized(Obj){} -
Obj:同步监视器
- Obj可以是任何对象,但是推荐使用共享资源作为同步监视器
- 同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身,或者是class
-
同步监视器的执行过程
- 第一个线程访问,锁定同步监视器,执行其中代码
- 第二个线程访问,发现同步监视器被锁定,无法访问
- 第一个线程访问完毕,解锁同步监视器
- 第二个线程访问,发现同步监视器没有锁,然后锁定并访问
-
之前的三个例子:
//不安全的买票 加锁 public class UnsafeBuyTicket { public static void main(String[] args) { BuyTicket station = new BuyTicket(); new Thread(station,"aa").start(); new Thread(station,"bb").start(); new Thread(station,"cc").start(); } } class BuyTicket implements Runnable { private int ticketNums=10; private boolean flag=true;//外部停止方式 @Override public void run() { while (flag) { try { //模拟延时 Thread.sleep(1000); buy(); } catch (InterruptedException e) { e.printStackTrace(); } } } //synchronized 同步方法,锁的是this private synchronized void buy(){ if (ticketNums <= 0) { flag=false; return; } //买票 System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--); } }//两个人去银行取钱 public class UnsafeBank { public static void main(String[] args) { //账户 Account account = new Account(100, "aa"); Drawing you = new Drawing(account, 50, "你"); Drawing she = new Drawing(account, 100, "她"); you.start(); she.start(); } } //账户 class Account{ int money;//余额 String name;//卡名 public Account(int money, String name) { this.money = money; this.name = name; } } //银行 模拟取款 class Drawing extends Thread{ Account account; int drawingMoney; int nowMoney; public Drawing(Account account, int drawingMoney, String name) { super(name); this.account = account; this.drawingMoney = drawingMoney; } @Override public void run() { //锁的对象就是变化的量,就是增删改的对象 synchronized (account){ if (account.money - drawingMoney < 0) { System.out.println(Thread.currentThread().getName()+"钱不够,取不了"); return; } //sleep可以放大问题的发生性 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } account.money-=drawingMoney; nowMoney+=drawingMoney; System.out.println(account.name+"账户余额:"+account.money); System.out.println(this.getName()+"手里的钱:"+nowMoney); } } } //aa账户余额:50 //你手里的钱:50 //她钱不够,取不了public class UnsafeList { public static void main(String[] args) { List<String> list = new ArrayList<String>(); for (int i = 0; i < 10000; i++) { new Thread(()->{ synchronized (list) { list.add(Thread.currentThread().getName()); } }).start(); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size());//10000 } }
测试JUC安全类型的集合
import java.util.concurrent.CopyOnWriteArrayList;
//测试JUC安全类型的集合
public class TestJUC {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());//10000
}
}

浙公网安备 33010602011771号