多个线程操作同一个资源
并发 :
同一个对象
被
多个线程
同时操作
队列和锁
由于同一进程的多个线程共享同一块存储空间
,
在带来方便的同时
,
也带来了访问
冲突问题
,
为了保证数据在方法中被访问时的正确性
,
在访问时加入
锁机制
synchronized
,
- 一个线程持有锁会导致其他所有需要此锁的线程挂起 ;
- 在多线程竞争下 , 加锁 , 释放锁会导致比较多的上下文切换 和 调度延时,引 起性能问题 ;
- 如果一个优先级高的线程等待一个优先级低的线程释放锁 会导致优先级倒 置 , 引起性能问题 .
线程同步方法:synchronized 方法 和synchronized 块
1.synchronized 方法
synchronized
方法控制对
“
对象
”
的访问
,
每个对象对应一把锁
,
每个 synchronized方法都必须获得调用该方法的对象的锁才能执行
,
否则线程会阻塞
, 方法一旦执行 ,
就独占该锁
,
直到该方法返回才释放锁
,
后面被阻塞的线程才能获得这个锁 ,
继续执行
2、synchronized
块
同步块 : synchronized
(Obj ) { }
Obj
称之为
同步监视器
- Obj 可以是任何对象 , 但是推荐使用共享资源作为同步监视器
- 同步方法中无需指定同步监视器 , 因为同步方法的同步监视器就是this , 就是 这个对象本身 , 或者是 class [ 反射中讲解 ]
问题1、多线程抢票(synchronized 方法)
public class TestTicket {
public static void main(String[] args) {
GetTicket getTicket = new GetTicket();
Thread t1 = new Thread(getTicket,"小王");
Thread t2 = new Thread(getTicket,"小李");
Thread t3 = new Thread(getTicket,"黄牛");
t1.start();
t2.start();
t3.start();
}
}
class GetTicket implements Runnable{
private int nums =10;
private boolean flag = true; //设置线程停止条件
@Override
public void run() {
buyTicket();
}
//同步方法,锁的是this
private synchronized void buyTicket() {
//判断是否有票
while (flag) {
if (nums <= 0) {
flag = false;
return; //程序结束
}
//所有进程在此暂定一下,模拟延时
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//买票
System.out.println(Thread.currentThread().getName() + "抢到了第" + nums-- + "票");
}
}
}
问题2、取钱问题(synchronized 块)
public class GetMoney {
public static void main(String[] args) {
Account acc = new Account("基金", 1000);
DrawMoney drawMoney50 = new DrawMoney(acc, 50);
DrawMoney drawMoney100 = new DrawMoney(acc, 100);
new Thread(drawMoney50,"小王").start();
new Thread(drawMoney100,"小王的女朋友").start();
}
}
class Account{
String name;
int money;
public Account(String name, int money) {
this.name = name;
this.money = money;
}
}
class DrawMoney implements Runnable{
Account account;
private int getMoney;
public DrawMoney(Account account, int getMoney) {
this.account = account;
this.getMoney = getMoney;
}
@Override
public void run() {
//同步块
synchronized (account){
if(account.money-getMoney>=0){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money-=getMoney;
System.out.println(Thread.currentThread().getName()+"取出来"+getMoney);
}else System.out.println("账户余额不足");
}
System.out.println("账户余额"+account.money);
}
}
问题3、ArrayList
public class ArrayListAdd {
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<String>();
for (int i = 0; i < 50000; i++) {
Thread t1 = new Thread(
() -> {
synchronized (list) {
list.add(Thread.currentThread().getName());
}
}
);
t1.start();
}
//防止子线程未运行完毕,主线程运行到这里
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}