同步代码块的出现是解决了多线程的安全问题,但是它增加了代码的缩进层级,同时降低了效率(每次无论是不是对的锁,每个路径都要去判断)
针对同步出现的这两个问题,首先讨论第一个。因此引出一个新的知识点————————
同步函数
关于同步函数的使用(一买车票的代码为例子)代码:
package Thread;
class Tickets implements Runnable{
private int ticket=100;
Object obj = new Object();
public void run(){
while(ticket!=0) {//多线程里要有循环
sale();
}
}
public synchronized void sale(){
if (ticket > 0) {
//使CPU在这里停一下,使用sleep()函数,Thread 中的静态函数。使其出现错误的数据
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}//没有解决方案
System.out.println(Thread.currentThread().getName() + " " + --ticket);
}
}
}
public class ThreadSellTickets {
public static void main(String[] args){
Tickets tc = new Tickets();
Thread T1 =new Thread(tc);
Thread T2 =new Thread(tc);
// Thread T3 =new Thread(tc);
//Thread T4 =new Thread(tc);
T1.start();
T2.start();
//T3.start();
//T4.start();
}
}
同步函数:用synchronized 修饰的函数,因为函数是被对象调用的,所以在使用同步函数时:this.函数名。
在函数调用时其实最完整的其实就是this.函数名
验证写法就是方法:(验证同步函数的所是否为this)
开启两条线程,一条执行同步代码块,一条执行同步函数,如果执行结果没有错误,证明所用的对象/锁 是一致的,如果执行出现了错误证明所用的对象不一致
代码:(完整正确的代码)
package Thread;
class Ticket implements Runnable{
private int ticket=100;
boolean flag=true;
Object obj = new Object();
public void run(){
if(flag){
for(int x=1;x<=5;x++){
synchronized (this){
if(ticket>0){
try{Thread.sleep(10);}catch(InterruptedException e){}
System.out.println(Thread.currentThread().getName()+" ticket = "+--ticket);
}
}
}
}
else{
for(int y=1;y<=5;y++) {
sale();
}
}
}
public synchronized void sale(){
if(ticket>0){
try{Thread.sleep(10);}catch(InterruptedException e){}
System.out.println(Thread.currentThread().getName()+" ticket = "+--ticket);
}
}
}
public class Differents {
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(InterruptedException e){}//使主线程停一会此时只有线程0 在运行
t.flag=false;
T2.start();
}
}
关于这段代码的解释:!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
首先明确CPU切换的意思:CPU是切换到线程,CPU切换到那个线程,哪一个线程就往下执行(指哪里,哪里跑)
解释线程开启间加入休眠函数,避免线程0开启后flag 立刻被改变,并且主线程开启线程1
这段代码run 函数中换成无限循环更好理解这段代码:
flag = true ; 线程0 会一直执行同步代码块,线程0 执行一段时间后,flag 被改变,线程1被开启,线程1执行的是同步函数。
如果两个锁不一样,当线程0执行同步代码块中代码到一半时CPU切换到线程1,线程1 可以在同步函数中对共享数据进行修改——数据会出现错误。
如果两个锁(为this )是一样的,线程0,执行同步代码块执行到一半时CPU切换到线程1,线程一无法进入同步函数,因为锁对象还在线程0那
代码同步代码块和同步函数的异同:
同:都是为同步服务,为了解决多线程的安全问题
异:锁不一样
同步代码块的对象/锁 是自定义的任意对象
,通过锁来区别同步。是更加常见的
同步函数的对象/锁 是固定的 this 。因此只需要一个同步时可以使用同步函数
静态同步函数使用的锁不是this 而是类所对应的.class 文件(字节码文件对象):类名.class (按照万物皆对象的原则这个文件其实也是一个对象)
解释:静态是随着类的加载而加载的,静态是被类名调用的。类加载后的.class 文件其实就是他的一个对象