一. 锁的定义
锁就是synchronized 关键字,记住synchronized(this )是锁定当前对象。在函数m1()里面写synchronized( this ),这个和public synchronized void m1() 等价。
但是他只锁定当前对象的synchronized 大括号内的话,其他,这个synchronized 不去锁定。这个对象的其他方法 / 变量( 被synchronized 语句块改过后的) 还能被其他线程调用。锁定了这个对象,但是不是说完全锁定了,针对这个方法的锁定期间,其他方法照样可以修改变量值。 所以,如果你想把类变量 ( 银行账户值 ) 值控制的完全,不会被错误修改,要把所有控制这个变量的方法全部考虑到,该synchronized 要synchronized 。
二. 锁的粒度
如果是对象变量,锁this,即只对操作这个对象的众多线程有限制作用,对其他类对象无关,如果是类变量,锁class。锁谁,就是拿到谁的锁,那么其他需要这个锁的语句块,就得排队,但是不需要这个锁的语句块,直接执行不受阻碍。所以就有了锁obja,锁objb这种有多把锁的情况。而不是简单的只锁this 。也就是说m1() 要等着锁的到来才能执行,而m2()不需要等着锁就能执行( synchronized )。
三. 锁的代码
锁代码1:
package test.java.Thread;
public class TestSync implements Runnable{
Timer timer = new Timer();
public static void main(String[] args) {
TestSync test = new TestSync();
Thread t1 = new Thread(test);
Thread t2 = new Thread(test);//注意这种用法,Runnable() 参数不为空,则调用t1.start() 时候,会调用test 的run()方法
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
@Override
public void run() {
timer.add(Thread.currentThread().getName());
}
}
class Timer {
private int num =0;
public void add(String name){
synchronized (this) { //很明显不加这一句,打印结果是错的,解决办法就是在执行这7句话的过程中,请你把我当前的对象锁住 //这里加this 叫锁定当前对象
num++; //锁定当前对象的意思是在执行这个大括号里面的语句之中,一个线程执行的过程之中,不会被另外一个线程打断,一个线程已经进入到
try{ //我这个锁定的区域里面了,你放心,不可能有另外一个线程也在这里面,这叫互斥锁。
Thread.sleep(1);
}catch(InterruptedException e){
}
System.out.println(name+" 你是第"+num+"个使用timer 的线程");//t1 你是第2个使用timer 的线程 t2 你是第2个使用timer 的线程
}//end synchronized
}
// public synchronized void add(String name){//这个是上面的简便写法,意思是在执行这个函数的过程之中,锁定当前对象
// .
// .
// .
// }
}
死锁代码2
package test.java.Thread;
/**
*
* @author jalo
* 这是两个线程死锁,哲学家问题是多个线程转着圈的死锁
* 解决死锁的办法之一,把锁的粒度加粗一些,你锁定一个对象不就行了?非要锁定里面的两个对象(o1,o2),
* 当然解决死锁还有很多其他办法。如果不写系统级的程序,很难碰到死锁的问题。因为死锁都被中间件厂商给解决了。
* 如果有机会写系统级的程序,比如自己实现一个数据库的连接池,就会自己实现各种锁,就会去考虑控制死锁。
*
*/
public class TestDeadLock implements Runnable{
int flag ;
static Object o1 = new Object();
static Object o2 = new Object();
@Override
public void run() {
if(flag==0){
synchronized (o1) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2) {
System.out.println("flag==0");
}
}
}
else if (flag==1){
synchronized (o2) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1) {
System.out.println("flag==1");
}
}
}
}
public static void main(String[] args) {
TestDeadLock td1 = new TestDeadLock();
TestDeadLock td2 = new TestDeadLock();
td1.flag = 0;
td2.flag = 1;
Thread t1 = new Thread(td1);
Thread t2 = new Thread(td2);
t1.start();
t2.start();
}
}
锁定对象期间访问其对象的面试题,代码3
package test.java.Thread;
/**
*
* @author jalo
* 这是个面试题,一个线程在调用m1()期间,另一个线程可以调用m2()吗?如果可以,那么m2()调用的结果是b=0还是b=1000?
* 可以调用,b=1000
* synchronized(this)是锁定当前对象,但是他只锁定当前对象的synchronized 大括号内的话,
* 这个对象的其他方法 / 变量,这个synchronized 不去锁定。这个对象的其他方法 / 变量( 被synchronized 语句块改过后的) 还能被其他线程调用。
*/
public class ThreadLockQuestion {
int b = 0;
public synchronized void m1() throws Exception{
b = 1000;
Thread.sleep(1000000000);
System.out.println("b= "+b);
}
public void m2(){
System.out.println(b);
}
public static void main(String[] args) {
ThreadLockQuestion tlq = new ThreadLockQuestion();
ThreadLock tl1 = new ThreadLock(tlq);
ThreadLock tl2 = new ThreadLock(tlq);
tl1.flag = 1;
tl2.flag = 2;
Thread t1 = new Thread(tl1);
Thread t2 = new Thread(tl2);
t1.start();
t2.start();
}
}
class ThreadLock implements Runnable{
ThreadLockQuestion tlq ;
int flag;
ThreadLock(ThreadLockQuestion tlq){
this.tlq = tlq;
}
@Override
public void run() {
try {
if(flag==1){
tlq.m1();
System.out.println("flag==1");
}
if(flag==2){
Thread.sleep(1000);
tlq.m2(); //这里m2()看到的是1000 ,
System.out.println("flag==2");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
结果:
1000 //说明是m2()的调用结果,而且b 已经被锁语句块给改成了1000,并且锁住对象期间,还能访问这个对象的变量,访问结果是最新值
flag==2
………… //一大堆时间过后
b=1000
flag==1
锁定对象期间修改其变量的面试题,代码4
package test.java.Thread;
/**
*
* @author jalo
* 这是个面试题,一个线程在调用m1()期间,另一个线程可以调用m2()吗?如果可以,那么m2()调用的结果是b=0还是b=1000?
* 可以调用,b=1000
* synchronized(this)是锁定当前对象,但是他只锁定当前对象的synchronized 大括号内的话,即m1()方法中的语句
* 锁定了这个对象,但是不是说完全锁定了这个对象,针对这个方法m1()的锁定期间,m2()方法照样可以修改变量b 的值。
*
*/
public class ThreadLockQuestion {
int b = 0;
public synchronized void m1() throws Exception{
b = 1000;
Thread.sleep(5000);
System.out.println("b= "+b); //这个是b=2000,意思是虽然m1()被锁定期间,但是m2()照样能改b 的值
}
public void m2() throws Exception{
Thread.sleep(2500);
b=2000;
System.out.println(b);
}
public static void main(String[] args) {
ThreadLockQuestion tlq = new ThreadLockQuestion();
ThreadLock tl1 = new ThreadLock(tlq);
ThreadLock tl2 = new ThreadLock(tlq);
tl1.flag = 1;
tl2.flag = 2;
Thread t1 = new Thread(tl1);
Thread t2 = new Thread(tl2);
t1.start();
t2.start();
}
}
class ThreadLock implements Runnable{
ThreadLockQuestion tlq ;
int flag;
ThreadLock(ThreadLockQuestion tlq){
this.tlq = tlq;
}
@Override
public void run() {
try {
if(flag==1){
tlq.m1();
System.out.println("flag==1");
}
if(flag==2){
Thread.sleep(1000);
tlq.m2(); //这里m2()看到的是1000 ,
System.out.println("flag==2");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
结果:
2000 //以下是m2()调用结果
flag==2
b= 2000 // 以下是m1()调用结果,可见m1()锁定期间,m2()照样能改b 的值。使用同步要非常小心,你m1()同步了,但是其他线程可以自由访问任意未同步的方法m2(),其访 问可以对你同步的方法m1()产生影响。所以要仔细考虑函数加不加同步,加了同步,导致效率变低,不加同步,有可能导致数据不一致的情况。
flag==1 //
代码4改进,代码5
如果你想把类变量 ( 银行账户值 ) 值控制的完全,不会被错误修改,要把所有控制这个变量的方法全部考虑到,该synchronized 要synchronized。这里把m2()也改成synchronized 即可。
改了这一句,加了synchronized,public synchronized void m2() throws Exception{
package test.java.Thread;
/**
*
* @author jalo
* 这是个面试题,一个线程在调用m1()期间,另一个线程可以调用m2()吗?如果可以,那么m2()调用的结果是b=0还是b=1000?
* 可以调用,b=1000
* synchronized(this)是锁定当前对象,但是他只锁定当前对象的synchronized 大括号内的话,即m1()方法中的语句
* 锁定了这个对象,但是不是说完全锁定了这个对象,针对这个方法m1()的锁定期间,m2()方法照样可以修改变量b 的值。
*
*/
public class ThreadLockQuestion {
int b = 0;
public synchronized void m1() throws Exception{
b = 1000;
Thread.sleep(5000);
System.out.println("b= "+b); //这个是b=2000,意思是虽然m1()被锁定期间,但是m2()照样能改b 的值
}
public synchronized void m2() throws Exception{
Thread.sleep(2500);
b=2000;
System.out.println(b);
}
public static void main(String[] args) {
ThreadLockQuestion tlq = new ThreadLockQuestion();
ThreadLock tl1 = new ThreadLock(tlq);
ThreadLock tl2 = new ThreadLock(tlq);
tl1.flag = 1;
tl2.flag = 2;
Thread t1 = new Thread(tl1);
Thread t2 = new Thread(tl2);
t1.start();
t2.start();
}
}
class ThreadLock implements Runnable{
ThreadLockQuestion tlq ;
int flag;
ThreadLock(ThreadLockQuestion tlq){
this.tlq = tlq;
}
@Override
public void run() {
try {
if(flag==1){
tlq.m1();
System.out.println("flag==1");
}
if(flag==2){
Thread.sleep(1000);
tlq.m2(); //这里m2()看到的是1000 ,
System.out.println("flag==2");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
结果:
b= 1000
flag==1 //由此可见,本来应该m2()调用先出结果,但是却死等m1()结束,说明synchronized m2() 起了作用。
2000
flag==2 //线程1,在调用 m1() 的执行的过程中,线程2要调用其他的 synchronized 的函数也会卡死,直到线程1执行完,排队的线程2 的函数再执行。
//于是,m1(),m2() 这种都是synchronized 的函数,又一样了,又有顺序了,又不能打乱了,无论几个线程执行他们,都是按照顺序。谁先调用(谁
先拿到this 锁,这里是t1 先拿到),谁就执行,执行完,再执行后排队的,一个个调用着走。这其实就是第一行说的和synchronized( this ) 等价,故
t1 调用 m1() 期间拿到this的锁,直到 t1 结束,t2 调用m2() 才能拿到this 的锁去执行m2()。这叫从原理上理解。