线程同步
synchronized
并发控制
在一个多线程的环境下,保证线程的安全性和准确性,同时还要提高它的性能。
并发:同个对象多个线程同时操作
线程不安全
1、12306取票会有不同的人抢到相同或者负数
package com.sxt.syn;
/**
* 线程不安全 有相同和负数的情况
*/
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--);
}
}
2、银行取钱
会出现账户余额负数的问题
加了判断依旧不能解决,这就是线程不安全
if (account.money-drawingMoney<0){
return;
}
package com.sxt.syn;
/**
* 线程不安全:取钱
*/
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 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() {
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);
}
}
/**
可悲的你----->账户余额为-70
happy的她----->账户余额为-70
happy的她----->口袋的钱为90
可悲的你----->口袋的钱为80
*/
3、加入数据丢失(被覆盖)
都是线程不安全的表现
package com.sxt.syn;
import java.util.ArrayList;
import java.util.List;
/**
* 线程不安全:操作容器
*/
public class UnsafeTest03 {
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
for (int i = 0; i <1000 ; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
System.out.println(list.size());
}
}
/**
993
*/
不是所有线程都要保证安全,只有我们要执行改的才需要保证线程安全,如果只需要读就不用
线程安全
1、等待池形成队列+2、锁机制
锁机制:synchronized
当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可
一个线程持有锁会导致其他所有需要此锁的线程挂起:
在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题;
如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先倒置,引起性能问题
synchronized关键字,它包括两种方法:synchronized方法和synchronized块
同步方法:public synchronized void method(int args){}
缺陷:若将一个大的方法声明为synchronized将会大大影响效率
package com.sxt.syn;
/**
* 线程安全 :在并发时保证数据的准确性、效率尽可能高
* synchronized
* 1、同步方法
* 2、同步块
*/
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--);
}
}
银行取钱:目标不对,锁定失败
package com.sxt.syn;
/**
* 线程安全 :在并发时保证数据的准确性、效率尽可能高
* * synchronized
* * 1、同步方法
* * 2、同步块
*/
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 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(具体的对象) 具体对象称之为同步监视器
方法中的块叫局部快
类中方法外的块叫构造快
在块上加个static叫静态块(先于构造快执行)
同步块在方法里
提款机取钱利用同步块确认线程安全
同步块:目标更明确
提高性能的代码
if (account.money<=0){
return;
}
package com.sxt.syn;
/**
* 线程安全 :在并发时保证数据的准确性、效率尽可能高
* * synchronized
* * 1、同步方法
* * 2、同步块:目标更明确
*/
public class SynBlockTest01 {
public static void main(String[] args) {
Account account = new Account(100,"结婚礼金");
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;
/**
* 线程安全:操作容器
*/
public class SynBlockTest02 {
public static void main(String[] args) throws InterruptedException {
List<Object> list = new ArrayList<>();
for (int i = 0; i <1000 ; i++) {
new Thread(()->{
//同步块
synchronized (list) {
list.add(Thread.currentThread().getName());
}
}).start();
}
Thread.sleep(10000);
System.out.println(list.size());
}
}