分享一下我老师大神的人工智能教程吧。零基础,通俗易懂!风趣幽默!http://www.captainbed.net/
也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!
1.在java多线程编程中对象锁、类锁、同步机制synchronized详解:
对象锁:在java中每个对象都有一个唯一的锁,对象锁用于对象实例方法或者一个对象实例上面的。
类锁:是用于一个类静态方法或者class对象的,一个类的实例对象可以有多个,但是只有一个class对象。
同步机制synchronized:synchronized关键字用于修饰方法或者单独的synchronized代码块,当一个线程想执行synchronized中的内容时,必须先获取到对象锁,当对象锁没有线程占用时,进入synchronized方法会自动获取到对象锁,执行完毕后会自动释放锁,如果对象锁被A线程占用,B线程想执行synchronized的代码只能等待A个线程执行完毕后,释放对象锁,B线程才能获取到对象锁进入方法执行。一个线程获得对象A的锁,也可以获得对象B的锁,两个不同类的对象锁没有关联。
举例说明包含synchronized的方法和synchronized代码块:
package com.test;public class TestThread { public void test1() { synchronized (this) { int i = 0; while (i++<5) { System.out.println(Thread.currentThread().getName() + " : " + i); try { Thread.sleep(500); } catch (InterruptedException ie) { } } } } public synchronized void test2() { int i = 0; while (i++<5) { System.out.println(Thread.currentThread().getName() + " : " + i); try { Thread.sleep(500); } catch (InterruptedException ie) { } } } public static void main(String[] args) { final TestThread test = new TestThread(); Thread test1 = new Thread(new Runnable() { public void run() { test.test1(); } }, "test1"); Thread test2 = new Thread(new Runnable() { public void run() { test.test2(); } }, "test2"); test1.start(); test2.start(); }}
以上代码执行结果如下:
以上代码是因为执行同一个对象,所以另一个线程要等前一个对象,执行完成后释放掉对象锁,拿到对象锁才能继续执行synchronized里面的东西。
如果我们去掉synchronized修饰的方法或者synchronized代码块,将会打印出以下的结果:
如何要是去掉一个synchronized后,输出的语句是交叉执行的。这就说明,对于同一个对象,如果线程A得到了对象锁,线程B可以访问对象没有同步的方法和代码。进行同步的代码和没有同步的代码是互不影响的。
举例类锁:
package com.test;public class MyThreadClass { public static void main(String[] args) { final MyThreadClass my=new MyThreadClass(); Thread thread1=new Thread(new Runnable() { @Override public void run() { try { my.test1(); } catch (InterruptedException e) { e.printStackTrace(); } } },"test1"); Thread thread2=new Thread(new Runnable() { @Override public void run() { try { MyThreadClass.test2(); } catch (InterruptedException e) { e.printStackTrace(); } } },"test2"); thread1.start(); thread2.start(); } public void test1() throws InterruptedException{ synchronized (MyThreadClass.class) { int i=0; while(i++<5){ System.out.println(Thread.currentThread().getName()+":"+i); Thread.sleep(500); } } } public static synchronized void test2() throws InterruptedException{ int i=0; while(i++<5){ System.out.println(Thread.currentThread().getName()+":"+i); Thread.sleep(500); } }}
执行结果:
类锁和对象锁其实是一样的,只是针对于不同的对象。
如果两个方法一个被synchronized修饰,一个静态方法被synchronized修饰(体现类锁和对象锁的区别),代码如下:
package com.test;public class MyThreadClass2 { public static void main(String[] args) { final MyThreadClass2 my=new MyThreadClass2(); Thread thread1=new Thread(new Runnable() { @Override public void run() { try { my.test1(); } catch (InterruptedException e) { e.printStackTrace(); } } },"test1"); Thread thread2=new Thread(new Runnable() { @Override public void run() { try { MyThreadClass2.test2(); } catch (InterruptedException e) { e.printStackTrace(); } } },"test2"); thread1.start(); thread2.start(); } public synchronized void test1() throws InterruptedException{ int i=0; while(i++<5){ System.out.println(Thread.currentThread().getName()+":"+i); Thread.sleep(500); } } public static synchronized void test2() throws InterruptedException{ int i=0; while(i++<5){ System.out.println(Thread.currentThread().getName()+":"+i); Thread.sleep(500); } }}
代码执行结果如下:
结果也是交叉输出的,虽然都是被synchronized修饰,但是一个是属于对象的,一个是属于class。所以类锁和对象锁是不同的,他们控制着不同的区域,互不干扰。
疑问:java中既然synchronized修饰的方法和synchronized代码块作用是一样的,为什么还需要synchronized代码块呢?
使用synchronized修饰方式是直接在方法上面加锁,synchronized方法块是在方法里面加锁,一个范围大,一个范围小。还有一个最主要的在应用场景如下:在Class中创建一个对象,在Class中要执行这个对象的某个方法,为了防止多个线程同时执行,采用同步加锁,但是如果这个方法出现死循环或者执行时间很长,其他线程也不能执行对象的其他同步方法,需要等待这个线程执行完毕,影响系统性能。如果采用synchronized修饰方法和synchronized代码块可能都会出现这种情况,我们来模拟一下这种状况:
创建一个java对象类,里面有两个方法:
package com.test;public class Test { public void test1(){ } public void test2(){ }}
创建测试类代码如下:
package com.test;public class MyThreadClass4 { static Test test=new Test(); public static void main(String[] args) { final MyThreadClass4 my=new MyThreadClass4(); Thread thread1=new Thread(new Runnable() { @Override public void run() { try { my.test1(); } catch (InterruptedException e) { e.printStackTrace(); } } },"test1"); Thread thread2=new Thread(new Runnable() { @Override public void run() { try { my.test2(); } catch (InterruptedException e) { e.printStackTrace(); } } },"test2"); thread1.start(); thread2.start(); } long startTime; public synchronized void test1() throws InterruptedException{ test.test1(); System.out.println(Thread.currentThread().getName()); startTime = System.currentTimeMillis();//获取当前时间 Thread.sleep(5000); } public synchronized void test2() throws InterruptedException{ long endTime = System.currentTimeMillis(); test.test2(); System.out.println(Thread.currentThread().getName()); System.out.println("程序运行时间:"+(endTime-startTime)+"ms"); }}
在class里面创建对象实例,然后将两个方法分别调用,在调用方法上面都加上synchronized修饰方法Thread.sleep(5000)来模拟方法执行时间过长或者死循环,但是可以看到时间为我们设置的时间,执行结果不理想执行结果如下:
如果我们采用synchronized代码块的方式如下:
package com.test;public class MyThreadClass3 { static Test test=new Test(); public static void main(String[] args) { final MyThreadClass3 my=new MyThreadClass3(); Thread thread1=new Thread(new Runnable() { @Override public void run() { try { my.test1(); } catch (InterruptedException e) { e.printStackTrace(); } } },"test1"); Thread thread2=new Thread(new Runnable() { @Override public void run() { try { my.test2(); } catch (InterruptedException e) { e.printStackTrace(); } } },"test2"); thread1.start(); thread2.start(); } long startTime; public void test1() throws InterruptedException{ synchronized (test) { test.test1(); System.out.println(Thread.currentThread().getName()); startTime = System.currentTimeMillis();//获取当前时间 Thread.sleep(5000); } } public synchronized void test2() throws InterruptedException{ long endTime = System.currentTimeMillis(); test.test2(); System.out.println(Thread.currentThread().getName()); System.out.println("程序运行时间:"+(endTime-startTime)+"ms"); }}
synchronized代码块中我们只是对当前test对象加锁,和执行这块代码的对象没有任何关系。执行test1的同时,我照样可以执行其他的synchronized同步方法,增强系统性能。执行结果如下: