Java中的线程以前也提到过,可是仍是想再详细的学习一下,跟着张孝祥老师,系统的再学习一下。面试
1、线程中的互斥安全
线程安全中的问题解释:线程安全问题能够用银行中的转帐ide
例题描述:oop
线程A与线程B分别访问同一个对象的方法,这样就会存在线程安全的问题,方法的做用是打印出字符串中的每个字符,方法以下:学习
1 public void output(String name) { 2 int len = name.length(); 3 for (int i = 0; i < len; i++) { 4 System.out.print(name.charAt(i)); 5 } 6 System.out.println(); 7 }
线程A和线程B代码以下:(直接写在了init()方法中了)测试
1 private void init() { 2 outputer outputer = new outputer(); 3 new Thread(new Runnable() { 4 @Override 5 public void run() { 6 while (true) { 7 try { 8 Thread.sleep(10); 9 } catch (InterruptedException e) { 10 e.printStackTrace(); 11 } 12 outputer.output("songshengchao"); 13 } 14 } 15 }).start(); 16 17 new Thread(new Runnable() { 18 @Override 19 public void run() { 20 while (true) { 21 try { 22 Thread.sleep(10); 23 } catch (InterruptedException e) { 24 e.printStackTrace(); 25 } 26 outputer.output("songxiaochao"); 27 } 28 } 29 }).start(); 30 }
测试一下,确定会出现线程不安全的问题,这是母庸质疑的事实,测试代码以下:优化
1 public static void main(String[] args) { 2 new TraditionalThreadSynchronized().init(); 3 }
三种解决办法,代码以下:this
1 public class outputer { 2 public void output(String name) { 3 int len = name.length(); 4 synchronized (this) { // 传进来当前调用方法的对象,要求线程用的是同一个对象 5 //synchronized (outputer.class) { // 这样和outputer3方法达到线程互斥 6 for (int i = 0; i < len; i++) { 7 System.out.print(name.charAt(i)); 8 } 9 System.out.println(); 10 } 11 } 12 13 // 方法上的锁对象用的就是this当前对象 14 public synchronized void output2(String name) { 15 int len = name.length(); 16 for (int i = 0; i < len; i++) { 17 System.out.print(name.charAt(i)); 18 } 19 System.out.println(); 20 } 21 22 // output3 想和output方法达到线程互斥 23 public static synchronized void output3(String name) { 24 int len = name.length(); 25 for (int i = 0; i < len; i++) { 26 System.out.print(name.charAt(i)); 27 } 28 System.out.println(); 29 } 30 }
注意:至于第三种静态的synchronized方法,在和第一个方法共用时,第一种方法必定是用当前对象的class字节码文件,才能确保两个方法用的同一个对象。spa
2、线程互斥与通讯的经典面试题线程
面试题:子线程循环10次,接着主线程循环100次,接着又回到子线程循环10次,接着在主线程循环100次,如此循环50次,程序如何写???
经验之谈,设计思想,设计思路:
要用到共同数据(包括同步锁)的若干个方法应该归在同一个类上,这种设计正好提现了程序的高内聚与健壮性
思路:先写主线程与子线程的循环,而后在考虑轮流执行。先考虑循环,代码以下:
1 public class TraditionalThreadCommunication { 2 3 public static void main(String[] args) { 4 5 new Thread(new Runnable() { 6 7 @Override 8 public void run() { 9 for (int i = 1; i <= 50; i++) { 10 synchronized (TraditionalThreadCommunication.class) { 11 for (int j = 1; j <= 10; j++) { 12 System.out.println("sub thread sequece of" + j + ", loop of " + i); 13 } 14 } 15 16 } 17 } 18 }).start(); 19 20 // 自己main方法就是主线程,直接能够写循环代码 21 for (int i = 1; i <= 50; i++) { 22 synchronized (TraditionalThreadCommunication.class) { 23 for (int j = 1; j <= 100; j++) { 24 System.out.println("main thread sequece of" + j + ", loop of " + i); 25 } 26 } 27 } 28 29 } 30 }
代码优化,用面向对象的思想,将那些代码放到一个公共的类中,而后执行类中的不一样方法,优化成一个公共的类,代码以下:
1 public class Business { 2 3 public synchronized void sub(int i){ 4 for (int j = 1; j <= 10; j++) { 5 System.out.println("sub thread sequece of" + j + ", loop of " + i); 6 } 7 } 8 9 public synchronized void main(int i){ 10 for (int j = 1; j <= 100; j++) { 11 System.out.println("main thread sequece of" + j + ", loop of " + i); 12 } 13 } 14 } 15 16 ---------------------------------------------------------------------------------------------- 17 18 public class TraditionalThreadCommunication { 19 20 21 public static void main(String[] args) { 22 Business business = new Business(); 23 new Thread(new Runnable() { 24 25 @Override 26 public void run() { 27 for (int i = 1; i <= 50; i++) { 28 business.sub(i); 29 } 30 } 31 }).start(); 32 33 // 自己main方法就是主线程,直接能够写循环代码 34 for (int i = 1; i <= 50; i++) { 35 business.main(i); 36 } 37 38 } 39 40 }
最终的完整代码以下(详细注释):
1 public class Business { 2 3 // 是不是子线程执行 默认子线程先执行 4 private boolean bShouldSub = true; 5 6 public synchronized void sub(int i) { 7 // 不是子线程应该执行 让给主线程 子线程执行等待的方法 8 while (!bShouldSub) { 9 try { 10 this.wait(); 11 } catch (InterruptedException e) { 12 e.printStackTrace(); 13 } 14 } 15 for (int j = 1; j <= 10; j++) { 16 System.out.println("sub thread sequece of" + j + ", loop of " + i); 17 } 18 // 子线程执行完毕后 让给主线程执行 19 bShouldSub = false; 20 // 唤醒主线程 21 this.notify(); 22 } 23 24 public synchronized void main(int i) { 25 // 是子线程应该执行 让给子线程执行 主线程执行等待的方法 26 while (bShouldSub) { 27 try { 28 this.wait(); 29 } catch (InterruptedException e) { 30 e.printStackTrace(); 31 } 32 } 33 for (int j = 1; j <= 100; j++) { 34 System.out.println("main thread sequece of" + j + ", loop of " + i); 35 } 36 // 主线程执行费完毕后 交给子线程执行 37 bShouldSub = true; 38 // 唤醒子线程 39 this.notify(); 40 } 41 } 42 ------------------------------------------------------------------------------------------------ 43 44 public class TraditionalThreadCommunication { 45 46 public static void main(String[] args) { 47 Business business = new Business(); 48 new Thread(new Runnable() { 49 50 @Override 51 public void run() { 52 for (int i = 1; i <= 50; i++) { 53 business.sub(i); 54 } 55 } 56 }).start(); 57 58 // 自己main方法就是主线程,直接能够写循环代码 59 for (int i = 1; i <= 50; i++) { 60 business.main(i); 61 } 62 63 } 64 65 }