线程同步:要在多线程的环境下保证数据的准确性和安全性
线程不安全的示例1:银行取钱
多个人同时取钱时,账户余额会出现负数的情况
/**
* 線程不安全:取錢
* @author Jack
*
*/
public class DrawMoney {
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() {
account.money-=drawingMoney;
packetTotal+=drawingMoney;
System.out.println(this.getName()+"-->賬戶餘額爲:"+account.money);
System.out.println(this.getName()+"-->口袋的錢爲:"+packetTotal);
}
}
线程不安全示例2:操作容器
启动一万个线程,并把每个线程的名称添加到容器中,结果容器的大小并不是10000
import java.util.ArrayList;
import java.util.List;
/**
* 線程不安全:操作容器
* @author Jack
*
*/
public class ListTest {
public static void main(String[] args) {
final List<String> list = new ArrayList<String>();
for(int i=0; i<10000; i++){
new Thread(new Runnable() {
@Override
public void run() {
list.add(Thread.currentThread().getName());
}
}).start();
}
System.out.println(list.size());
}
}
并发:同一个对象被多个线程同时操作![在这里插入图片描述](https://img-blog.csdnimg.cn/20210416164516870.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3OTE3Njkx,size_16,color_FFFFFF,t_70)
并发的情况很可能造成数据的不准确,为了解决这个问题需要线程同步机制
线程同步:队列+锁 (synchronized)
同步之后会导致性能下降
synchronized方法
synchronized快
注意:synchronized锁的范围太大会影响性能,范围太小的话则可能锁不住导致数据不安全。要在保证安全的情况下兼顾性能,这是个难点
解决多人取款的线程同步问题:
/**
* 線程同步:取錢
* 同步塊:目標更明確
* @author Jack
*
*/
public class DrawMoney {
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() {
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);
}
}
}
解决操作容器的线程同步问题:
import java.util.ArrayList;
import java.util.List;
/**
* 線程同步:操作容器
* @author Jack
*
*/
public class ListTest {
public static void main(String[] args) throws InterruptedException {
final List<String> list = new ArrayList<String>();
for(int i=0; i<10000; i++){
new Thread(new Runnable() {
@Override
public void run() {
synchronized(list){
list.add(Thread.currentThread().getName());
}
}
}).start();
}
Thread.sleep(5000);
System.out.println(list.size());
}
}
多线程并发例子:电影院购票
支持多人同时购票,每个人可以选择坐位,通过synchronized代码块保证线程并发时数据的准确性
import java.util.ArrayList;
import java.util.List;
public class HappyCinema {
public static void main(String[] args) {
//电影院剩余位置
List<Integer> availableSeats = new ArrayList<Integer>();
availableSeats.add(1);
availableSeats.add(3);
availableSeats.add(5);
availableSeats.add(7);
availableSeats.add(8);
//顾客1要买的位置
List<Integer> seats1 = new ArrayList<Integer>();
seats1.add(1);
seats1.add(3);
//顾客2要买的位置
List<Integer> seats2 = new ArrayList<Integer>();
seats2.add(1);
seats2.add(8);
Cinema c = new Cinema("happy cinema",availableSeats);
new Thread(new Customer(c,seats1),"张三").start();
new Thread(new Customer(c,seats2),"李四").start();
}
}
/*
* 顾客线程
* 成员变量:影院(在哪个影院买)、要买的票数
* 线程体中执行影院的购票方法
*/
class Customer implements Runnable{
Cinema cinema;
List<Integer> seats;
public Customer(Cinema cinema, List<Integer> seats) {
this.cinema = cinema;
this.seats = seats;
}
@Override
public void run() {
synchronized (cinema) {
boolean flag = cinema.bookTickets(seats);
if(flag){
System.out.println("出票成功,"+Thread.currentThread().getName()+"-->位置为:"+seats);
}else{
System.out.println("出票失败,"+Thread.currentThread().getName()+"-->位置不够");
}
}
}
}
/*
* 影院
* 成員變量:影院名稱、剩余票数
* 方法:购票(购买的票数<可用剩余票数,则购买成功,并且更新剩余票数。否则购票失败)
*/
class Cinema{
String name;
List<Integer> availableSeats;
public Cinema(String name, List<Integer> availableSeats) {
this.name = name;
this.availableSeats = availableSeats;
}
//购票方法,传入一个参数:购票数量
public boolean bookTickets(List<Integer> seats){
System.out.println("欢迎光临"+this.name+",当前剩余票数为:"+availableSeats);
List<Integer> copy = new ArrayList<Integer>();
copy.addAll(availableSeats);
//相减
copy.removeAll(seats);
//判断大小
if(availableSeats.size()-copy.size()!=seats.size()){
return false;
}
//成功
availableSeats = copy;
return true;
}
}
多线程并发例子二:12306上购票(单张)
public class Happy12306 {
public static void main(String[] args) {
Web12306 c = new Web12306("12306官网", 5);
new Passenger(c,"张三",4).start();
new Passenger(c,"李四",2).start();
}
}
/*
* 乘客线程
*/
class Passenger extends Thread{
int seats;
public Passenger(Runnable target, String name, int seats) {
super(target, name);
this.seats=seats;
}
}
/*
* 火车票网
*/
class Web12306 implements Runnable{
String name;
int availableSeats;
public Web12306(String name, int availableSeats) {
this.name = name;
this.availableSeats = availableSeats;
}
@Override
public void run() {
Passenger p = (Passenger)Thread.currentThread();
boolean flag = this.bookTickets(p.seats);
if(flag){
System.out.println("出票成功,"+Thread.currentThread().getName()+"-->位置为:"+p.seats);
}else{
System.out.println("出票失败,"+Thread.currentThread().getName()+"-->位置不够");
}
}
//购票方法,传入一个参数:购票数量
public synchronized boolean bookTickets(int seats){
System.out.println("欢迎光临"+this.name+",当前剩余票数为:"+availableSeats);
if(seats>availableSeats){
return false;
}
availableSeats-=seats;
return true;
}
}
并发容器:java.util.concurrent.CopyOnWriteArrayList 底层已经实现了synchronized,所以不需要再加同步锁
import java.util.concurrent.CopyOnWriteArrayList;
/**
* 并发容器
* @author Jack
*
*/
public class SynContainer {
public static void main(String[] args) throws InterruptedException {
final CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
for(int i=0;i<10000;i++){
new Thread(new Runnable() {
@Override
public void run() {
list.add(Thread.currentThread().getName());
}
}).start();
}
Thread.sleep(10000);
System.out.println(list.size());
}
}
死锁:
/**
* 死锁:过多的同步可能造成相互不释放资源,从而相互等待,一般发生于同步中持有多个对象的锁
*
* @author Jack
*
*/
public class DeadLock {
public static void main(String[] args) {
Markup g1 = new Markup(0, "欢欢");
Markup g2 = new Markup(1, "婷婷");
g1.start();
g2.start();
}
}
//口红
class Lipstick{
}
//镜子
class Mirror{
}
//化妆
class Markup extends Thread{
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
//选择
int choice;
//名字
String girl;
public Markup(int choice, String girl) {
this.choice = choice;
this.girl = girl;
}
@Override
public void run() {
//化妆
markup();
}
//相互持有对方的对象锁,可能造成死锁
private void markup() {
if(choice==0){
synchronized (lipstick) {
//获得口红的锁
System.out.println(this.girl+"涂口红");
//1秒钟后想拥有镜子的锁
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (mirror) {
System.out.println(this.girl+"照镜子");
}
}
}else{
synchronized (mirror) {
//获得镜子的锁
System.out.println(this.girl+"照镜子");
//1秒钟后想拥有口红的锁
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lipstick) {
System.out.println(this.girl+"涂口红");
}
}
}
}
}
避免死锁:不要再同一个代码块中同时持有多个对象的锁
//相互持有对方的对象锁,可能造成死锁
private void markup() {
if(choice==0){
synchronized (lipstick) {
//获得口红的锁
System.out.println(this.girl+"涂口红");
//1秒钟后想拥有镜子的锁
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (mirror) {
System.out.println(this.girl+"照镜子");
}
}else{
synchronized (mirror) {
//获得镜子的锁
System.out.println(this.girl+"照镜子");
//1秒钟后想拥有口红的锁
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (lipstick) {
System.out.println(this.girl+"涂口红");
}
}
}
并发协作模型:生产者消费者模式–>01.管程法
/**
* 协作模型:生产者消费者实现方式一:管程法
* 借助缓冲区
* @author Jack
*
*/
public class CoTest01 {
public static void main(String[] args) {
//缓冲区
SynContainer container = new SynContainer();
new Productor(container).start();
new Consumer(container).start();
}
}
//生产者
class Productor extends Thread{
SynContainer container;
public Productor(SynContainer container) {
this.container = container;
}
@Override
public void run() {
//生产
for(int i=1;i<=10;i++){
System.out.println("生产-->"+i+"个馒头");
container.push(new Steamedbun(i));
}
}
}
//消费者
class Consumer extends Thread{
SynContainer container;
public Consumer(SynContainer container) {
this.container = container;
}
@Override
public void run() {
//消费
for(int i=1;i<=15;i++){
System.out.println("消费-->"+container.pop().id+"个馒头");
}
}
}
//缓冲区
class SynContainer{
Steamedbun[] buns = new Steamedbun[10];//存储容器
int count = 0;//计数器
//存储 生产
public synchronized void push(Steamedbun bun){
//何时能生产: 容器存在空间
//不能生产: 只有等待
if(count==buns.length){
try {
this.wait();//线程阻塞,生产者通知消费的时候解除阻塞
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//存在空间 可以生产
buns[count] = bun;
count++;
this.notify();//存在数据了, 可以通知消费了
}
//获取 消费
public synchronized Steamedbun pop(){
//何时消费:容器中有数据的时候
//没有数据:等待
if(count==0){
try {
this.wait();//线程阻塞,生产者通知消费的时候解除阻塞
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//存在数据 可以消费
count--;
Steamedbun bun = buns[count];
this.notify();//存在空间了,可以唤醒对方生产了
return bun;
}
}
//馒头
class Steamedbun{
int id;
public Steamedbun(int id) {
this.id = id;
}
}
并发协作模型:生产者消费者模式–>02.信号灯法
/**
* 协作模式:生产者消费者模式实现方式二:信号灯法
* 借助标志位
* @author Jack
*
*/
public class CoTest02 {
public static void main(String[] args) {
TV tv = new TV();
new Player(tv).start();
new Watcher(tv).start();
}
}
//生产者 演员
class Player extends Thread{
TV tv;
public Player(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for(int i=0;i<5;i++){
if(i%2==0){
this.tv.play("奇葩说");
}else{
this.tv.play("太污了,来瓶立白洗洗嘴");
}
}
}
}
//消费者 观众
class Watcher extends Thread{
TV tv;
public Watcher(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for(int i=0;i<5;i++){
tv.watch();
}
}
}
//同一个资源 电视
class TV{
String voice;
//信号灯
//T 表示演员表演 观众等待
//F 表示观众观看 演员等待
boolean flag = true;
//表演
public synchronized void play(String voice){
//演员等待
if(!flag){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//表演
System.out.println("表演了:"+voice);
this.voice = voice;
//唤醒
this.notifyAll();
//切换标识
this.flag=!this.flag;
}
//观看
public synchronized void watch(){
//观众等待
if(flag){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//观看
System.out.println("听到了:"+voice);
//唤醒
this.notifyAll();
//切换标识
this.flag=!this.flag;
}
}