线程同步(synchronized):是指在一个多线程的环境下,我们要保证数据的准确性和安全性,同时还要提高它的性能。
package com.sxt.syn;
/**
* 线程不安全: 数据有负数、相同
*
* @author
*
*/
public class UnsafeTest01 {
public static void main(String[] args) {
//一份资源
UnsafeWeb12306 web =new UnsafeWeb12306();
//多个代理
new Thread(web,"码畜").start();
new Thread(web,"码农").start();
new Thread(web,"码蟥").start();;
}
}
class UnsafeWeb12306 implements Runnable{
//票数
private int ticketNums =10;
private boolean flag = true;
@Override
public void run() {
while(flag) {
test();
}
}
public void test() {
if(ticketNums<0) {
flag = false;
return ;
}
//模拟延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->"+ticketNums--);
}
}
模拟12306抢票
负数的出现:假设最后一张票是1,现在A、B、C都来抢票,假设B进来了,A也进来了,C也进来了,假设B优先进来,Thread.sleep(200)等待(抱着资源睡觉),A跟C都在等待,假设B先获得时间片把这张票拿走了,此时C醒来了,只能拿0,A也醒来了只能拿-1。这是临界值的概念,就是最后一张票你没有控制,线程不安全。
相同票的出现:开辟多线程,线程会有自己的工作空间,现在有线程A、线程B、线程C,它们的工作空间和主存进行交互。假设主存中现在有PickNums=10,正常情况应该是线程A将10拷贝过来,并减个1,再覆盖回主存,此时主存的PickNums=9。出现相同票问题是因为线程B、线程C有可能在线程A减去1再覆盖回主存之前,将主存中的10也拷贝了过去。【主要的问题存在于工作内存和主内存在拷贝的时候,即获取值的时候不一致,没有等到主内存更新完以后再来拷贝】
package com.sxt.syn;
/**
* 线程不安全:取钱
*
* @author
*
*/
public class UnsafeTest02 {
public static void main(String[] args) {
//账户
Account account =new Account(100,"结婚礼金");
Drawing you = new Drawing(account,80,"可悲的你");
Drawing wife = new Drawing(account,90,"happy的她");
you.start();
wife.start();
}
}
//模拟取款
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() {
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 UnsafeTest03 {
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();
}
System.out.println(list.size());
}
}
不是所有的情况下都要保证线程安全,一般来说我们对数据存在“改”的行为我们就需要控制它保证线程安全,如果只是对数据进行“读”的行为,也就是只拷贝,那就没关系。
存在又“改”又“读”,也需要保证线程安全。