package thread;
public class SynchronizedTest {
public static void main(String[] args) {
final Output output=new Output();
//启动两个线程,都是用output对象中的output方法进行打印
new Thread(new Runnable() {
@Override
public void run() {
output.output("weijianfei");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
output.output("dreambroken");
}
}).start();
}
}
class Output{
public void output(String s){
for(int i=0;i<s.length();i++){
try {
Thread.sleep(100);//模拟处理一个字符需要100毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print(s.charAt(i));
}
System.out.println();
}
}
运行结果:
wderiejaimabnrfoeki
en
这结果不是我们期待的。这种不可预期的结果是因为某个时间点CPU只能运行一个线程,并且并不是一直把该线程执行完后才去执行其他线程,而是这个线程执行一个时间段后,不管该线程是否结束了,CPU都会去执行其他线程。
为了解决这中问题,锁就出来了,每个对象都有一把锁,谁(线程)得到了锁,谁就执行,执行完后释放锁,其他线程再去抢锁。(回头看上面红字部分的第一点).
代码改善后,如下.
package thread;
public class SynchronizedTest {
public static void main(String[] args) {
final Output output=new Output();
//启动两个线程,都是用output对象中的output方法进行打印
new Thread(new Runnable() {
@Override
public void run() {
output.output("weijianfei");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
output.output("dreambroken");
}
}).start();
}
}
class Output{
public synchronized void output(String s){
for(int i=0;i<s.length();i++){
try {
Thread.sleep(100);//模拟处理一个字符需要100毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print(s.charAt(i));
}
System.out.println();
}
}
运行结果
dreambroken
weijianfei
是我们期待的结果。
我们知道synchronized还可以修饰代码块,所以上面的public void output(String s)方法,可以改成这样.
public void output(String s){
synchronized (this) {
for (int i = 0; i < s.length(); i++) {
try {
Thread.sleep(100);// 模拟处理一个字符需要100毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print(s.charAt(i));
}
System.out.println();
}
}
当使用synchronized修饰代码块时,一定要保证多个线程抢的是同一个对象的锁。synchronized(this)抢的是当前对象的锁,对于上面程序,抢的是output对象的锁(final Output output=new Output()这里的output)。如果每个线程不是竞争同一把锁,那大家都拿到锁,大家都执行,那就没意义了,比如代码修改如下。
public void output(String s){
synchronized (s) {
for (int i = 0; i < s.length(); i++) {
try {
Thread.sleep(100);// 模拟处理一个字符需要100毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print(s.charAt(i));
}
System.out.println();
}
}
这样一来,上面的两个线程,一个拿到的"weijianfei"对象的锁,一个拿到的是"dreambroken"的锁,大家都拿到锁,所以他们都可以执行,那结果又是不可预期的了。所以要注意保证多个线程竞争的是同一个对象的锁.
上面红字部分说道,同一个对象内的多个方法用synchronized修饰时,如果某个线程在执行其中一个synchronized修饰的方法了,那么其他线程就无法访问任何synchronized修饰的方法(非synchronized修饰的方法可以).
package thread;
public class SynchronizedTest {
public static void main(String[] args) {
final Output output=new Output();
//启动两个线程,都是用output对象中的output方法进行打印
new Thread(new Runnable() {
@Override
public void run() {
output.output("weijianfei");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
output.output2("dreambroken");
}
}).start();
}
}
class Output{
public synchronized void output(String s) {
for (int i = 0; i < s.length(); i++) {
try {
Thread.sleep(100);// 模拟处理一个字符需要100毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print(s.charAt(i));
}
System.out.println();
}
public synchronized void output2(String s) {
for (int i = 0; i < s.length(); i++) {
try {
Thread.sleep(100);// 模拟处理一个字符需要100毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print(s.charAt(i));
}
System.out.println();
}
}
运行结果
weijianfei
dreambroken
仍然是我们期待的结果。
但是,需要注意的是,如果某个方法有static修饰时,那就有需要注意的地方了。比如public synchronized void output(String s)和public static synchronized void output2(String s)竞争的并不是同一个对象的锁.
package thread;
public class SynchronizedTest {
public static void main(String[] args) {
final Output output=new Output();
//启动两个线程,都是用output对象中的output方法进行打印
new Thread(new Runnable() {
@Override
public void run() {
output.output("weijianfei");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
output.output2("dreambroken");
}
}).start();
}
}
class Output{
public synchronized void output(String s) {
for (int i = 0; i < s.length(); i++) {
try {
Thread.sleep(100);// 模拟处理一个字符需要100毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print(s.charAt(i));
}
System.out.println();
}
public static synchronized void output2(String s) {
for (int i = 0; i < s.length(); i++) {
try {
Thread.sleep(100);// 模拟处理一个字符需要100毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print(s.charAt(i));
}
System.out.println();
}
}
运行结果
dwreiejaimabnrfoeki
en
这是因为public synchronized void output(String s)对应的锁是this,当前对象output(final Output output=new Output()),而public static synchronized void output2(String s)对应的是Class(Output.class)对象,前者对象位于堆中,后者对象位于方法区中.
synchronized还可以修饰变量,为了让多个线程竞争同一个对象的锁,所以变量应该是全局变量.
class Output{
String lock="";
public void output(String s) {
synchronized (lock) {
for (int i = 0; i < s.length(); i++) {
try {
Thread.sleep(100);// 模拟处理一个字符需要100毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print(s.charAt(i));
}
System.out.println();
}
}
}
总结:必须保证每个线程竞争资源时,把资源进行加锁时,每个线程抢的是同一把锁。
|