线程同步synchronized锁(同步方法)
1.并发
并发∶同一个对象被多个线程同时操作
2.线程同步
处理多线程问题时,多个线程访问同一个对象﹐并且某些线程还想修改这个对象.这时候我们就需要线程同步﹒线程同步其实就是一种等待机制﹐多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用。
3.队列和锁
为了解决安全性 锁机制 (synchronized)
4.线程安全
-
线程不安全三个例子
/ 不安全取钱
// 两个人去取钱
public class UnSafeBank {
public static void main(String[] args) {
// 账户
Account account = new Account(100,"结婚存款");
Drawing you = new Drawing(account,50,"你");
Drawing girlFriend = new Drawing(account,100,"girlFriend");
new Thread(you).start();
new Thread(girlFriend).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 noMoney;
public Drawing(Account account,int drawingMoney,String name){
super(name);
this.account =account;
this.drawingMoney =drawingMoney;
}
// 取钱
public void run(){
//判断有没有钱
if(account.money-drawingMoney<0){
System.out.println(Thread.currentThread().getName()+"钱不够,取不了");
return;
}
// Thread.sleep(100);造成线程不安全
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 卡内余额 = 余额 - 取出的钱
account.money = account.money -drawingMoney;
// 手里的钱
noMoney = noMoney + drawingMoney;
System.out.println(account.name+"余额位:"+account.money);
// Thread.currentThread().getName() = this.getName();
System.out.println(this.getName()+"手里的钱:"+noMoney);
}
}// 不安全买票
// 线程不安全
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket station = new BuyTicket();
new Thread(station,"小明").start();
new Thread(station,"老师").start();
new Thread(station,"黄牛").start();
}
}
class BuyTicket implements Runnable{
// 票
private int ticketNums = 10;
boolean flag = true; //外部停止方式
// 不安全集合
public class UnSafeList {
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(300);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(list.size());
}
}
5.同步方法
-
由于我们可以通过private关键字来保证数据对象只能被方法访问﹐所以我们只需要针对方法提出一套机制,这套机制就是synchronized关键字,它包括两种用法︰synchronized方法和synchronized 块。
-
同步方法:public synchronized void method(int args){ }
-
synchronized方法控制对“对象”的访问﹐每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行﹐否则线程会阻塞,方法一旦执行﹐就独占该锁,直到该方法返回才释放锁﹐后面被阻塞的线程才能获得这个锁,继续执行。
-
缺陷:若将一个大的方法申明为synchronized将会影响效率。
-
synchronized默认锁这个对象
-
同步块:synchronized(obj){ }
-
Obj称之为同步监视器,Obj可以是任何对象﹐但是推荐使用共享资源作为同步监视器,同步方法中无需指定同步监视器﹐因为同步方法的同步监视器就是this ,就是这个对象本身,或者是 class [反射中讲解]。
-
同步监视器的执行过程:
-
第一个线程访问,锁定同步监视器﹐执行其中代码.
-
第二个线程访问﹐发现同步监视器被锁定﹐无法访问.
-
第一个线程访问完毕﹐解锁同步监视器﹒
-
第二个线程访问,发现同步监视器没有锁﹐然后锁定并访问
锁的对象是变化的量
-
锁对象为this
/ 不安全买票
// 线程不安全
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket station = new BuyTicket();
new Thread(station,"小明").start();
new Thread(station,"老师").start();
new Thread(station,"黄牛").start();
}
}
class BuyTicket implements Runnable{
// 票
private int ticketNums = 100;
boolean flag = true; //外部停止方式
-
锁对象为任意对象
package com.yuan.syn;
// 不安全取钱
// 两个人去取钱
public class UnSafeBank {
public static void main(String[] args) {
// 账户
Account account = new Account(10000,"结婚存款");
Drawing you = new Drawing(account,50,"你");
Drawing girlFriend = new Drawing(account,100,"girlFriend");
new Thread(you).start();
new Thread(girlFriend).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 noMoney;
public Drawing(Account account,int drawingMoney,String name){
super(name);
this.account =account;
this.drawingMoney =drawingMoney;
}
// 取钱
public void run(){
synchronized (account){
//判断有没有钱
if(account.money-drawingMoney<0){
System.out.println(Thread.currentThread().getName()+"钱不够,取不了");
return;
}
// Thread.sleep(100);造成线程不安全
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 卡内余额 = 余额 - 取出的钱
account.money = account.money -drawingMoney;
// 手里的钱
noMoney = noMoney + drawingMoney;
System.out.println(account.name+"余额位:"+account.money);
// Thread.currentThread().getName() = this.getName();
System.out.
-