通过分析,发现,打印出0,-1,-2的错票。
多线程出现安全问题!!!!
- tick只有一份,多个线程共用这个tick;
- 当多条语句在操作一个线程共享数据时,一个线程对多条语句只执行了一部分,还没执行完另一个线程已经开始执行共享代码块!!
- 解决办法:将共享代码块加上一个锁,也叫同步代码块;——— cynchronized
- 格式:synchronized(对象){ 代码块 };对象:只要是对象就可以,可以用上帝(Object obj)
- 锁原理:就是在同步代码块前设置一个标志位,代码块被线程执行时,标志位置0,代码块未被执行时,标志位置1.
- 持有锁的线程可以在代码块中执行,没有锁的线程及时获取到了CPU资源也不能在同步中中执行!
- 锁的弊端:未执行代码块的线程要不断重复的判断标志位,这会消耗资源,拖慢进程速度。
同步的前提:
- 两个或两个以上的线程。
- 必须时多个线程同时使用一个锁。
- 必须保证同步代码只有一个线程在运行。
什么代码需要同步?
- 方法:明确哪些代码块是多线程运行。
- 明确共享数据。
- 明确多线程运行代码中哪些语句是操作共享数据的。
- 函数也可以封装代码,格式:public synchronized void add( **** ){ } ; 作为修饰符,放在前面。
注意:在任何时候,你都可以用Thread.currentThread().getName()来获取当前线程的名字
发现存在代码安全问题:
/*
需求:
银行有一个金库。
有两个储户分别存300元,每次存100,存3次
*/
class Bank{
private int sum;
public void add(int n){
sum = sum + n;
try{Thread.sleep(10);}catch(Exception e){}
System.out.println("sum = "+sum);
}
}
class Cus implements Runnable{
private Bank b = new Bank();
public void run(){
for(int x=0; x<3; x++){
b.add(100);
}
}
}
class BankDemo{
public static void main(String[] args){
Cus c = new Cus();
Thread t1 = new Thread(c);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
- 结果: image.png
-
解决问题:
/*
需求:
银行有一个金库。
有两个储户分别存300元,每次存100,存3次
*/
class Bank{
private int sum;
Object obj = new Object();
public void add(int n){
synchronized(obj){
sum = sum + n;
try{Thread.sleep(10);}catch(Exception e){}
System.out.println("sum = "+sum);
}
}
}
class Cus implements Runnable{
private Bank b = new Bank();
public void run(){
for(int x=0; x<3; x++){
b.add(100);
}
}
}
class BankDemo{
public static void main(String[] args){
Cus c = new Cus();
Thread t1 = new Thread(c);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
-
结果:
- image.png
同步函数:
- 同步函数用的是哪一个锁?
- 函数需要被对象调用,那么函数就都有一个所属对引用。就是this。
- 所以同步函数使用的锁事this。
/*
需求:
银行有一个金库。
有两个储户分别存300元,每次存100,存3次
*/
class Bank{
private int sum;
//Object obj = new Object();
public synchronized void add(int n){ //同步函数!!
//synchronized(obj){
sum = sum + n;
try{Thread.sleep(10);}catch(Exception e){}
System.out.println("sum = "+sum);
//}
}
}
class Cus implements Runnable{
private Bank b = new Bank();
public void run(){
for(int x=0; x<3; x++){
b.add(100);
}
}
}
class BankDemo{
public static void main(String[] args){
Cus c = new Cus();
Thread t1 = new Thread(c);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
一个小练习:
* /*
* 需求:现有两个线程:
* 一个线程在同步代码块中运行;
* 一个线程在同步函数中运行;
* 两个线程都在执行买票程序。
* */
* class Ticket implements Runnable{
* private int tick = 1000;
* Object obj = new Object();
* boolean flag = true ;
* public void run(){
* if(flag){
* while(true){
* synchronized(obj){ //synchronized( this ),解决0号票问题。
* if(tick >0){
* try{Thread.sleep(10);}catch(Exception e){}
* System.out.println(Thread.currentThread().getName()+"code.....sale "+ tick--);
* }
* }
* }
* }
* else{
* while(true){
* this.show();
* }
* }
* }
* public synchronized void show(){
* if(tick >0){
* try{Thread.sleep(10);}catch(Exception e){}
* System.out.println(Thread.currentThread().getName()+"show.....sale "+ tick--);
* }
* }
* }
* class TickDemo{
* public static void main(String[] args){
* Ticket t = new Ticket();
* Thread t1 = new Thread(t);
* Thread t2 = new Thread(t);
* t1.start();
* try{Thread.sleep(10);}catch(Exception e){} //解决办法
* t.flag = false; //问题的渊源!原因:主线程执行太快,这三条语句瞬间就执行完成;flag的作用完全没有体现出来,flag=true时同步代码块还没来的及执行,flag就等于false了,紧接着执行show( )函数;之后就一直为false了。
* t2.start();
* }
* }
- 代码看似没问题,其实问题可大!所有线程都是由show( )同步函数执行完成的。如下:
- image.png
- 解决办法在代码中!!!
- 之后有出现0号票!!!可怕!!
- image.png
- 解决:把同步代码块中obj对象换成this就OK了!
- 如果同步函数被静态修饰时,使用的锁是什么呢?
- 通过验证,发现不再是this,因为静态方法中也不可以定义this。
- 原因:静态进内存时没有对象,只有类,也就是该类对应的字节码文件对象。
- 类名.class 该对象的类型是class。
- 静态的同步方法,使用的锁事该方法所在类的字节码文件对象。类名.class。
网友评论