线程 synchroized
synchroized 同步方法
由于我们可以通过 private关键字来保证数据对象只能被方法访问,所以我们只需要针对方法提岀一套机制,这套机制就是 synchronized 关键字。它包括两种用法 synchronized方法和 synchronized 块。
同步方法
public synchronized void method (int args) {
}
synchronized 方法控制对 “对象" 的访问。
每个对象对应一把锁,线程中使用 对象 调用 synchronized 方法 都必须获得 该对象的锁才能执行 synchronized 方法,否则线程会阻塞。方法一旦执行,就独占该锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行。
缺陷:若将一个大的方法申明为 synchronized 将会影响效率。
package com.gcbeen.thread;
// 安全买票
public class TestSafeBuyTicket {
public static void main(String[] args) {
SafeBuyTicket buyTicket = new SafeBuyTicket();
new Thread(buyTicket, "张三").start();
new Thread(buyTicket, "李四").start();
new Thread(buyTicket, "王五").start();
}
}
class SafeBuyTicket implements Runnable {
// 票
private int ticketNums = 10;
boolean flag = true;
@Override
public void run() {
// 买票
while (flag) {
try {
buy();
} catch (Exception e) {
e.printStackTrace();
}
}
}
// synchronized 同步方法
// 买票
private synchronized void buy() {
// 判断是否有票
if (ticketNums <= 0) {
flag = false;
return;
}
// 延迟
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 买票
System.out.println(Thread.currentThread().getName()
+ "拿到" + ticketNums--);
}
}
// 张三拿到10
// 张三拿到9
// 张三拿到8
// 张三拿到7
// 张三拿到6
// 张三拿到5
// 张三拿到4
// 张三拿到3
// 张三拿到2
// 王五拿到1
synchroized 同步块
同步块
synchronized (Obj) {
}
obj称之为同步监视器
Obj可以是任何对象,但是推存使用共享资源作为同步监视器。
同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身,或者是class。
同步监视器的执行过程
-
第一个线程访问,锁定同步监视器,执行其中代码;
-
第二个线程访问,发现同步监视器被锁定,无法访问;
-
第一个线程访问完毕,解锁同步监视器;
-
第二个线程访问,发现同步监视器没有锁,然后锁定并访问。
锁的对象就是变量,需要增删改查的对象
安全的取钱
package com.gcbeen.thread;
// 安全的取钱 同步块
public class TestSafeBank {
public static void main(String[] args) {
SafeAccount account = new SafeAccount(100, "养老基金");
SafeDrawing drawing = new SafeDrawing(account, 60, "夸克");
SafeDrawing same = new SafeDrawing(account, 60, "same");
drawing.start();
same.start();
}
}
class SafeAccount {
int money; // 余额
String cardName; // 卡名
public SafeAccount(int money, String cardName) {
this.money = money;
this.cardName = cardName;
}
}
class SafeDrawing extends Thread {
SafeAccount account; // 账户
int drawingMoney; // 取余额
int nowMoney; // 个人手里的钱
public SafeDrawing(SafeAccount account, int drawingMoney, String name) {
// super(name) = 父类构造方法(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;
}
try {
Thread.sleep(1000); // 放大问题的发生性
} catch (InterruptedException e) {
e.printStackTrace();
}
// 卡内金额 = 余额 - 个人手里的钱
account.money = account.money - drawingMoney;
// 个人手里的钱
nowMoney = nowMoney + drawingMoney;
System.out.println(account.cardName + "余额为:" + account.money);
// this.getName()==Thread.currentThread().getName()
System.out.println(this.getName() + "手里的钱:" + nowMoney);
}
}
}
// 养老基金余额为:40
// 夸克手里的钱:60
// same余额不足,不能进行取钱
方法里面需要操作共享资源的代码才需要加锁。锁太多浪费资源
线程安全的集合
package com.gcbeen.thread;
import java.util.ArrayList;
import java.util.List;
// 线程安全的集合 同步块
public class TestSafeList {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int k = 0; k < 3; k++) {
new Thread(() -> {
for (int i = 0; i < 10000; i++) {
synchronized (list) {
list.add(Thread.currentThread().getName());
}
}
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
// 30000
标签:account,同步,Thread,synchronized,线程,synchroized,public
From: https://www.cnblogs.com/gcbeen/p/16743654.html