一、synchronized的使用
java的synchronized关键字能够修饰方法和语句块,也就是同步方法和同步语句块。synchronized()并不是保护数据不被访问,只是保证同一时刻只有一个线程在运行。
- 数据私有
- 所有的访问都同步化
- 用数据本身做钥匙
synchronized (object) { // } 钥匙在对象中,而不在代码中。 每个对象有一个钥匙
为了执行synchronized()块,线程需要得到对象中的钥匙。一旦获得了钥匙,对象就不再拥有钥匙。 如果当线程要执行synchronized()时,钥匙不在对象中,线程就stall了。一直到钥匙还到了对象中,才被这个线程拿到。 当线程离开synchorized()块,钥匙就还给了对象。
对象锁标志:
1、每个对象都有一个标志,它可以被认为是“锁标志”。
2、synchronized允许和锁标志交互。
释放锁标志:
1、线程执行到synchronized()代码块末尾时释放
2、synchronized()代码块抛出中断或异常时自动释放
public class TestSyc {
public static void main(String[] args) {
Shop p=new Shop();
ThreadTest t1=new ThreadTest(p);
ThreadTest t2=new ThreadTest(p);
t1.start();
t2.start();
}
static class ThreadTest extends Thread{
Shop p;
public ThreadTest(Shop p){
this.p=p;
}
@Override
public void run(){
p.count();
}
}
}
//同步方法/同步代码块:解决多个线程对象操作同一个数据时发生数据紊乱的情况
//能够确保当前只有一个线程对象操作该同步方法/ 同步代码块中的内容
class Shop {
int num;
public /*synchronized*/ void count(){//同步方法 默认拿到当前对象的锁
synchronized (this) {//同步代码块 synchronized(同步锁(对象)){代码}
num++;
Thread t = Thread.currentThread();
try {
t.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(t.getName()+"你是第"+num+"个人来访问的");
}
}
}
输出结果:
Thread-1你是第1个人来访问的
Thread-0你是第2个人来访问的
二、死锁
两个线程相互等待来自对方的锁,它不能被监测到或避免。
它可以通过以下方法来避免:
1、决定获取锁的次序
2、始终遵照这个次序
3、按照相反的次序释放锁
下面看一个死锁的例子:
//死锁:两个线程相互等待来自对方的锁
//同步代码块的相互嵌套并不一定会造成死锁
//避免死锁:1、尽量不使用同步代码块的嵌套 2、改变锁的顺序
public class TestDead {
public static void main(String[] args) {
Object o1=new Object();
Object o2=new Object();
Thread1 t1=new Thread1(o1,o2);
t1.setName("线程1");
Thread2 t2=new Thread2(o1,o2);
t2.setName("线程2");
t1.start();
t2.start();
}
static class Thread1 extends Thread{
Object o1;
Object o2;
public Thread1(Object o1,Object o2){
this.o1=o1;
this.o2=o2;
}
@Override
public void run() {
synchronized (o1) {
System.out.println(getName()+"我有我的o1,等待拿你的o2");
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2) {
System.out.println(getName()+"我拿到你的o2");
}
}
}
}
static class Thread2 extends Thread{
Object o1;
Object o2;
public Thread2(Object o1,Object o2){
this.o1=o1;
this.o2=o2;
}
@Override
public void run() {
synchronized (o2) {
System.out.println(getName()+"我有我的o2,等待拿你的o1");
synchronized (o1) {
System.out.println(getName()+"我拿到了o1");
}
}
}
}
}
三、synchronized的原理
每个对象有个线程池,对象可以调用wait方法让对该对象同步的线程加入到该线程池中,或者调用notify方法让等待的一个线程离开此线程池。如果是notifyall方法,则让该对象的等待池中的对象都离开。
每个对象都包含了一把锁(也叫作“监视器”),它自动成为对象的一部分(不必为此写任何特殊的代码)。当线程第一次给对象加锁的时候,计数器会加1,离开时会减1。同样任务是可重入的,每次重入也是加1,离开减1。synchronized是独占式的,拿到对象锁才能继续,没有获取到锁就会阻塞。调用任何synchronized方法时,对象就会被锁定,不可再调用那个对象的其他任何synchronized方法,除非第一个方法完成了自己的工作,并解除锁定。
参考资料: