并发 同步 性能分析
显然同步方法,直接锁住方法的对象 ,很精准,但会造成线程安全 范围太大---->效率低下的问题
换用同步块锁定 可以锁定特定的对象 但可能会出现线程不安全 ticketNums对象在变 锁不住的问题
锁定this即方法的对象 如果缩小锁定范围,可能会出现线程不安全 范围太小 锁不住的问题
因此我们应该尽可能的锁定合理的范围(不是指代码,指数据的完整性)
很多时候我们需要用到双重检查的问题 一般用来解决临界值的问题
虽然代码有所增多,但性能却有了很大的提升
if (ticketNums <= 0) {//考虑没有票的可能
flag = false;
return;
}
synchronized (this) {
if (ticketNums <= 0) {//考虑最后一张票
flag = false;
return;
}
package com.sxt.syn;
/**
* 线程安全 :在并发时保证数据的准确性、效率尽可能高
* synchronized
* 1、同步方法
* 2、同步块
*/
public class SynBlockTest03 {
public static void main(String[] args) {
//一份资源
SynWeb12306 web = new SynWeb12306();
//多个代理
new Thread(web,"码畜").start();
new Thread(web,"码农").start();
new Thread(web,"码磺").start();
}
}
class SynWeb12306 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();
}
test05();
}
}
//线程安全:尽可能锁定合理的范围(不是指代码,指数据的完整性)
//double checking
public void test05() {
if (ticketNums <= 0) {//考虑没有票的可能
flag = false;
return;
}
synchronized (this) {
if (ticketNums <= 0) {//考虑最后一张票
flag = false;
return;
}
//模拟网络延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "---->" + ticketNums--);
}
}
//线程不安全 范围太小 锁不住
public void test04() {
synchronized (this) {
if (ticketNums <= 0) {
flag = false;
return;
}
}
//模拟网络延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "---->" + ticketNums--);
}
//线程不安全 ticketNums对象在变
public void test03() {
synchronized ((Integer)ticketNums) {
if (ticketNums <= 0) {
flag = false;
return;
}
//模拟网络延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "---->" + ticketNums--);
}
}
//线程安全 范围太大---->效率低下
public void test02() {
synchronized (this) {
if (ticketNums <= 0) {
flag = false;
return;
}
//模拟网络延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "---->" + ticketNums--);
}
}
//线程安全 同步
public synchronized void test01(){
if(ticketNums<=0){
flag=false;
return;
}
//模拟网络延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"---->"+ticketNums--);
}
}
多线程_ 并发 _同步 _快乐影院
写一个顾客类一个影院类
影院类里有属性影院座位数、影院名称
影院类中写一个订票方法 如果订票数大于座位数,返回flase
不大于就是座位数减订票数返回目前座位数
顾客类重写Runnable接口,实现多线程
添加属性要去的影院、要买的座位数
run()中判断购票是否成功
synchronized利用方法块锁住影院
main()里实现两个线程并发
package com.sxt.syn;
/**
* 快乐影院
*/
public class HappyCinema {
public static void main(String[] args) {
Cinema c = new Cinema(20,"快乐影院");
new Thread(new Customer(c,2),"南疆").start();
new Thread(new Customer(c,1),"宝").start();
}
}
//顾客
class Customer implements Runnable{
Cinema cinema;
int seats;
public Customer(Cinema cinema, int 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{
int available;//可用的位置
String name;//名称
public Cinema(int available, String name) {
this.available = available;
this.name = name;
}
//订票
public boolean bookTickets(int seats) {
System.out.println("可用位置为:"+available);
if (seats>available){
return false;
}
available-=seats;
return true;
}
}
改造快乐影院,可以进行选票
添加容器
难在判断出票是否成功
将可用的位置拷贝一份,有人订票就调用copy.removeAll()减去位置
判断可用位置的容器量减去定完票的copy容器中的容量是否等于订票的容量,如果不相等return flase 相等的话就将copy的容量给影院位置
package com.sxt.syn;
import java.util.ArrayList;
import java.util.List;
/**
* 快乐影院
*/
public class HappyCinema02 {
public static void main(String[] args) {
//可用的位置
List<Integer> available=new ArrayList<Integer>();
available.add(1);
available.add(2);
available.add(3);
available.add(6);
available.add(7);
//顾客需要的位置
List<Integer> seats1=new ArrayList<Integer>();
seats1.add(1);
seats1.add(6);
List<Integer> seats2=new ArrayList<Integer>();
seats2.add(2);
seats2.add(3);
seats2.add(4);
SxtCinema c = new SxtCinema(available,"快乐影院");
new Thread(new HappyCustomer(c,seats1),"南疆").start();
new Thread(new HappyCustomer(c,seats2),"宝").start();
}
}
//顾客
class HappyCustomer implements Runnable{
SxtCinema cinema;
List<Integer> seats;
public HappyCustomer(SxtCinema 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("出票成功"+"\t" + Thread.currentThread().getName() + "-<位置为:" + seats);
} else {
System.out.println("出票失败"+"\t" + Thread.currentThread().getName() + "-<位置不够");
}
}
}
}
//影院
class SxtCinema{
List<Integer> available;//可用的位置
String name;//名称
public SxtCinema(List<Integer> available, String name) {
this.available = available;
this.name = name;
}
public boolean bookTickets(List<Integer> seats) {
System.out.println("欢迎光临"+this.name+"\n"+"当前可用位置为:" + available);
List<Integer> copy = new ArrayList<Integer>();
copy.addAll(available);
//相减
copy.removeAll(seats);
//判断大小
if (available.size()-copy.size()!=seats.size()){
return false;
}
//成功
available=copy;
return true;
}
}
/**
欢迎光临快乐影院
当前可用位置为:[1, 2, 3, 6, 7]
出票成功 南疆-<位置为:[1, 6]
欢迎光临快乐影院
当前可用位置为:[2, 3, 7]
出票失败 宝-<位置不够
*/
同步方法实现购票
同步方法写在火车票网里
顾客作为Thread的子类继承父类的Runnable target
在看是谁在购票时,我们可以将当前线程进行强转给子类顾客,通过顾客对象的调用判断是否购票成功
package com.sxt.syn;
/**
* 快乐火车票
*/
public class Happy12306 {
public static void main(String[] args) {
Web12306 c = new Web12306(20,"快乐火车");
new Passenger(c,"南疆",3).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{
int available;//可用的位置
String name;//名称
public Web12306(int available, String name) {
this.available = available;
this.name = name;
}
@Override
public void run() {
Passenger p=(Passenger) Thread.currentThread();
boolean flag = false;
try {
flag = this.bookTickets(p.seats);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (flag) {
System.out.println("出票成功" + Thread.currentThread().getName() + "-<位置为:" + p.seats);
} else {
System.out.println("出票失败" + Thread.currentThread().getName() + "-<位置不够");
}
}
//购票
public synchronized boolean bookTickets(int seats) throws InterruptedException {
Thread.sleep(200);
System.out.println("可用位置为:"+available);
if (seats>available){
return false;
}
available-=seats;
return true;
}
}
并发操作编程容器
juc的并发编程中 list 有对应的并发容器 直接供我们使用 内部已经实现好了锁定:CopyOnWriteArrayList list = new CopyOnWriteArrayList();
package com.sxt.syn;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* 线程安全:操作并发容器
*/
public class SynContainer {
public static void main(String[] args) throws InterruptedException {
CopyOnWriteArrayList<String > list = new CopyOnWriteArrayList<String>();
for (int i = 0; i <1000 ; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(1000);
System.out.println(list.size());
}
}
/**
1000
*/
死锁
死锁:多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能进行,而导致两个或者多个线程都在等待着对方释放资源,都停止执行的情形。某一个同步块同时拥有“两个以上对象的锁”时,就可能发生“死锁"的问题。
化妆死锁问题
张柏芝拿着口红,王菲拿着镜子
互相都不放下,造成死锁
package com.sxt.syn;
/**
* 死锁:过多的同步可能造成相互不释放资源
* 从而相互等待,一般发生于同步中持有多个对象的锁
*/
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 + "涂口红");
//一秒后想拥有镜子的锁
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (mirror) {//获得口红的锁
System.out.println(this.girl + "照镜子");
}
}
} else {
synchronized (mirror) {//获得镜子的锁
System.out.println(this.girl + "照镜子");
//2秒后想拥有口红的锁
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lipstick) {//获得口红的锁
System.out.println(this.girl + "涂口红");
}
}
}
}
}
解决死锁:避免:不要在同一个代码块中,同时持有多个对象的锁
禁止锁套锁
package com.sxt.syn;
/**
* 死锁:过多的同步可能造成相互不释放资源
* 从而相互等待,一般发生于同步中持有多个对象的锁
*
* 避免:不要在同一个代码块中,同时持有多个对象的锁
*/
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 + "涂口红");
//一秒后想拥有镜子的锁
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
/*synchronized (mirror) {//获得口红的锁
System.out.println(this.girl + "照镜子");
}*/
}
synchronized (mirror) {//获得口红的锁
System.out.println(this.girl + "照镜子");
}
} else {
synchronized (mirror) {//获得镜子的锁
System.out.println(this.girl + "照镜子");
//2秒后想拥有口红的锁
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
/*synchronized (lipstick) {//获得口红的锁
System.out.println(this.girl + "涂口红");
}*/
}
synchronized (lipstick) {//获得口红的锁
System.out.println(this.girl + "涂口红");
}
}
}
}