synchronized方法
package com.sxt.syn;
/**
* 线程安全: 在并发时保证数据的正确性、效率尽可能高
* synchronized
* 1、同步方法
* 2、同步块
* @author
*
*/
public class SynTest01 {
public static void main(String[] args) {
//一份资源
SafeWeb12306 web =new SafeWeb12306();
//多个代理
new Thread(web,"码畜").start();
new Thread(web,"码农").start();
new Thread(web,"码蟥").start();;
}
}
class SafeWeb12306 implements Runnable{
//票数
private int ticketNums =10;
private boolean flag = true;
@Override
public void run() {
while(flag) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
test();
}
}
//线程安全 同步
public synchronized void test() {
if(ticketNums<=0) {
flag = false;
return ;
}
//模拟延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->"+ticketNums--);
}
}
synchronized是锁对象的资源。
我们数据都是在方法里面操作的,我们通过方法就可以去控制这个数据了。通过块也能去保证数据的安全。
锁了(方法)资源,这个资源是对象的资源,那此时这个对象是this,方法里面的资源是不是都是和这个对象相关的资源,如果不是就琐失败了。
package com.sxt.syn;
/**
* 线程安全: 在并发时保证数据的正确性、效率尽可能高
* synchronized
* 1、同步方法
* 2、同步块
* @author
*
*/
public class SynTest02 {
public static void main(String[] args) {
//账户
Account account =new Account(100,"结婚礼金");
SafeDrawing you = new SafeDrawing(account,80,"可悲的你");
SafeDrawing wife = new SafeDrawing(account,90,"happy的她");
you.start();
wife.start();
}
}
class Account{
int money; //金额
String name; //名称
public Account(int money, String name) {
this.money = money;
this.name = name;
}
//模拟取款
class SafeDrawing extends Thread{
Account account ; //取钱的账户
int drawingMoney ;//取的钱数
int packetTotal ; //口袋的总数
public SafeDrawing(Account account, int drawingMoney,String name) {
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
@Override
public void run() {
test();
}
//目标不对锁定失败 这里不是锁this 应该锁定 account
public synchronized void test() {
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为什么没锁上,这些都是线程的难点。
synchronized块
注:
刚刚我们发现使用同步方法锁的这个资源的对象不对,因为默认情况下这个成员方法是this,因此,我要锁一个具体的对象该怎么办呢?借助synchronized同步块!
synchronized同步块在小括号里面填入的是一个具体的对象,比如丢入Account对象、this或者丢入模子的信息,小括号里才是我们需要锁定的对象。然后花括号里面写入代码,我们将花括号的代码一般来说就称为块,加入了synchronized就称为“同步块”。
Java里面的块有四种:
1、方法里面写一个块,我们称为局部块(普通块),用来解决变量的作用域,快速释放内存;
2、如果是在类中方法外写一个块,这个块我们称为构造块,作用和构造器一样,用来初始“对象”信息的。
3、如果在构造块上加一个static,我们称为静态块,静态块和构造块的区别是:静态块加载一次是用于初始化类的,先于构造块执行。
4、最后一个是同步块,也是在方法里面,用于解决线程安全的问题。
同步监视器的执行过程:
package com.sxt.syn;
/**
* 线程安全: 在并发时保证数据的正确性、效率尽可能高
* synchronized
* 1、同步方法
* 2、同步块 ,目标更明确
* @author
*
*/
public class SynBlockTest01 {
public static void main(String[] args) {
//账户
Account account =new Account(1000,"结婚礼金");
SynDrawing you = new SynDrawing(account,80,"可悲的你");
SynDrawing wife = new SynDrawing(account,90,"happy的她");
you.start();
wife.start();
}
}
//模拟取款 线程安全
class SynDrawing extends Thread{
Account account ; //取钱的账户
int drawingMoney ;//取的钱数
int packetTotal ; //口袋的总数
public SynDrawing(Account account, int drawingMoney,String name) {
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
@Override
public void run() {
test() ;
}
//目标锁定account
public void test() {
//提高性能
if(account.money<=0) {
return ;
}
//同步块
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);
}
}
}
package com.sxt.syn;
import java.util.ArrayList;
import java.util.List;
/**
* 线程安全:操作容器
*
* @author *
*/
public class SynBlockTest02 {
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<String>();
for(int i=0;i<10000;i++) {
new Thread(()->{
//同步块
synchronized(list) {
list.add(Thread.currentThread().getName());
}
}) .start();
}
Thread.sleep(10000);
System.out.println(list.size());
}
}
上面的案例除了main方法没有其他方法,我们锁main方法没有用,方法中使用的是lambda表达式,所以没有方法我们锁谁呢?我们锁不了main方法,只能锁List。synchronized(list){list.add(Thread.currentThread().getName());}。在添加的时候保证是拿到锁的。
在上面这个案例中如果没有加最后的Thread.sleep(),最后执行的结果也是不正确的,这是因为main方法中有“多个线程”和“主线程”,“多个线程”没有执行完,main方法就先执行完了–>System.out.println(list.size());