一、synchronized锁机制
1、同步锁
脏读:一个常见的概念。在多线程中,难免会出现在多个线程中对同一个对象的实例变量进行并发访问的情况,如果不做正确的同步处理,那么产生的后果就是"脏读",也就是取到的数据其实是被更改过的。
示例:
public class Lock_JobRun extends Thread{
private String str;
private Lock_RunFuncion lockRunFuncion;
public Lock_JobRun(String str,Lock_RunFuncion lockRunFuncion) {
this.str=str;
this.lockRunFuncion=lockRunFuncion;
}
public void run(){
try {
lockRunFuncion.addNumber(str);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Lock_RunFuncion{
private int num=0;
public void addNumber(String str) throws InterruptedException {
if(str.equals("a")){
num=100;
System.out.println(Thread.currentThread().getName()+" str: "+str+" 执行完毕睡觉去了..");
Thread.sleep(1000);
}else{
num=200;
System.out.println(Thread.currentThread().getName()+" str: "+str+" 执行完毕");
}
System.out.println(Thread.currentThread().getName()+" 执行结果:"+num);
}
}
public class RunThread {
public static void main(String[] args) throws InterruptedException {
Lock_RunFuncion lockRunFuncion=new Lock_RunFuncion();
Lock_JobRun lockJobRunA=new Lock_JobRun("a",lockRunFuncion);
Lock_JobRun lockJobRunB=new Lock_JobRun("b",lockRunFuncion);
lockJobRunA.start();
lockJobRunB.start();
}
}
结果:
Thread-0 str: a 执行完毕睡觉去了…
Thread-1 str: b 执行完毕
Thread-1 执行结果:200
Thread-0 执行结果:200
结论:一个对象分别让两个线程去执行,都是异步的,Thread-0调用完后休眠1S,过程中被Thread-1给修改了,因此最后Thread-0打印到的是被Thread-1修改后的
这样数据就不正确了啊,这时可以使用同步锁 synchronized
加上synchronized 时呢?
代码示例:
public class Lock_RunFuncion{
private int num=0;
public synchronized void addNumber(String str) throws InterruptedException {
if(str.equals("a")){
num=100;
System.out.println(Thread.currentThread().getName()+" str: "+str+" 执行完毕睡觉去了..");
Thread.sleep(1000);
}else{
num=200;
System.out.println(Thread.currentThread().getName()+" str: "+str+" 执行完毕");
}
System.out.println(Thread.currentThread().getName()+" 执行结果:"+num);
}
}
执行结果:
Thread-0 str: a 执行完毕睡觉去了…
Thread-0 执行结果:100
Thread-1 str: b 执行完毕
Thread-1 执行结果:200
总结:Thread-1 必须等Thread-0执行完后才去执行
2、多个对象多个锁
代码示例:
Lock_RunFuncion lockRunFuncionA=new Lock_RunFuncion();
Lock_RunFuncion lockRunFuncionB=new Lock_RunFuncion();
Lock_JobRun lockJobRunA=new Lock_JobRun("a",lockRunFuncionA);
Lock_JobRun lockJobRunB=new Lock_JobRun("b",lockRunFuncionB);
lockJobRunA.start();
lockJobRunB.start();
结果:
Thread-1 str: b 执行完毕
Thread-0 str: a 执行完毕睡觉去了…
Thread-1 执行结果:200
Thread-0 执行结果:100
总结:看到没有,同步锁没有同步执行了我。
之前实例的同步锁,应该是对象执行完后到下一个对象。其实这只是针对同一个对象才会有等待的情况。对象都不同了都不互相干扰。
3、synchronized方法与锁对象
(1)对象内方法锁与非方法锁
代码示例:
public class Lock_RunFuncion_1 {
public synchronized void synchrMethod() throws InterruptedException {
System.out.println("synchrMethod "+Thread.currentThread().getName()+" start");
Thread.sleep(3000);
System.out.println("synchrMethod "+Thread.currentThread().getName()+" end");
}
public void method() throws InterruptedException {
System.out.println("method "+Thread.currentThread().getName()+" start");
System.out.println("method "+Thread.currentThread().getName()+" end");
}
}
public class Lock_JobRun_2 extends Thread{
private Lock_RunFuncion_1 lockRunFuncion1;
public Lock_JobRun_2(Lock_RunFuncion_1 lockRunFuncion1) {
this.lockRunFuncion1 = lockRunFuncion1;
}
public void run(){
try {
lockRunFuncion1.synchrMethod();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Lock_JobRun_1 extends Thread{
private Lock_RunFuncion_1 lockRunFuncion1;
public Lock_JobRun_1(Lock_RunFuncion_1 lockRunFuncion1) {
this.lockRunFuncion1 = lockRunFuncion1;
}
public void run(){
try {
lockRunFuncion1.method();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
Lock_RunFuncion_1 lockRunFuncion1=new Lock_RunFuncion_1();
Lock_JobRun_1 lockJobRun1=new Lock_JobRun_1(lockRunFuncion1);
Lock_JobRun_2 lockJobRun2=new Lock_JobRun_2(lockRunFuncion1);
lockJobRun1.start();
lockJobRun2.start();
}
结果:
method Thread-0 start
synchrMethod Thread-1 start
method Thread-0 end
synchrMethod Thread-1 end
总结:一个对象中可以存在 异步和同步方法,异步方法当然也不需要等待同步执行完了,如果两个方法都是同步的,会依次执行
验证一个对象里都加方法锁:
代码示例:
public synchronized void method() throws InterruptedException {
System.out.println("method "+Thread.currentThread().getName()+" start");
System.out.println("method "+Thread.currentThread().getName()+" end");
}
结果:
method Thread-0 start
method Thread-0 end
synchrMethod Thread-1 start
synchrMethod Thread-1 end
(2)synchronized锁重入
代码示例:
public class Lock_RunFuncion_2 {
public synchronized void method1() {
System.out.println("m1");
method2();
}
public synchronized void method2() {
System.out.println("m2");
method3();
}
public synchronized void method3() {
System.out.println("m3");
}
}
public class Lock_JobRun_3 extends Thread{
public void run(){
Lock_RunFuncion_2 lock_runFuncion_2=new Lock_RunFuncion_2();
lock_runFuncion_2.method1();
}
}
public static void main(String[] args) {
Lock_JobRun_3 lockJobRun3=new Lock_JobRun_3();
lockJobRun3.start();
}
结果:
m1
m2
m3
总结:可以看到依次打印了。这证明了对象可以再次获取自己的内部锁。这种锁重入的机制,也支持在父子类继承的环境中。
注意: 发生异常是自动释放锁
(3)synchronized同步代码块
有没有想过用synchroized同对象执行的方法都要进行等待,那么方法里含有不需要等待的代码吗?
代码示例:
public class sync_RunFuncion {
int count=1000000000;
public void method() throws InterruptedException {
System.out.println("not synchronized start --- "+Thread.currentThread().getName());
for (int i=0;i<count;i+=2) {
i--;
}
System.out.println("not synchronized find --- "+Thread.currentThread().getName());
synchronized (this){
System.out.println("synchronized start --- "+Thread.currentThread().getName());
for (int i=0;i<count;i+=2) {
i--;
}
System.out.println("synchronized find --- "+Thread.currentThread().getName());
}
}
}
public class Sync_JobRun extends Thread {
private sync_RunFuncion syncRunFuncion;
public Sync_JobRun(sync_RunFuncion syncRunFuncion) {
this.syncRunFuncion=syncRunFuncion;
}
@Override
public void run(){
syncRunFuncion.method();
}
}
public static void main(String[] args) throws InterruptedException {
sync_RunFuncion syncRunFuncion=new sync_RunFuncion();
Sync_JobRun syncJobRun=new Sync_JobRun(syncRunFuncion);
syncJobRun.start();
Sync_JobRun syncJobRun2=new Sync_JobRun(syncRunFuncion);
syncJobRun2.start();
}
结果:
not synchronized start — Thread-1
not synchronized start — Thread-0
not synchronized find — Thread-1
synchronized start — Thread-1
not synchronized find — Thread-0
synchronized find — Thread-1
synchronized start — Thread-0
synchronized find — Thread-0
总结:可以看到线程1还没调用完not synchronized find的方法,线程0就开始调用not synchronized start方法了。
而synchronized 方法却老老实实地执行完start-find…。
可以得出在synchronized内当某个线程在访问时,会被阻塞。而之外的却不会。
这样做大大地提高了效率,异步方法也不需要等待同步方法释放锁了
。
(4)两个synchronized块之间具有互斥性
代码示例:
public class sync_RunFuncion {
public void opposite_Tom(){
synchronized (this){
try {
System.out.println("opposite_Tom start.. --- "+Thread.currentThread().getName());
Thread.sleep(1000);
System.out.println("opposite_Tom end.. --- "+Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void opposite_Jones(){
synchronized (this) {
try {
System.out.println("opposite_Jones start.. --- "+Thread.currentThread().getName());
Thread.sleep(1000);
System.out.println("opposite_Jones end.. --- "+Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Sync_Op_Tom_JobRun extends Thread{
private sync_RunFuncion syncRunFuncion;
public Sync_Op_Tom_JobRun(sync_RunFuncion syncRunFuncion) {
this.syncRunFuncion=syncRunFuncion;
}
@Override
public void run(){
syncRunFuncion.opposite_Jones();
}
}
public class Sync_Op_Jones_JobRun extends Thread{
private sync_RunFuncion syncRunFuncion;
public Sync_Op_Jones_JobRun(sync_RunFuncion syncRunFuncion) {
this.syncRunFuncion=syncRunFuncion;
}
@Override
public void run(){
syncRunFuncion.opposite_Tom();
}
}
public static void main(String[] args) {
sync_RunFuncion syncRunFuncion=new sync_RunFuncion();
for(int i=0;i<2;i++) {
Sync_Op_Jones_JobRun syncOpJonesJobRun = new Sync_Op_Jones_JobRun(syncRunFuncion);
Sync_Op_Tom_JobRun syncOpTomJobRun = new Sync_Op_Tom_JobRun(syncRunFuncion);
syncOpJonesJobRun.start();
syncOpTomJobRun.start();
}
}
结果:
opposite_Tom start… — Thread-2
opposite_Tom end… — Thread-2
opposite_Jones start… — Thread-3
opposite_Jones end… — Thread-3
opposite_Jones start… — Thread-1
opposite_Jones end… — Thread-1
opposite_Tom start… — Thread-0
opposite_Tom end… — Thread-0
总结:这里看得不是很仔细,到sleep会暂停一会,依次执行,synchronized 块的是获取对象锁,锁的是整个对象,要么是opposite_Tom执行要么opposite_Jones执行,看哪个线程先获取锁,谁先执行,执行期间会将对象阻塞。
(5)synchronized块和synchronized方法
代码示例:
public class sync_RunFuncion {
public void sync_Block(){
synchronized (this){
try {
System.out.println("opposite_Tom start.. --- "+Thread.currentThread().getName());
Thread.sleep(2000);
System.out.println("opposite_Tom end.. --- "+Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void sync_Function(){
System.out.println("opposite_Jones start.. --- "+Thread.currentThread().getName());
System.out.println("opposite_Jones end.. --- "+Thread.currentThread().getName());
}
}
public class Sync_Op_Jones_JobRun extends Thread{
private sync_RunFuncion syncRunFuncion;
public Sync_Op_Jones_JobRun(sync_RunFuncion syncRunFuncion) {
this.syncRunFuncion=syncRunFuncion;
}
@Override
public void run(){
//---调用sync码块与sync方法特性
syncRunFuncion.sync_Block();
}
}
public class Sync_Op_Tom_JobRun extends Thread{
private sync_RunFuncion syncRunFuncion;
public Sync_Op_Tom_JobRun(sync_RunFuncion syncRunFuncion) {
this.syncRunFuncion=syncRunFuncion;
}
@Override
public void run(){
//---调用sync码块与sync方法特性
syncRunFuncion.sync_Function();
}
}
public static void main(String[] args) {
sync_RunFuncion syncRunFuncion=new sync_RunFuncion();
for(int i=0;i<3;i++) {
Sync_Op_Jones_JobRun syncOpJonesJobRun = new Sync_Op_Jones_JobRun(syncRunFuncion);
Sync_Op_Tom_JobRun syncOpTomJobRun = new Sync_Op_Tom_JobRun(syncRunFuncion);
syncOpJonesJobRun.start();
syncOpTomJobRun.start();
}
}
}
结果:
opposite_Jones start… — Thread-1
opposite_Jones end… — Thread-1
opposite_Tom start… — Thread-0
…等待2S 自己加的忽略就好
opposite_Tom end… — Thread-0
opposite_Jones start… — Thread-5
opposite_Jones end… — Thread-5
opposite_Tom start… — Thread-4
…等待2S 自己加的忽略就好
opposite_Tom end… — Thread-4
opposite_Tom start… — Thread-2
…等待2S 自己加的忽略就好
opposite_Tom end… — Thread-2
opposite_Jones start… — Thread-3
opposite_Jones end… — Thread-3
结论:synchronized方法和synchronized代码块都是调用同一个对象锁的。
(6)将任意对象作为对象监视器
前面都使用synchronized(this)的格式来同步代码块,其实Java还支持对"任意对象"作为对象监视器来实现同步的功能。
这个"任意对象"大多数是实例变量及方法的参数,使用格式为synchronized(非this对象)。看一下将任意对象作为对象监视器的使用例子:
代码示例:
public class Sync_Object_JobRun extends Thread {
private sync_Object_RunFuncion syncRunFuncion;
public Sync_Object_JobRun(sync_Object_RunFuncion syncRunFuncion) {
this.syncRunFuncion=syncRunFuncion;
}
@Override
public void run(){
try {
syncRunFuncion.method();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class RunThread {
public static void main(String[] args) {
sync_Object_RunFuncion syncRunFuncion=new sync_Object_RunFuncion();
for(int i=0;i<4;i++){
Sync_Object_JobRun syncObjectJobRun=new Sync_Object_JobRun(syncRunFuncion);
syncObjectJobRun.start();
}
}
}
public class sync_Object_RunFuncion {
private String str=new String();
public void method() throws InterruptedException {
int count=1000000000;
synchronized (str){
System.out.println("synchronized start --- "+Thread.currentThread().getName());
for (int i=0;i<count;i+=2) {
i--;
}
Thread.sleep(1000);
System.out.println("synchronized find --- "+ Thread.currentThread().getName());
}
}
}
結果:
synchronized start — Thread-1
synchronized find — Thread-1
synchronized start — Thread-2
synchronized find — Thread-2
synchronized start — Thread-3
synchronized find — Thread-3
synchronized start — Thread-0
synchronized find — Thread-0
总结:synchronized放非this的对象也实现了同步的效果,实际上在new的时候就已经实例化了对象,调用的都是同一个。
只要对象的引用不变,即使改变了对象的属性,运行结果依然是同步的。