package JavaMultiThread; /* * 实例变量的非线程安全 */ public class t3 { public static void main(String[] args) { HasSelfPrivateNum numRef1 = new HasSelfPrivateNum(); HasSelfPrivateNum numRef2 = new HasSelfPrivateNum(); ThreadA thread1 = new ThreadA(numRef1); thread1.start(); ThreadB thread2 = new ThreadB(numRef1); thread2.start(); } } class HasSelfPrivateNum{ private int num = 0; synchronized public void addI(String username) { try { if(username.equals("a")) { num = 100; System.out.println("a set over!"); Thread.sleep(2000); }else { num = 200; System.out.println("b set over!"); } System.out.println(username + " num = " + num); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } class ThreadA extends Thread{ private HasSelfPrivateNum numRef; public ThreadA(HasSelfPrivateNum numRef) { super(); this.numRef = numRef; } @Override public void run() { super.run(); numRef.addI("a"); } } class ThreadB extends Thread{ private HasSelfPrivateNum numRef; public ThreadB(HasSelfPrivateNum numRef) { super(); this.numRef = numRef; } @Override public void run() { super.run(); numRef.addI("b"); } }
a set over! a num = 100 b set over! b num = 200
如果多个线程共同访问1个对象中的实例变量,则可能出现”非线程安全“问题,可能会出现覆盖的情况。“非线程安全”问题存在于“实例变量”中,如果是方法内部的变量,永远都是线程安全的,这由于方法内的变量是私有的。
用synchronized给方法加锁以后,在两个线程访问同一个对象中的同步方法时一定是线程安全的。本实验先打印a,后打印b。
- 非线程安全:当多个线程对同一个对象中的实例变量进行并发访问时,产生的后果就是”脏读“,也就是读取到的数据其实是被修改过的。
- 线程安全:获得的实例变量的值是经过同步处理的,不会出现脏读的现象
package JavaMultiThread; /* * 多个对象多个锁 */ public class t3 { public static void main(String[] args) { HasSelfPrivateNum numRef1 = new HasSelfPrivateNum(); HasSelfPrivateNum numRef2 = new HasSelfPrivateNum(); ThreadA thread1 = new ThreadA(numRef1); thread1.start(); ThreadB thread2 = new ThreadB(numRef2); thread2.start(); } } class HasSelfPrivateNum{ private int num = 0; synchronized public void addI(String username) { try { if(username.equals("a")) { num = 100; System.out.println("a set over!"); Thread.sleep(2000); }else { num = 200; System.out.println("b set over!"); } System.out.println(username + " num = " + num); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } class ThreadA extends Thread{ private HasSelfPrivateNum numRef; public ThreadA(HasSelfPrivateNum numRef) { super(); this.numRef = numRef; } @Override public void run() { super.run(); numRef.addI("a"); } } class ThreadB extends Thread{ private HasSelfPrivateNum numRef; public ThreadB(HasSelfPrivateNum numRef) { super(); this.numRef = numRef; } @Override public void run() { super.run(); numRef.addI("b"); } }
a set over! b set over! b num = 200 a num = 100
两个线程分别访问同一个类的两个不同实例的相同名称的同步方法,效果是异步的方式执行的。
关键字synchronized取得的锁都是对象锁,当多个线程访问同一个对象时,哪个线程先执行带synchronized关键字的方法,哪个线程就持有该方法所属对象的锁Lock,其他线程就只能呈等待状态。
但是如果是多个线程访问多个对象,则JVM会创建多个锁。
package JavaMultiThread; /* *synchronized锁住的是对象(不加锁) */ public class t1 { public static void main(String[] args) { MyObject object = new MyObject(); ThreadH a = new ThreadH(object); a.setName("A"); ThreadH b = new ThreadH(object); b.setName("B"); a.start(); b.start(); } } class MyObject{ public void methodA() { try { System.out.println("begin methodA threadName = " + Thread.currentThread().getName()); Thread.sleep(5000); System.out.println("end!"); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } class ThreadH extends Thread{ private MyObject object; public ThreadH( MyObject object) { //用MyObject对象创建Threada进程 super(); this.object = object; } @Override public void run() { super.run(); object.methodA(); } }
begin methodA threadName = A begin methodA threadName = B end! end!
package JavaMultiThread; /* *synchronized锁住的是对象 */ public class t1 { public static void main(String[] args) { MyObject object = new MyObject(); ThreadH a = new ThreadH(object); a.setName("A"); ThreadH b = new ThreadH(object); b.setName("B"); a.start(); b.start(); } } class MyObject{ synchronized public void methodA() { try { System.out.println("begin methodA threadName = " + Thread.currentThread().getName()); Thread.sleep(5000); System.out.println("end!"); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } class ThreadH extends Thread{ private MyObject object; public ThreadH( MyObject object) { //用MyObject对象创建Threada进程 super(); this.object = object; } @Override public void run() { super.run(); object.methodA(); } }
begin methodA threadName = A end! begin methodA threadName = B end!
只要共享资源的读写访问才需要同步化
package JavaMultiThread; public class t1 { public static void main(String[] args) { MyObject object = new MyObject(); ThreadH a = new ThreadH(object); a.setName("A"); ThreadC b = new ThreadC(object); b.setName("B"); a.start(); b.start(); } } class MyObject{ synchronized public void methodA() { try { System.out.println("begin methodA threadName = " + Thread.currentThread().getName()); Thread.sleep(5000); System.out.println("end endTime = " + System.currentTimeMillis()); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } public void methodB() { try { System.out.println("begin methodB threadName = " + Thread.currentThread().getName() + " begin time = " + System.currentTimeMillis()); Thread.sleep(5000); System.out.println("end"); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } class ThreadH extends Thread{ private MyObject object; public ThreadH( MyObject object) { //用MyObject对象创建Threada进程 super(); this.object = object; } @Override public void run() { super.run(); object.methodA(); } } class ThreadC extends Thread{ private MyObject object; public ThreadC( MyObject object) { //用MyObject对象创建Threada进程 super(); this.object = object; } @Override public void run() { super.run(); object.methodB(); } }
begin methodA threadName = A begin methodB threadName = B begin time = 1602684531118 end endTime = 1602684535614 end
从这个实验可以得知,虽然线程A先持有了object对象的锁,但线程B完全可以异步调用非synchronizd类型的方法。
package JavaMultiThread; public class t1 { public static void main(String[] args) throws InterruptedException { MyObject object = new MyObject(); ThreadH a = new ThreadH(object); a.setName("A"); ThreadC b = new ThreadC(object); b.setName("B"); a.start(); Thread.sleep(500); b.start(); } } class MyObject{ synchronized public void methodA() { try { System.out.println("begin methodA threadName = " + Thread.currentThread().getName()); Thread.sleep(5000); System.out.println("end endTime = " + System.currentTimeMillis()); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } synchronized public void methodB() { try { System.out.println("begin methodB threadName = " + Thread.currentThread().getName() + " begin time = " + System.currentTimeMillis()); Thread.sleep(5000); System.out.println("end"); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } class ThreadH extends Thread{ private MyObject object; public ThreadH( MyObject object) { //用MyObject对象创建Threada进程 super(); this.object = object; } @Override public void run() { super.run(); object.methodA(); } } class ThreadC extends Thread{ private MyObject object; public ThreadC( MyObject object) { //用MyObject对象创建Threada进程 super(); this.object = object; } @Override public void run() { super.run(); object.methodB(); } }
begin methodA threadName = A end endTime = 1602684573421 begin methodB threadName = B begin time = 1602684573421 end
总结一下:
- A线程先持有object对象的Lock锁,B线程可以以异步的方式调用object对象中的非synchronized类型的方法。
- A线程先持有object对象的Lock锁,B线程如果在这时调用object对象中的synchronized类型的方法则需要等待,也就是出现了同步
读脏数据
package JavaMultiThread; /* * 读脏数据:dirtyRead:发生脏数据的情况是在读取实例变量时,此值已经被其他线程更改过 */ public class t7 { public String username = "Xia Zhenbin"; public String password = "123456"; public static void main(String[] args) { try { t7 temp = new t7(); ThreadM thread = new ThreadM(temp); thread.start(); Thread.sleep(200); //sleep时间设置得短一些,那么第一个线程未执行完,第二个线程getInfo就执行完了 temp.getInfo(); } catch (InterruptedException e) { e.printStackTrace(); } } synchronized public void setInfo(String username, String password) { try { this.username = username; Thread.sleep(5000); this.password = password; System.out.println("setInfo method thread name= " + Thread.currentThread().getName() + " username = " + username + " password = " + password); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } public void getInfo() { System.out.println("getInfo method thread name = " + Thread.currentThread().getName() + " username = " + username + " password = " + password); } } class ThreadM extends Thread{ t7 temp = new t7(); public ThreadM(t7 temp) { super(); this.temp = temp; } @Override public void run() { super.run(); temp.setInfo("Lu Yanbing", "654321"); } }
getInfo method thread name = main username = Lu Yanbing password = 123456 setInfo method thread name= Thread-0 username = Lu Yanbing password = 654321
使用synchronized同步锁
package JavaMultiThread; /* * 读脏数据:dirtyRead:发生脏数据的情况是在读取实例变量时,此值已经被其他线程更改过 */ public class t7 { public String username = "Xia Zhenbin"; public String password = "123456"; public static void main(String[] args) { try { t7 temp = new t7(); ThreadM thread = new ThreadM(temp); thread.start(); Thread.sleep(200); //sleep时间设置得短一些,那么第一个线程未执行完,第二个线程getInfo就执行完了 temp.getInfo(); } catch (InterruptedException e) { e.printStackTrace(); } } synchronized public void setInfo(String username, String password) { try { this.username = username; Thread.sleep(5000); this.password = password; System.out.println("setInfo method thread name= " + Thread.currentThread().getName() + " username = " + username + " password = " + password); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } synchronized public void getInfo() { System.out.println("getInfo method thread name = " + Thread.currentThread().getName() + " username = " + username + " password = " + password); } } class ThreadM extends Thread{ t7 temp = new t7(); public ThreadM(t7 temp) { super(); this.temp = temp; } @Override public void run() { super.run(); temp.setInfo("Lu Yanbing", "654321"); } }
setInfo method thread name= Thread-0 username = Lu Yanbing password = 654321 getInfo method thread name = main username = Lu Yanbing password = 654321
package JavaMultiThread; /* * 锁重入:当一个线程得到一个对象锁后,再次请求此对象时是可以再次得到该对象的锁的。 */ public class t8 { public static void main(String[] args) { ThreadQ thread = new ThreadQ(); thread.start(); } } class Service { synchronized public void service1() { System.out.println("This is service1!"); service2(); } synchronized public void service2() { System.out.println("This is service2!"); service3(); } synchronized public void service3() { System.out.println("This is service3!"); } } class ThreadQ extends Thread { @Override public void run() { super.run(); Service service = new Service(); service.service1(); } }
This is service1! This is service2! This is service3!
关键字synchronized拥有锁重入的功能,也就是在使用synchronized时,当一个线程得到一个对象锁后,再次请求此对象锁时是可以再次得到该对象的锁的。这也就证明在一个synchronized方法 / 块内部调用本类的其他方法 /块时,是永远可以得到锁的。试想,如果对象锁还没有释放,当再次想要获得这个对象的锁的时候还是可以获取的,如果锁不可以重入的话,就会造成死锁!!!
可重入锁也支持在父子类继承的环境中。
同步锁不具有继承性
package JavaMultiThread; /* * 同步锁不具有继承性 */ public class t8 { public static void main(String[] args) { Sub sub = new Sub(); ThreadA1 thread = new ThreadA1(sub); thread.setName("a"); thread.start(); ThreadB1 thread1 = new ThreadB1(sub); thread1.setName("b"); thread1.start(); } } class Main { synchronized public void serviceMethod() { try { System.out.println("int main 下一步 sleep begin threadName = " + Thread.currentThread().getName() + " time = " + System.currentTimeMillis()); Thread.sleep(5000); System.out.println("int main 下一步 sleep end threadName = " + Thread.currentThread().getName() + " time = " + System.currentTimeMillis()); } catch (InterruptedException e) { e.printStackTrace(); } } } class Sub extends Main { @Override public void serviceMethod() { //《==============这里没加同步锁synchronized try { System.out.println("int sub 下一步 sleep begin threadName = " + Thread.currentThread().getName() + " time = " + System.currentTimeMillis()); Thread.sleep(5000); System.out.println("int sub 下一步 sleep end threadName = " + Thread.currentThread().getName() + " time = " + System.currentTimeMillis()); super.serviceMethod(); } catch (InterruptedException e) { e.printStackTrace(); } } } class ThreadA1 extends Thread{ private Sub sub; public ThreadA1(Sub sub) { super(); this.sub = sub; } @Override public void run() { super.run(); sub.serviceMethod(); } } class ThreadB1 extends Thread{ private Sub sub; public ThreadB1(Sub sub) { super(); this.sub = sub; } @Override public void run() { super.run(); sub.serviceMethod(); } }
int sub 下一步 sleep begin threadName = a time = 1602925179611 int sub 下一步 sleep begin threadName = b time = 1602925179611 《=============出现了不同步 int sub 下一步 sleep end threadName = b time = 1602925184619 int sub 下一步 sleep end threadName = a time = 1602925184619 int main 下一步 sleep begin threadName = b time = 1602925184619 int main 下一步 sleep end threadName = b time = 1602925189634 int main 下一步 sleep begin threadName = a time = 1602925189634 int main 下一步 sleep end threadName = a time = 1602925194647
将子类继承父类的方法加同步锁synchronized
package JavaMultiThread; /* * 同步锁不具有继承性 */ public class t8 { public static void main(String[] args) { Sub sub = new Sub(); ThreadA1 thread = new ThreadA1(sub); thread.setName("a"); thread.start(); ThreadB1 thread1 = new ThreadB1(sub); thread1.setName("b"); thread1.start(); } } class Main { synchronized public void serviceMethod() { try { System.out.println("int main 下一步 sleep begin threadName = " + Thread.currentThread().getName() + " time = " + System.currentTimeMillis()); Thread.sleep(5000); System.out.println("int main 下一步 sleep end threadName = " + Thread.currentThread().getName() + " time = " + System.currentTimeMillis()); } catch (InterruptedException e) { e.printStackTrace(); } } } class Sub extends Main { @Override synchronized public void serviceMethod() { //<=================这里加了同步锁 try { System.out.println("int sub 下一步 sleep begin threadName = " + Thread.currentThread().getName() + " time = " + System.currentTimeMillis()); Thread.sleep(5000); System.out.println("int sub 下一步 sleep end threadName = " + Thread.currentThread().getName() + " time = " + System.currentTimeMillis()); super.serviceMethod(); } catch (InterruptedException e) { e.printStackTrace(); } } } class ThreadA1 extends Thread{ private Sub sub; public ThreadA1(Sub sub) { super(); this.sub = sub; } @Override public void run() { super.run(); sub.serviceMethod(); } } class ThreadB1 extends Thread{ private Sub sub; public ThreadB1(Sub sub) { super(); this.sub = sub; } @Override public void run() { super.run(); sub.serviceMethod(); } }
int sub 下一步 sleep begin threadName = a time = 1602925318835 int sub 下一步 sleep end threadName = a time = 1602925323851 int main 下一步 sleep begin threadName = a time = 1602925323851 int main 下一步 sleep end threadName = a time = 1602925328852 int sub 下一步 sleep begin threadName = b time = 1602925328852 《=========进程a执行完以后,进程b才开始执行,是不是同步了 int sub 下一步 sleep end threadName = b time = 1602925333853 int main 下一步 sleep begin threadName = b time = 1602925333853 int main 下一步 sleep end threadName = b time = 1602925338867
synchronized同步代码块
package JavaMultiThread; public class t9 { public static void main(String[] args) { Task task = new Task(); ThreadA2 thread = new ThreadA2(task); thread.start(); ThreadB2 thread1 = new ThreadB2(task); thread1.start(); } } class Task { public void doLongTimeTask(){ for(int i = 0; i < 100; i++) { System.out.println("Nosynchronized threadName = " + Thread.currentThread().getName() + " i = " + (i + 1)); } System.out.println(""); synchronized(this) { for(int i = 0; i < 100; i++) { System.out.println("Synchronized threadName = " + Thread.currentThread().getName() + " i = " + (i + 1)); } } } } class ThreadA2 extends Thread{ private Task task; public ThreadA2(Task task) { // TODO 自动生成的构造函数存根 super(); this.task = task; } @Override public void run() { super.run(); task.doLongTimeTask(); } } class ThreadB2 extends Thread{ private Task task; public ThreadB2(Task task) { // TODO 自动生成的构造函数存根 super(); this.task = task; } @Override public void run() { super.run(); task.doLongTimeTask(); } }
Nosynchronized threadName = Thread-0 i = 1 Nosynchronized threadName = Thread-0 i = 2 Nosynchronized threadName = Thread-0 i = 3 Nosynchronized threadName = Thread-0 i = 4 Nosynchronized threadName = Thread-0 i = 5 Nosynchronized threadName = Thread-0 i = 6 Nosynchronized threadName = Thread-0 i = 7 Nosynchronized threadName = Thread-0 i = 8 Nosynchronized threadName = Thread-1 i = 1 《======这里出现了不同步 Nosynchronized threadName = Thread-0 i = 9 Nosynchronized threadName = Thread-1 i = 2 Nosynchronized threadName = Thread-0 i = 10 Nosynchronized threadName = Thread-1 i = 3 Nosynchronized threadName = Thread-0 i = 11 Nosynchronized threadName = Thread-1 i = 4 Nosynchronized threadName = Thread-0 i = 12 Nosynchronized threadName = Thread-1 i = 5 Nosynchronized threadName = Thread-0 i = 13 Nosynchronized threadName = Thread-1 i = 6 Nosynchronized threadName = Thread-0 i = 14 Nosynchronized threadName = Thread-1 i = 7 Nosynchronized threadName = Thread-0 i = 15 Nosynchronized threadName = Thread-1 i = 8 Nosynchronized threadName = Thread-0 i = 16 Nosynchronized threadName = Thread-1 i = 9 Nosynchronized threadName = Thread-0 i = 17 Nosynchronized threadName = Thread-1 i = 10 Nosynchronized threadName = Thread-0 i = 18 Nosynchronized threadName = Thread-1 i = 11 Nosynchronized threadName = Thread-0 i = 19 Nosynchronized threadName = Thread-1 i = 12 Nosynchronized threadName = Thread-0 i = 20 Nosynchronized threadName = Thread-1 i = 13 Nosynchronized threadName = Thread-1 i = 14 Synchronized threadName = Thread-0 i = 1 Nosynchronized threadName = Thread-1 i = 15 Synchronized threadName = Thread-0 i = 2 Nosynchronized threadName = Thread-1 i = 16 Synchronized threadName = Thread-0 i = 3 Nosynchronized threadName = Thread-1 i = 17 Synchronized threadName = Thread-0 i = 4 Nosynchronized threadName = Thread-1 i = 18 Synchronized threadName = Thread-0 i = 5 Nosynchronized threadName = Thread-1 i = 19 Synchronized threadName = Thread-0 i = 6 Synchronized threadName = Thread-0 i = 7 Synchronized threadName = Thread-0 i = 8 Synchronized threadName = Thread-0 i = 9 Synchronized threadName = Thread-0 i = 10 Synchronized threadName = Thread-0 i = 11 Nosynchronized threadName = Thread-1 i = 20 Synchronized threadName = Thread-0 i = 12 Synchronized threadName = Thread-0 i = 13 Synchronized threadName = Thread-0 i = 14 Synchronized threadName = Thread-0 i = 15 Synchronized threadName = Thread-0 i = 16 Synchronized threadName = Thread-0 i = 17 Synchronized threadName = Thread-0 i = 18 Synchronized threadName = Thread-0 i = 19 Synchronized threadName = Thread-0 i = 20 Synchronized threadName = Thread-1 i = 1 《========synchronized同步代码块,这里是同步的 Synchronized threadName = Thread-1 i = 2 Synchronized threadName = Thread-1 i = 3 Synchronized threadName = Thread-1 i = 4 Synchronized threadName = Thread-1 i = 5 Synchronized threadName = Thread-1 i = 6 Synchronized threadName = Thread-1 i = 7 Synchronized threadName = Thread-1 i = 8 Synchronized threadName = Thread-1 i = 9 Synchronized threadName = Thread-1 i = 10 Synchronized threadName = Thread-1 i = 11 Synchronized threadName = Thread-1 i = 12 Synchronized threadName = Thread-1 i = 13 Synchronized threadName = Thread-1 i = 14 Synchronized threadName = Thread-1 i = 15 Synchronized threadName = Thread-1 i = 16 Synchronized threadName = Thread-1 i = 17 Synchronized threadName = Thread-1 i = 18 Synchronized threadName = Thread-1 i = 19 Synchronized threadName = Thread-1 i = 20
当一个线程访问object的一个synchronized同步代码块时,另一个线程可以访问该object对象中的非synchronized(this)同步代码块。
从上例,可以看出不在synchronized块中就是异步执行的,在synchronized块中就是同步执行的。
package JavaMultiThread; /* * synchronized代码块,当某一个线程访问Object对象的其中一个synchronized(this)代码块,那么其他线程访问该对象的别的所有的synchronized(this)代码块时均被阻塞 * 这个例子也间接说明 synchronized锁住的是对象 */ public class t9 { public static void main(String[] args) { Task task = new Task(); ThreadA2 thread = new ThreadA2(task); thread.start(); ThreadB2 thread1 = new ThreadB2(task); thread1.start(); } } class Task { synchronized public void doLongTimeTask(){ synchronized(this) { for(int i = 0; i < 20; i++) { System.out.println("Synchronized Method threadName = " + Thread.currentThread().getName() + " i = " + (i + 1)); } } } public void doprintInfoTask() { System.out.println("begin!"); System.out.println("Synchronized Method threadName = " + Thread.currentThread().getName()); System.out.println("end!"); } } class ThreadA2 extends Thread{ private Task task; public ThreadA2(Task task) { // TODO 自动生成的构造函数存根 super(); this.task = task; } @Override public void run() { super.run(); task.doLongTimeTask(); } } class ThreadB2 extends Thread{ private Task task; public ThreadB2(Task task) { // TODO 自动生成的构造函数存根 super(); this.task = task; } @Override public void run() { super.run(); task.doprintInfoTask(); } }
Synchronized Method threadName = Thread-0 i = 1 Synchronized Method threadName = Thread-0 i = 2 Synchronized Method threadName = Thread-0 i = 3 Synchronized Method threadName = Thread-0 i = 4 Synchronized Method threadName = Thread-0 i = 5 Synchronized Method threadName = Thread-0 i = 6 Synchronized Method threadName = Thread-0 i = 7 Synchronized Method threadName = Thread-0 i = 8 Synchronized Method threadName = Thread-0 i = 9 Synchronized Method threadName = Thread-0 i = 10 Synchronized Method threadName = Thread-0 i = 11 Synchronized Method threadName = Thread-0 i = 12 Synchronized Method threadName = Thread-0 i = 13 Synchronized Method threadName = Thread-0 i = 14 begin! Synchronized Method threadName = Thread-0 i = 15 Synchronized Method threadName = Thread-1 Synchronized Method threadName = Thread-0 i = 16 end! Synchronized Method threadName = Thread-0 i = 17 Synchronized Method threadName = Thread-0 i = 18 Synchronized Method threadName = Thread-0 i = 19 Synchronized Method threadName = Thread-0 i = 20
更改后:
package JavaMultiThread; /* * synchronized代码块,当某一个线程访问Object对象的其中一个synchronized(this)代码块,那么其他线程访问该对象的别的所有的synchronized(this)代码块时均被阻塞 * 这个例子也间接说明 synchronized锁住的是对象 */ public class t9 { public static void main(String[] args) { Task task = new Task(); ThreadA2 thread = new ThreadA2(task); thread.start(); ThreadB2 thread1 = new ThreadB2(task); thread1.start(); } } class Task { synchronized public void doLongTimeTask(){ synchronized(this) { for(int i = 0; i < 20; i++) { System.out.println("Synchronized Method threadName = " + Thread.currentThread().getName() + " i = " + (i + 1)); } } } synchronized public void doprintInfoTask() { System.out.println("begin!"); System.out.println("Synchronized Method threadName = " + Thread.currentThread().getName()); System.out.println("end!"); } } class ThreadA2 extends Thread{ private Task task; public ThreadA2(Task task) { // TODO 自动生成的构造函数存根 super(); this.task = task; } @Override public void run() { super.run(); task.doLongTimeTask(); } } class ThreadB2 extends Thread{ private Task task; public ThreadB2(Task task) { // TODO 自动生成的构造函数存根 super(); this.task = task; } @Override public void run() { super.run(); task.doprintInfoTask(); } }
Synchronized Method threadName = Thread-0 i = 1 Synchronized Method threadName = Thread-0 i = 2 Synchronized Method threadName = Thread-0 i = 3 Synchronized Method threadName = Thread-0 i = 4 Synchronized Method threadName = Thread-0 i = 5 Synchronized Method threadName = Thread-0 i = 6 Synchronized Method threadName = Thread-0 i = 7 Synchronized Method threadName = Thread-0 i = 8 Synchronized Method threadName = Thread-0 i = 9 Synchronized Method threadName = Thread-0 i = 10 Synchronized Method threadName = Thread-0 i = 11 Synchronized Method threadName = Thread-0 i = 12 Synchronized Method threadName = Thread-0 i = 13 Synchronized Method threadName = Thread-0 i = 14 Synchronized Method threadName = Thread-0 i = 15 Synchronized Method threadName = Thread-0 i = 16 Synchronized Method threadName = Thread-0 i = 17 Synchronized Method threadName = Thread-0 i = 18 Synchronized Method threadName = Thread-0 i = 19 Synchronized Method threadName = Thread-0 i = 20 begin! Synchronized Method threadName = Thread-1 end!
在使用同步synchronized(this)代码块时需要注意的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对同一个object
中所有其他synchronized(this)同步代码块的访问将被阻塞,这也说明synchronized使用的是“对象监听器”
![](https://img2020.cnblogs.com/blog/1881034/202010/1881034-20201023110135235-372005057.png)
验证三个结论
package JavaMultiThread; public class t13 { public static void main(String[] args) { MyService1 service = new MyService1(); MyObject1 object = new MyObject1(); Thread thread = new MyThreadA5(service, object); thread.setName("a"); thread.start(); Thread thread1 = new MyThreadB5(service, object); thread1.setName("b"); thread1.start(); } } class MyObject1 { } class MyService1 { public void testMethod1(MyObject1 object) { synchronized(object) { try { System.out.println("testMethod1 ____getLock time = " + System.currentTimeMillis() + " run ThreadName = " + Thread.currentThread().getName()); Thread.sleep(2000); System.out.println("testMethod1 releaseLock time = " + System.currentTimeMillis() + " run ThreadName = " + Thread.currentThread().getName()); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } } class MyThreadA5 extends Thread{ private MyService1 service; private MyObject1 object; public MyThreadA5(MyService1 service, MyObject1 object) { // TODO 自动生成的构造函数存根 super(); this.object = object; this.service = service; } @Override public void run() { // TODO 自动生成的方法存根 super.run(); service.testMethod1(object); } } class MyThreadB5 extends Thread{ private MyService1 service; private MyObject1 object; public MyThreadB5(MyService1 service, MyObject1 object) { // TODO 自动生成的构造函数存根 super(); this.object = object; this.service = service; } @Override public void run() { // TODO 自动生成的方法存根 super.run(); service.testMethod1(object); } }
testMethod1 ____getLock time = 1603094973637 run ThreadName = a testMethod1 releaseLock time = 1603094975639 run ThreadName = a testMethod1 ____getLock time = 1603094975639 run ThreadName = b testMethod1 releaseLock time = 1603094977644 run ThreadName = b
可以看出是两个线程是同步的,因为synchronized(非this x对象),锁住的是用一个对象object。
synchronized给class类上锁
package SynchronizedCass; public class t1 { public static void main(String[] args) { ThreadA thread = new ThreadA(); thread.setName("进程a"); thread.start(); ThreadB thread1 = new ThreadB(); thread1.setName("进程b"); thread1.start(); } } class Service { synchronized public static void printA() { try { System.out.println("线程名称为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + " 进入printA "); Thread.sleep(3000); System.out.println("线程名称为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + " 离开printA "); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } synchronized public static void printB() { System.out.println("线程名称为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + "进入printB"); System.out.println("线程名称为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + "离开printB"); } } class ThreadA extends Thread{ @Override public void run() { super.run(); Service.printA(); } } class ThreadB extends Thread{ @Override public void run() { super.run(); Service.printB(); } }
线程名称为:进程a 在 1603096693630 进入printA 线程名称为:进程a 在 1603096696637 离开printA 线程名称为:进程b 在 1603096696637 进入printB 线程名称为:进程b 在 1603096696637 离开printB
package SynchronizedCass; /* * 简单理解,类锁和对象锁不是同一个,当一个线程持有类锁时,其他线程还是可以拿到对象锁 */ public class t1 { public static void main(String[] args) { Service service = new Service(); Service service1 = new Service(); ThreadA thread = new ThreadA(service); thread.setName("进程a"); thread.start(); ThreadB thread1 = new ThreadB(service); thread1.setName("进程b"); thread1.start(); ThreadC thread2 = new ThreadC(service); thread2.setName("进程c"); thread2.start(); } } class Service { synchronized public static void printA() { try { System.out.println("线程名称为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + " 进入printA "); Thread.sleep(3000); System.out.println("线程名称为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + " 离开printA "); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } synchronized public static void printB() { System.out.println("线程名称为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + " 进入printB"); System.out.println("线程名称为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + " 离开printB"); } synchronized public void printC() { System.out.println("线程名称为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + " 进入printC"); System.out.println("线程名称为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + " 离开printC"); } } class ThreadA extends Thread{ private Service service; public ThreadA(Service service) { // TODO 自动生成的构造函数存根 this.service = service; } @Override public void run() { super.run(); Service.printA(); } } class ThreadB extends Thread{ private Service service; public ThreadB(Service service) { // TODO 自动生成的构造函数存根 this.service = service; } @Override public void run() { super.run(); Service.printB(); } } class ThreadC extends Thread{ private Service service; public ThreadC(Service service) { // TODO 自动生成的构造函数存根 this.service = service; } @Override public void run() { super.run(); service.printC(); } }
线程名称为:进程a 在 1603098873662 进入printA 线程名称为:进程c 在 1603098873662 进入printC 线程名称为:进程c 在 1603098873662 离开printC 线程名称为:进程a 在 1603098878675 离开printA 线程名称为:进程b 在 1603098878675 进入printB 线程名称为:进程b 在 1603098878675 离开printB
类锁的另一种表示方法
同步synchronized(class)代码块的作用其实和synchronized static方法的作用是一样的
package SynchronizedCass; /* * 简单理解,类锁和对象锁不是同一个,当一个线程持有类锁时,其他线程还是可以拿到对象锁 */ public class t1 { public static void main(String[] args) { Service service = new Service(); Service service1 = new Service(); ThreadA thread = new ThreadA(service); thread.setName("进程a"); thread.start(); ThreadB thread1 = new ThreadB(service1); thread1.setName("进程b"); thread1.start(); } } class Service { public static void printA() { synchronized(Service.class) { //类锁的另一种写法而已 try { System.out.println("线程名称为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + " 进入printA "); Thread.sleep(5000); System.out.println("线程名称为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + " 离开printA "); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } synchronized public static void printB() { System.out.println("线程名称为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + " 进入printB"); System.out.println("线程名称为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + " 离开printB"); } synchronized public void printC() { System.out.println("线程名称为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + " 进入printC"); System.out.println("线程名称为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + " 离开printC"); } } class ThreadA extends Thread{ private Service service; public ThreadA(Service service) { // TODO 自动生成的构造函数存根 this.service = service; } @Override public void run() { super.run(); Service.printA(); } } class ThreadB extends Thread{ private Service service; public ThreadB(Service service) { // TODO 自动生成的构造函数存根 this.service = service; } @Override public void run() { super.run(); Service.printB(); } } class ThreadC extends Thread{ private Service service; public ThreadC(Service service) { // TODO 自动生成的构造函数存根 this.service = service; } @Override public void run() { super.run(); service.printC(); } }
线程名称为:进程a 在 1603098946244 进入printA 线程名称为:进程a 在 1603098951246 离开printA 线程名称为:进程b 在 1603098951246 进入printB 线程名称为:进程b 在 1603098951246 离开printB
在JVM中具有String常量池缓存的功能,因此String和synchronized(string)一起使用的时候,要注意常量池带来的一些例外。
注:JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化
-
为字符串开辟一个字符串常量池,类似于缓存区。
-
创建字符串常量时,首先坚持字符串常量池是否存在该字符串。
-
存在该字符串,返回引用实例,不存在,实例化该字符串并放入池中。
- 举个例子
- String str1 = “hello”;
String str2 = “hello”;
System.out.printl("str1 == str2" : str1 == str2 ) //返回的是true
package SynchronizedCass; /* * 简单理解,类锁和对象锁不是同一个,当一个线程持有类锁时,其他线程还是可以拿到对象锁 */ public class t1 { public static void main(String[] args) { Service service = new Service(); ThreadA a = new ThreadA(service); a.setName("A"); a.start(); ThreadB b = new ThreadB(service); b.setName("B"); b.start(); } } class Service { public static void print(String stringParam) { try { synchronized (stringParam) { while(true) { System.out.println(Thread.currentThread().getName()); Thread.sleep(1000); } } } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } class ThreadA extends Thread{ private Service service; public ThreadA(Service service) { // TODO 自动生成的构造函数存根 this.service = service; } @Override public void run() { super.run(); Service.print("AA"); } } class ThreadB extends Thread{ private Service service; public ThreadB(Service service) { // TODO 自动生成的构造函数存根 this.service = service; } @Override public void run() { super.run(); Service.print("AA"); } }
A A A A A A A ...
两个线程持有相同的锁,A线程没有释放锁前,B线程不能持有该锁。
锁对象换成object以后,两个线程就编程异步的了。
package SynchronizedCass; /* * 简单理解,类锁和对象锁不是同一个,当一个线程持有类锁时,其他线程还是可以拿到对象锁 */ public class t1 { public static void main(String[] args) { Service service = new Service(); ThreadA a = new ThreadA(service); a.setName("A"); a.start(); ThreadB b = new ThreadB(service); b.setName("B"); b.start(); } } class Service { public static void print(Object object) { try { synchronized (object) { while(true) { System.out.println(Thread.currentThread().getName()); Thread.sleep(1000); } } } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } class ThreadA extends Thread{ private Service service; public ThreadA(Service service) { // TODO 自动生成的构造函数存根 this.service = service; } @Override public void run() { super.run(); Service.print(new Object()); } } class ThreadB extends Thread{ private Service service; public ThreadB(Service service) { // TODO 自动生成的构造函数存根 this.service = service; } @Override public void run() { super.run(); Service.print(new Object()); } }
A B B A B A A B A B B A B A A B A B A B...
2.2.16 锁对象的改变
在将任何数据类型作为同步锁时,需要注意的是,是否有多个线程同时持有锁的对象,如果同时持有相同的锁对象,则这些线程之间就是同步的;如果分别获得锁对象,这些线程之间就是一步的。
package SynchronizedCass; /* * synchronized锁住的是lock对象,但是lock对象变了 */ public class t2 { public static void main(String[] args) { MyService service = new MyService(); ThreadA1 a = new ThreadA1(service); a.setName("A"); a.start(); ThreadB1 b = new ThreadB1(service); b.setName("B"); b.start(); } } class MyService { private String lock = "123"; public void testMethod() { try { synchronized (lock) { System.out.println(Thread.currentThread().getName() + " begin " + System.currentTimeMillis()); lock = "345"; Thread.sleep(2000); System.out.println(Thread.currentThread().getName() + " end " + System.currentTimeMillis()); } } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } class ThreadA1 extends Thread{ private MyService service; public ThreadA1(MyService service) { super(); this.service = service; } @Override public void run() { service.testMethod(); } } class ThreadB1 extends Thread{ private MyService service; public ThreadB1(MyService service) { super(); this.service = service; } @Override public void run() { service.testMethod(); } }
A begin 1603113655979 B begin 1603113655979 A end 1603113657983 B end 1603113657983
注意:只要对象不变,即使对象的属性被改变了,运行的结果还是同步的,下面这个例子刚好说明这个问题。
package SynchronizedCass; /* * synchronized锁住的是lock对象,但是lock对象变了 */ public class t2 { public static void main(String[] args) throws InterruptedException { MyService service = new MyService(); Userinfo userinfo = new Userinfo(); ThreadA1 a = new ThreadA1(service, userinfo); a.setName("A"); a.start(); Thread.sleep(50); ThreadB1 b = new ThreadB1(service, userinfo); b.setName("B"); b.start(); } } class Userinfo { public String name = "Xi dada"; public Userinfo() { // TODO 自动生成的构造函数存根 } public void setUsername(String name) { this.name = name; } } class MyService { public void serviceMethodA(Userinfo userinfo) { synchronized(userinfo) { try { System.out.println(Thread.currentThread().getName()); System.out.println("Username = " + userinfo.name); userinfo.setUsername("Xiazhen"); Thread.sleep(3000); System.out.println("end Time = " + System.currentTimeMillis()); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } } class ThreadA1 extends Thread{ private MyService service; private Userinfo userinfo; public ThreadA1(MyService service, Userinfo userinfo) { super(); this.service = service; this.userinfo = userinfo; } @Override public void run() { service.serviceMethodA(userinfo); } } class ThreadB1 extends Thread{ private MyService service; private Userinfo userinfo; public ThreadB1(MyService service, Userinfo userinfo) { super(); this.service = service; this.userinfo = userinfo; } @Override public void run() { service.serviceMethodA(userinfo); } }
A Username = Xi dada end Time = 1603114559708 B Username = Xiazhen end Time = 1603114562716
package SynchronizedCass; /** * 内置类与静态内置类 * @author user * */ public class t3 { public static void main(String[] args) { final OutClass.Inner inner = new OutClass.Inner(); Thread thread = new Thread(new Runnable() { @Override public void run() { inner.method1(); } }); thread.setName("A"); Thread thread1 = new Thread(new Runnable() { @Override public void run() { inner.method2(); } }); thread1.setName("B"); thread.start(); thread1.start(); } } class OutClass { static class Inner{ public void method1() { synchronized ("其他的锁") { for(int i = 1; i <= 10; i++) { System.out.println(Thread.currentThread().getName() + " i =" + i); } try { Thread.sleep(100); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } public synchronized void method2() { for(int i = 11; i <= 20; i++) { System.out.println(Thread.currentThread().getName() + " i =" + i); } try { Thread.sleep(100); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } }
A i =1 B i =11 A i =2 B i =12 A i =3 B i =13 A i =4 B i =14 A i =5 A i =6 B i =15 B i =16 A i =7 B i =17 A i =8 B i =18 A i =9 B i =19 B i =20 A i =10
package SynchronizedCass; /** * 内置类与静态内置类 * @author user * */ public class t3 { public static void main(String[] args) { final OutClass.InnerClass1 inner1 = new OutClass.InnerClass1(); final OutClass.InnerClass2 inner2 = new OutClass.InnerClass2(); Thread thread = new Thread(new Runnable() { @Override public void run() { inner1.method1(inner2); } }); thread.setName("A"); Thread thread1 = new Thread(new Runnable() { @Override public void run() { inner1.method2(); } }); thread1.setName("B"); Thread thread2 = new Thread(new Runnable() { @Override public void run() { inner2.method1(); } },"C"); thread.start(); thread1.start(); thread2.start(); } } class OutClass { static class InnerClass1{ public void method1(InnerClass2 class2) { String threadName = Thread.currentThread().getName(); synchronized (class2) { System.out.println(threadName + "进入InnerClass1中的method1"); for(int i = 0; i <= 10; i++) { System.out.println(Thread.currentThread().getName() + " i =" + i); } try { Thread.sleep(100); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } System.out.println(threadName + "离开InnerClass1中的method1"); } } public synchronized void method2() { String threadName = Thread.currentThread().getName(); System.out.println(threadName + "进入InnerClass1中的method2"); for(int i = 11; i <= 20; i++) { System.out.println(" j =" + i); } try { Thread.sleep(100); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } System.out.println(threadName + "离开InnerClass1中的method2"); } } static class InnerClass2 { public synchronized void method1() { String threadName = Thread.currentThread().getName(); System.out.println(threadName + "进入InnerClass2中的method1"); for(int i = 0; i <= 10; i++) { System.out.println(" k =" + i); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } System.out.println(threadName + "离开InnerClass2中的method1"); } } }
A进入InnerClass1中的method1 B进入InnerClass1中的method2 j =11 j =12 j =13 j =14 j =15 j =16 j =17 j =18 j =19 j =20 A i =0 A i =1 A i =2 A i =3 A i =4 A i =5 A i =6 A i =7 A i =8 A i =9 A i =10 B离开InnerClass1中的method2 A离开InnerClass1中的method1 C进入InnerClass2中的method1 k =0 k =1 k =2 k =3 k =4 k =5 k =6 k =7 k =8 k =9 k =10 C离开InnerClass2中的method1
一定注意锁的对象是谁!!!