为何要实现线程同步
多线程机制的目的是为了能并发执行程序,为何要实现线程间同步,使其看起来像是让每个线程按顺序执行呢?答案是:当线程之间没有“相交点”(共享资源)时,不需要线程同步,但是当操作某个线程间共享资源(如静态变量),就需要实现线程同步。
java提供的线程同步机制
一.synchronized关键字
1.当synchronized关键字修饰方法
-
a.大部分人熟悉的线程同步方式是上锁,而synchronized关键字实现同步的方式其实也是上锁。那么上锁就是要看给什么东西上锁,是方法还是对象,当synchronized关键字修饰方法时,是给调用该方法的对象上的锁(因为一个类有多个对象,通过哪个对象调用的方法就给哪个个对象上锁)。那么这个锁是什么锁呢,是每个对象都拥有的内置锁。
-
b.使用synchronized关键字的例子:
/*
* synMethrod.java文件
* 定义了一个synchronized方法methrod,该方法先打印一次字符串,然后sleep一秒,在打印一次字符串
*/
public class synMethrod{
public synchronized void methrod(String arg) {
System.out.println(arg + "start");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(arg + "end");
}
}
***************************************************************
/*
* ThradPractice.java文件
*/
import java.lang.*;
import java.util.*;
public class ThreadPractice{
public static void main(String[] args) {
synMethrod m = new synMethrod();
Thread t1 = new MyThread(m,"Thread1");
Thread t2 = new MyThread(m,"Thread2");
t1.start();
t2.start();
}
}
class MyThread extends Thread{
private synMethrod m;
private String s;
public MyThread(synMethrod m,String s) {
this.m = m;
this.s = s;
}
@Override
public void run() {
m.methrod(s);
}
}
上述代码的运行结果:
可以看出,在线程1调用methrod方法后,线程2并没有马上进行methord方法,而是等线程1执行完methrod方法后,线程2才执行methrod方法。如果将methrod方法的synchronized关键字去掉,那么程序的运行结果如下:
可以看出此时线程1和线程2同时进入了methrod方法执行。
注意:对于上述代码,由于在java中,将对象作为参数时,是进行了引用传递,所以在线程1和线程2中的synMethrod对象是同一个对象,synchronized关键字是将该synMethrod对象的内置锁进行上锁
2.当synchronized关键字修饰静态方法
a.静态成员不属于任何对象,那么当synchronized关键字上的锁可以理解为类锁,当synchronized关键字修饰一般成员方法时上的锁可以理解为对象锁,属于某个对象。
b.下面将举一个例子,可以看出“类锁”和“对象锁不冲突”。在synMethrod类中新定义一个静态方法。线程1调用原methrod方法,线程2调用staticMethrod方法。如下:
/*
* synMethrod.java文件
* 新定义了staticMethrod方法
*/
public class synMethrod{
public void methrod(String arg) {
System.out.println(arg + "start");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(arg + "end");
}
public static synchronized void staticMethord(String arg) {
System.out.println(arg + "start (static)");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(arg + "end (static)");
}
}
********************************************************************
/*
* ThreadPractice.java
* 线程1执行一般成员方法,线程2执行静态成员方法
*/
import java.lang.*;
import java.util.*;
public class ThreadPractice{
public static void main(String[] args) {
synMethrod m = new synMethrod();
Thread t1 = new MyThread(m,"Thread1");
Thread t2 = new MyThread2(m,"Thread2");
t1.start();
t2.start();
}
}
class MyThread extends Thread{
private synMethrod m;
private String s;
public MyThread(synMethrod m,String s) {
this.m = m;
this.s = s;
}
@Override
public void run() {
m.methrod(s);
}
}
class MyThread2 extends Thread{
private synMethrod m;
private String s;
public MyThread2(synMethrod m,String s) {
this.m = m;
this.s = s;
}
@Override
public void run() {
m.staticMethord(s);
}
}
上述代码的执行结果如下:
可以看出,线程1和线程2并没有实现同步,证明“类锁”和"对象锁"之间不存在互斥。
如果将static关键字删掉,上述程序运行结果如下:
从结果可以看出线程1和线程2实现了互斥,因为他们都使用了同一个对象的“对象锁”。
3.synchronized关键字修饰代码块
为了对线程同步进行更加精确地控制,可以synchronized关键字来修饰代码块。而synchronized关键字在修饰代码块时又分为两种类型,即使用“类锁”还是使用“对象锁”。
- 使用类锁
语法:
synchronized(类名.class){
//需要进行同步的代码块
}
- 使用对象锁
语法
synchronized(this){
//需要进行同步的代码块
}
例子如下:
/*
* synMethrod文件
*/
public class synMethrod{
public void methrod(String arg) {
//使用对象锁
synchronized(this){
System.out.println(arg + "start");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(arg + "end");
}
}
public void staticMethord(String arg) {
//使用类锁
synchronized(synMethrod.class) {
System.out.println(arg + "start (static)");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(arg + "end (static)");
}
}
}
总结:使用synchronized关键字进行现场同步时需要理解一下两点
1.synchronized关键字是通过上锁来实现了
2.synchronized关键字使用的锁分为“类锁”和“对象锁”,根据不同使用场景选择不同的锁(即synchronized关键字的使用方式)