Java基础巩固之线程的同步synchronized

时间:2023-02-14 20:42:05

Java中的synchronized关键字,可以用来修饰方法和代码块,主要是为了保证在同一时刻只有一条线程访问被修饰的代码。

synchronized分为对象级别的同步和类级别的同步。

1、当使用synchronized关键字修饰普通方法时,如果已经有一条线程访问此方法,那么其他使用同一对象调用此方法的线程,需要等待此线程执行完成后释放对象锁才能执行该方法,同时需要注意的是也无法执行当前对象中使用synchronized修饰的其他方法,但是可以执行非synchronized修饰的方法

2、当使用static synchronized关键字修饰普通方法时,如果已经有一条线程访问此方法,那么其他使用此类的任何实例化对象调用此方法的线程,需要等待此线程执行完成后释放锁才能执行该方法,同时需要注意的是也无法执行类中使用synchronized或者static synchronized修饰的其他方法,但是可以执行非synchronized修饰的方法


来一段代码,看看在没使synchronized关键字同步代码时,用两个线程访问同一资源是否会出现问题

Outputer类包含output方法,用于打印字符

class Outputer
{
public synchronized void output(String str)
{
int len = str.length();
for(int i = 0; i < len; i++)
{
System.out.print(str.charAt(i));
}

System.out.println();
}
}
创建两条线程,用于调用output方法
//实例化Outputer对象
static Outputer outputer = new Outputer();
public static void main(String[] args)
{
//线程1
Thread t1 = new Thread(new Runnable()
{
@Override
public void run()
{
while(true)
{
//调用outputer对象的output
outputer.output("abcdefghijk");
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
});

//线程2
Thread t2 = new Thread(new Runnable()
{
@Override
public void run()
{
while(true)
{
//调用outputer对象的output
outputer.output("mnopqrstuvw");
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
});

t1.start();
t2.start();
}

在执行过程中,出现错乱的输出:

Java基础巩固之线程的同步synchronized


解决方法:

1、将循环输出的代码,放到synchronized同步代码块中

class Outputer
{
public void output(String str)
{
String sync = "";
/*
* 1、两条线程使用的是同一个outputer对象,所以可以使用"this"
* 2、由于用一个对象中的"sync"变量只会存在一份,所以也可以使用"sync"变量来控制
*/
synchronized (this/*sync*/)
{
int len = str.length();
for(int i = 0; i < len; i++)
{
System.out.print(str.charAt(i));
}

System.out.println();
}
}
}

2、用synchronized关键字修饰方法

class Outputer
{
/**
* 普通方法需要类的实例化对象来调用,所以synchronized修饰的方法也只针对于同一个对象
* @param str
*/
public synchronized void output(String str)
{
int len = str.length();
for(int i = 0; i < len; i++)
{
System.out.print(str.charAt(i));
}

System.out.println();
}
}

以上方法只在多条线程使用同一个Outputer()对象实例调用output方法时起作用,如果每条线程都使用不同的Outputer()实例对象,则还是会出现错乱现象。


解决方法:

1、使用 static synchronized修饰方法

class Outputer
{
/**
* 静态方法的调用不依赖于任何实例化对象,是类级别的方法。
* 所以synchronized控制的是类级别
* @param str
*/
public static synchronized void output(String str)
{
int len = str.length();
for(int i = 0; i < len; i++)
{
System.out.print(str.charAt(i));
}

System.out.println();
}
}

2、在synchronized块的参数中传入字节码对象

class Outputer
{
public void output(String str)
{
/**
* 类的字节码也是一个Class对象,且在JVM中是唯一存在的
*/
synchronized (Outputer.class)
{
int len = str.length();
for(int i = 0; i < len; i++)
{
System.out.print(str.charAt(i));
}

System.out.println();
}
}
}