看如下代码:
package project;
public class Demo {
public static void main(String []args){
final Out out=new Out();
new Thread(){
public void run(){
int i=0;
while(i<5){
i++;
out.print("1111111111");
}
}
}.start();
new Thread(){
public void run(){
int i=0;
while(i<5){
i++;
out.print("2222222222");
}
}
}.start();
}
}
class Out{
public void print(String str){
for(int i=0;i<str.length();i++){
System.out.print(str.charAt(i));
}
System.out.println();
}
}
运行结果:
理想的打印应该是每行只有1或2并且每行10个数字,而上面却出现了行内数字个数不为10,1中还夹杂着2。
那么出现这种现象的原因是什么呢?
线程并不是同时执行的,而是CPU快速的在这线程之间切换执行,由于切换速度极快使我们感觉同时执行罢了。
在这里,A线程在还没有执行完print()方法时,就切换到了B线程调用了print(),然后在B线程结束或者还没结束又切换到了A线程,如此反复,造成的结果。
那么怎么控制在A(或B)线程在调用print()方法时,另一个线程不调用print()方法呢?
针对线程同步问题java早就有解决方法了,最简单的就是给方法加上synchronized关键字,如下:
class Out{
public synchronized void print(String str){
for(int i=0;i<str.length();i++){
System.out.print(str.charAt(i));
}
System.out.println();
}
}
这样结果就会是理想状态的结果了:
这是什么意思呢?加上synchronized关键字后,比如A线程执行print()方法就相当于拿到了一把锁,只有获取这个锁才能执行此方法,如果在A线程执行print()方法过程中B线程也想插一脚进来执行print()方法,对不起此时这是不能够的,因为此时锁在A线程手里,B线程无权拿到这把锁,只有等到A线程执行完后放弃锁,B线程才能拿到锁执行print()方法。
为print()方法加上synchronized后其就变成了同步方法,普通同步方法的锁是this,也就是当前对象,比如Demo中,外部要想调用print()方法就必须创建Out类实例对象out,此时print()同步方法的锁就是这个out。
我们还可以通过同步代码块
写法:synchronized(obj){},其中obj为锁对象。
如这里,修改Out类:
class Out{
public void print(String str){
synchronized(this){
for(int i=0;i<str.length();i++){
System.out.print(str.charAt(i));
}
System.out.println();
}
}
}
这样结果还会是理想状态:
原因是在执行同步代码块里面的内容的线程 获得了锁,只有在它执行完毕后释放了锁,其他线程才能获得锁,并执行代码块里面的内容。
如上是对一个类的一个对象,里面的方法或者代码块加锁。
如果是一个类的多个对象呢?会出现什么情况?看下面一段代码:
package project;
public class Demo {
public static void main(String []args){
final Out out1=new Out();
final Out out2=new Out();
new Thread(){
public void run(){
int i=0;
while(i<5){
i++;
out1.print("1111111111");
}
}
}.start();
new Thread(){
public void run(){
int i=0;
while(i<5){
i++;
out2.print("2222222222");
}
}
}.start();
}
}
class Out{
public void print(String str){
synchronized(this){
for(int i=0;i<str.length();i++){
System.out.print(str.charAt(i));
}
System.out.println();
}
}
}
上面的代码了,创建了两个Out类对象,两个线程分别执行不同的对象的print()方法 。
看下运行结果:
很显然,结果并不理想,synchronized 加锁并没起作用。
原因:synchronized 的锁对象为this,也就是当前实例对象,但这里是同一个类的不同对象实例,两个线程访问的不是同一个对象,所以synchronized并不会起作用。
那么这里要怎么让synchronized起作用呢?
让synchronized锁这个类对应的Class对象。
修改Out类:
class Out{
public void print(String str){
synchronized(Out.class){
for(int i=0;i<str.length();i++){
System.out.print(str.charAt(i));
}
System.out.println();
}
}
}
以上将synchronized(this)改为了synchronized(Out.class)
运行结果如下:
这样加锁后,就算不是同一个类的实例对象,锁也能起作用。