并发:多个线程同时操作同一个对象
线程同步
队列 + 锁 = 同步
为保证数据在方法中被访问的正确性,在访问时加入锁机制
,当一个线程获得资源的访问权时,会加上排他锁,独占资源,其他线程必须等待,使用后释放锁即可
但是就存在一些问题:
- 一个线程持有锁时会导致其他所有需要此锁的线程挂起
- 在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题
- 如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能问题
线程安全
在并发时保证数据的准确性,并且效率尽可能高
synchronized
两种用法:
- synchronized方法
- synchronized块
缺陷:
若将一个大方法声明为synchronized会大大影响效率
synchronized方法
package com.tsymq.thread.concurrnet;
public class Safe implements Runnable {
// 票数
private int tickets = 1000;
private boolean flag = true;
@Override
public void run() {
while (flag) {
qiangpiao();
}
}
private synchronized void qiangpiao(){
if (tickets <= 0) {
flag = false;
return;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
tickets--;
System.out.println(Thread.currentThread().getName() + "抢票,剩余票数:" + tickets);
}
public static void main(String[] args) {
System.out.println("main线程开启");
Safe huangniu = new Safe();
new Thread(huangniu, "携程").start();
new Thread(huangniu, "美团").start();
new Thread(huangniu, "去哪儿").start();
}
}
synchronized块
java中4种块:
- 普通块局部块
- class构造块
- 静态块
- 同步块
package com.tsymq.thread.concurrnet;
public class SafeDrawing {
public static void main(String[] args) {
// 账户
Account account = new Account(170, "礼金");
Drawing you = new Drawing(account, 80, "你");
Drawing she = new Drawing(account, 90, "她");
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 packetTotal; // 口袋余额
public Drawing(Account account, int drawingMoney, String name){
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
@Override
public void run() {
drawing();
}
private void drawing() {
if(account.money <= 0){
return;
}
// 应锁定account对象
synchronized (account){
if (account.money - drawingMoney < 0){
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money -= drawingMoney;
packetTotal += drawingMoney;
System.out.println(this.getName() + "账户余额:" + account.money);
System.out.println(this.getName() + "口袋余额:" + packetTotal);
}
}
}
使用synchronized块时一定要锁对东西,要锁的是一个不变的对象,对象在变和对象的属性在变是不一样的,而且要尽可能锁定合理的范围(不是代码的多少,是数据的完整性)