上述代码解析的结果是:有两个线程my1和my2,当my1首先抢到cpu的执行权,进去到run方法中,run方法中调用了printVal(val)方法。在这个方法上使用到了同步(synchronized),这样就相当于一个同步函数。当my1进来访问时,首先进行判断是否有锁,如果有,就会进去printVal(val)方法,然后首先就将锁给关闭,然后执行for循环,打印v的值。当for循环结束时,线程my1就会开启同步锁,然后出去。这时线程my2可能就会抢到cpu的执行权,然后进入到同步函数中。
如果printVal(val)方法不是同步方法,那么就会出现当线程my1抢到执行权进入run方法,执行for循环,当i=1时,执行到if(i>0)完,cpu的执行权被线程my2抢到了,这时它也来判断i = 1,i>0,满足,判断完后,这时cpu的执行权又被my1抢到了,这时,i被打印,然后i--,这时线程my2抢到了,它就不会去执行if判断了,它答应出来i的值就变了-1,这时就出现了多线程的安全问题。
出现多线程的原因是:当多条语句在操作同一个线程共享数据时,一个线程对多余语句只执行了一部分,还没执行完,另一个线程参与进来执行,导致共享数据的错误。如果一个让一个线程进入方法将语句全部执行完再让其他的线程进来执行,那么线程的安全性的问题就会被解决。因此,我们在方法体上添加了同步(synchronized)。
在上述代码中,会发现同步代码块(synchronized)后面带的参数(锁)有所不同,函数需要被对象调用,那么函数都有一个对象引用this,所以,一般方法里,同步使用的锁是this。但是如果同步函数是个静态的,而静态方法中不可以定义this,静态在进内存时,内存中没有本类的对象,但是一定有该类对应的字节码对象(类名.Class)——该对象的类型是Class。因此静态同步方法使用的锁是该同步方法所在类的字节码对象(类名.Class)。
在Java中,还有一种机制:等待-唤醒机制(wait-notify,notifyAll)
线程在运行时,线程会开启一个线程池,等待线程就存在线程池中,而且唤醒的是唤醒线程池中第一个等待的线程。
这种机制用于同步中,因此这种机制在操作同步中的线程时,都必须要标识它们说操作线程持有的锁,只有同一个锁上被等待的线程,才能被痛一个锁给唤醒(notify),不可以对不同锁中的线程进行唤醒。
也就是说,等待和唤醒必须是同一个锁,而锁可以是任意对象,所以可以被任意对象调用的方法定义在object中。
停止线程(run方法结束)
开启多新村运行,运行代码通常是循环结构的,只要控制循环就可以让run方法结束,也就是线程结束。