多线程:
1.进程和线程
进程是资源分配的最小单位,线程是CPU调度的最小单位。
- 每个进程的创建都需要系统为其开辟资源内存空间,并发执行的程序在执行过程中分配和管理资源的基本单位,速度和销毁也较慢。进程和进程之间一般没有关联性。 System.Diagnostics.Process 进程的创建类,属于系统资源。
- 线程是运行在进程中的可认为是小子进程,线程共享调用进程中的资源内存空间,多个线程同时运行就出现并发线程,线程的调度顺序如果不影响结果且较占用时间,则可使用多线程。
- 服务是管理和协调守护进程的。
- 任务是完成某项操作的活动。可以是进程,可以是线程,可大可小。
比如Tomcat的webapp下有两个程序project1和project2.这两个程序默认各自会有一个进程,但一个程序也可开辟多个进程,一般各进程间没有资源的互相调用,且可独立移出时才开辟(浏览器的新建标签页,程序中消息推送功能)。而每次用户的请求服务都会生成一个主线程,如果后台java中创建多个Thread 子线程,就会出现并发情况,这些子线程共享进程的资源空间,速度快,生成销毁也快。
2. 并行和并发
并行是多条线路同时进行。
并发是多个请求同时抢占一个资源。
以打饭为例:并行,就是开了多个窗口,这些人可以排成好几个队进行打饭操作。并发,一个窗口多个人在等待,这些人没有先后顺序,谁先到cpu口,谁先执行。不一定会按照ABC顺序进行。
3.synchronized
关键字
线程的调度顺序影响其结果,则需要同步锁(例如:银行系统中的存取,必须先存后取,不能存取都开线程后,让任意优先)。每一个对象都会有一个默认的隐形锁,单线程不起作用。多线程调用时,就会查找synchronized是否起作用。对象加锁和解锁是很耗性能的,因此慎用,当然安全比性能优先。
Java中的每个对象都有一个监视器,来监测并发代码的重入。在非多线程编码时该监视器不发挥作用,反之如果在synchronized 范围内,监视器发挥作用。
wait/notify必须存在于synchronized块中。并且,这三个关键字针对的是同一个监视器(某对象的监视器)。这意味着wait之后,其他线程可以进入同步块执行。
当某代码并不持有监视器的使用权时(如图中5的状态,即脱离同步块)去wait或notify,会抛出java.lang.IllegalMonitorStateException。也包括在synchronized块中去调用另一个对象的wait/notify,因为不同对象的监视器不同,同样会抛出此异常。
synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
锁定代码块,会锁定传入类对这部分代码的访问控制, 以以下代码为例:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package jquery.test.interview; 2 3 /** 4 * 银行存取业务 5 * 6 * @author DennyZhao 7 * @date 2017年6月29日 8 * @version 1.0 9 */ 10 public class BankAccess { 11 12 private int money; 13 14 /** 15 * 存入钱 16 * @param store 17 */ 18 public void saveMoney(int store){ 19 money+=store; 20 } 21 22 /** 23 * 减去手续费 24 */ 25 public void getoutMoney(int out){ 26 money-=out; 27 } 28 29 /** 30 * 查看钱 31 */ 32 public int searchMoney(){ 33 return money; 34 } 35 } 36 37 38 -----------------------------银行操作类 39 package jquery.test.interview; 40 41 public class BankTreader implements Runnable { 42 /** 创建银行操作类 **/ 43 private BankAccess bank; 44 45 private int saveMoney; 46 47 public int getSaveMoney() { 48 return saveMoney; 49 } 50 51 public void setSaveMoney(int saveMoney) { 52 this.saveMoney = saveMoney; 53 } 54 55 /** 56 * 构造类 57 * @param bank 58 */ 59 public BankTreader(BankAccess bank){ 60 this.bank = bank; 61 } 62 63 public void handlerBank(){ 64 synchronized(bank){ 65 bank.saveMoney(saveMoney); //存入 66 bank.getoutMoney((int)Math.round(bank.searchMoney()*0.01));//收取总额手续费,当然实际银行不会这样 67 System.out.println("---------" + bank.searchMoney()); //查看 68 } 69 } 70 @Override 71 public void run() { 72 this.handlerBank(); 73 } 74 75 -------------------------------------------- 76 测试方法: 77 public static void main(String[] args) { 78 BankAccess bank = new BankAccess(); 79 BankTreader handler = new BankTreader(bank); 80 handler.setSaveMoney(500); 81 Thread t1 = new Thread(handler,"t1"); 82 handler.setSaveMoney(300); 83 Thread t2 = new Thread(handler,"t2"); 84 t1.start(); 85 t2.start(); 86 } 87 88 结果: 89 ---------297 90 ---------591
其中通过对银行的存入,手续费,查看3个方法绑定到一个同步块中,实现多个方法绑定的作用,而不影响多线程单独调用各个方法。
如果synchronized代码块对象是this,就相当于锁定了handler对象,而非bank对象。
如果synchronized代码块对象是handler的Class类,则对所有的handler对象其作用,就是不论你new 多个handler还是一样可以控制同步化(例如下面结果)。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 public static void main(String[] args) { 2 BankAccess bank = new BankAccess(); 3 BankTreader handler = new BankTreader(bank); 4 BankTreader handler2 = new BankTreader(bank); 5 handler.setSaveMoney(500); 6 Thread t1 = new Thread(handler,"t1"); 7 handler2.setSaveMoney(300); 8 Thread t2 = new Thread(handler2,"t2"); 9 t1.start(); 10 t2.start(); 11 } 12 13 测试结果: 14 ---------495 15 ---------787
2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
public synchronized test(){*****} 锁住了这个对象即this,等同于 代码块 synchronized(this){*****},但不影响对其它代码块的调用。
因同步方法同步的是对象,如果有两个对象实例则不会影响。因此要注意避免在多线程中创建多个实例。
以下这种例子,就相当于没有加同步代码块。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /** 2 * 3 * 4 */ 5 public class ClassA { 6 public synchronized void setiTest(int iTest) { 7 8 9 ClassA a = new ClassA(); 10 ClassA b = new ClassA(); 11 Thread t1= new Thread(){ 12 @Override 13 public void run() { 14 a.setiTest(555555555); 15 System.out.println("-----"); 16 } 17 18 }; 19 20 Thread t1= new Thread(){ 21 @Override 22 public void run() { 23 b.setiTest(555555555); 24 System.out.println("-----"); 25 } 26 27 };
3. 修饰一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
因为静态方法属于类对象,而非实例对象。因此静态的synchronized方法作用域是所有的实例对应的类。也就是整个过程只会有一个口,不论你new多少对象。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
静态方法不加同步块: public static int staticMoney; private static int sum; public static void getStaticMoney(){ sum+=staticMoney; System.out.println(sum); } 测试口: public static void main(String[] args) { BankAccess bank = new BankAccess(); BankTreader handler = new BankTreader(bank); BankTreader handler2 = new BankTreader(bank); BankTreader handler3 = new BankTreader(bank); handler.staticMoney = 100; Thread t1 = new Thread(handler,"t1"); Thread t2 = new Thread(handler2,"t2"); Thread t3 = new Thread(handler3,"t3"); t1.start(); t2.start(); t3.start(); try { t1.sleep(100); t2.sleep(50); t3.sleep(0); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } 加sleep防止线程同时进行,就相当于一个执行完后,执行另一个。 结果 : 200 200 200 去掉sleep进行: 结果: 运行1次 100 300 200 运行 2次 100 200 100 运行 3次 200 200 300。。。。。。 会出现结果未知,因为static的1个方法同时被多个线程同时调用尽管是多个对象,但类只有一个,就并发安全问题。 加上synchronized,多次运行结果: 100 200 300 这样就保证了多个线程在运行这个方法时,变为单线程。
4. 修饰一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 public void handlerBank(){ 2 synchronized(BankAccess.class){ 3 System.out.println(System.currentTimeMillis() + "---A--" + bank.searchMoney()); 4 //拖延时间 5 for(int i=0; i <8000000;i++){ 6 int b= i; 7 } 8 bank.saveMoney(saveMoney); //存入 9 bank.getoutMoney((int)Math.round(bank.searchMoney()*0.01));//收取总额手续费,当然实际银行不会这样 10 System.out.println(System.currentTimeMillis() + "----B-----" + bank.searchMoney()); //查看 11 } 12 } 13 14 测试方法: 15 public static void main(String[] args) { 16 BankAccess bank = new BankAccess();--构造多个实例 17 BankAccess bank2 = new BankAccess(); 18 BankAccess bank3 = new BankAccess(); 19 bank.saveMoney(1000); 20 bank2.saveMoney(2000); 21 bank3.saveMoney(4000); 22 BankTreader handler = new BankTreader(bank);--构造多个操作类 23 Thread t1 = new Thread(handler,"t1"); 24 BankTreader handler2 = new BankTreader(bank2); 25 Thread t2 = new Thread(handler2,"t2"); 26 BankTreader handler3 = new BankTreader(bank3); 27 Thread t3 = new Thread(handler3,"t3"); 28 try { 29 t2.sleep(10); 30 t3.sleep(30); 31 } catch (InterruptedException e) { 32 // TODO Auto-generated catch block 33 e.printStackTrace(); 34 } 35 t1.start(); 36 t2.start(); 37 t3.start(); 38 39 } 40 41 42 结果: 43 1498732805187---A--1000 44 1498732805193----B-----990 45 1498732805193---A--4000 46 1498732805196----B-----3960 47 1498732805196---A--2000 48 1498732805200----B-----1980 49 50 结果分析: 51 对Bank类的操作还是变为线性,当t1,执行时,因执行时间长,因此当t2,t3在不同时间段醒来后,t1中的BankAccess依然没执行完,当执行完后,t3先抢到锁,因此t2,继续等待。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 public static void main(String[] args) { 2 BankAccess bank = new BankAccess(); 3 BankAccess bank2 = new BankAccess(); 4 BankAccess bank3 = new BankAccess(); 5 bank.saveMoney(1000); 6 bank2.saveMoney(2000); 7 bank3.saveMoney(4000); 8 BankTreader handler = new BankTreader(bank); 9 Thread t1 = new Thread(handler,"t1"); 10 handler.setBank(bank2); 11 Thread t2 = new Thread(handler,"t2"); 12 handler.setBank(bank3); 13 Thread t3 = new Thread(handler,"t3"); 14 try { 15 t2.sleep(10); 16 t3.sleep(30); 17 } catch (InterruptedException e) { 18 // TODO Auto-generated catch block 19 e.printStackTrace(); 20 } 21 t1.start(); 22 t2.start(); 23 t3.start(); 24 System.out.println("-------------------i am main------"); 25 } 26 27 结果: 28 -------------------i am main------ 29 1498733549403---A--4000 30 1498733549408----B-----3960 31 1498733549408---A--3960 32 1498733549411----B-----3920 33 1498733549411---A--3920 34 1498733549414----B-----3881 35 36 从结果可以看出: 37 尽管bank设置有1000,2000,4000.但因为t1,t2,t3并不是new的时候就执行,而是主程序已经结束了才开始的,此时handler中实际的对象为bank3.因此t1,t2,t3中都是4000的值。也就只有bank3一个对象,此对象在里面进行了3次扣款。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 public static void main(String[] args) { 2 BankAccess bank2 = new BankAccess(); 3 BankAccess bank1 = new BankAccess(); 4 bank2.saveMoney(4000); 5 bank1.saveMoney(2000); 6 BankTreader handler2 = new BankTreader(bank2); 7 BankTreader handler1 = new BankTreader(bank1); 8 Thread t1 = new Thread(handler1,"t1"); 9 Thread t2 = new Thread(handler2,"t2"); 10 t1.start(); 11 t2.start(); 12 System.out.println("-------------------i am main------"); 13 } 14 15 结果: 16 -------------------i am main------ 17 1498734103614---A--2000 18 1498734103620----B-----1980 19 1498734103620---A--4000 20 1498734103623----B-----3960 21 22 不同的对象,不同的调用者,但是结果还是线性同步进行的。
一个线程在访问一个对象的同步方法时,另一个线程可以同时访问这个对象的非同步方法。(只要不是同步方法,就不等对象锁,所以不管这个对象是否有其它线程锁定了,在其它线程访问非同步方法都不需要等同步锁的动作 )
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package jquery.test.interview; 2 3 public class ClassA { 4 private int iTest; 5 private final static Object obj = new Object(); 6 7 public int getiTest() { 8 System.out.println("---get1--" + System.currentTimeMillis()); 9 //synchronized(obj){ 10 for(int i=0;i<555555555;i++){ 11 int b= i; 12 } 13 //} 14 System.out.println("---get2--" + System.currentTimeMillis()); 15 return iTest; 16 } 17 18 public synchronized void setiTest(int iTest) { 19 System.out.println("---set1--" + System.currentTimeMillis()); 20 //synchronized(ClassA.class){ 21 for(int i=0;i<iTest;i++){ 22 int b= i; 23 } 24 //} 25 System.out.println("---set2--" + System.currentTimeMillis()); 26 this.iTest = iTest; 27 28 } 29 30 31 } 32 33 34 /** 35 测试类 36 **/ 37 package jquery.test.interview; 38 39 public class finalVarTest { 40 41 42 public static void main(String[] args) { 43 ClassA a = new ClassA(); 44 Thread t1= new Thread(){ 45 @Override 46 public void run() { 47 a.setiTest(555555555); 48 System.out.println("-----"); 49 } 50 51 }; 52 53 Thread t2= new Thread(){ 54 @Override 55 public void run() { 56 System.out.println("---a.getiTest();--" + a.getiTest()); 57 //a.getiTest(); 58 } 59 60 }; 61 //try { 62 t1.start(); 63 try { 64 t2.sleep(1); 65 } catch (InterruptedException e) { 66 // TODO Auto-generated catch block 67 e.printStackTrace(); 68 } 69 t2.start(); 70 //t1.join(); 71 //t2.join(); 72 //} catch (InterruptedException e) { 73 System.out.println("-join----"); 74 // e.printStackTrace(); 75 //} 76 77 78 79 System.out.println("-main----"); 80 } 81 82 }
一个线程在访问一个对象的同步方法时,另一个线程不能同时访问这个同步方法。
死锁:多个线程多个对象互相调用时,要谨防死锁。一般出现在双重同步代码锁中经常出现。如下面代码:两个对象,两个线程,A,B.A对象调用B对象,B对象调用A对象,这个时候就是各自有把锁,却在等待对方释放锁来执行调用的对象。
/** * 这种classB就容易锁住 */ public synchronized void setiTest(int iTest) { System.out.println("---set1--" + System.currentTimeMillis()); for(int i=0;i<iTest;i++){ int b= i; } synchronized(classB){ for(int i=0;i<iTest;i++){ int b= i; } //} System.out.println("---set2--" + System.currentTimeMillis()); this.iTest = iTest; } public static void main(String[] args) throws InterruptedException { ClassA c1 = new ClassA(); ClassA c2 = new ClassA(); c1.setClassB(c2); c2.setClassB(c1); Thread t1 = new Thread(new Runnable(){ @Override public void run() { c1.setiTest(50000000); } }, "t1"); Thread t2 = new Thread(new Runnable(){ @Override public void run() { c2.setiTest(50000000); } }, "t2"); t1.start(); t2.start(); System.out.println("-----Main thread----"); }
一个线程在访问一个对象的同步方法时,另一个线程不能同时访问其它同步方法。
如果一个线程在访问一个同步方法,则其他的同步方法此时也不允许其它线程访问,因为方法在调用时锁的是调用者对象,因此其它线程要访问其它方法,而其它方法也是同步的话,就需要解锁,而锁被另一个线程的另一个方法所占用,因此只能等待。
破解的方式:就是将锁加在方法中的代码段上,且对象用一个其它的不变对象,这样锁定的就是本方法,不会影响其它方法对锁的调用。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 public synchronized int getiTest() { 2 System.out.println("---get1--" + System.currentTimeMillis()); 3 //synchronized(obj){ 4 for(int i=0;i<555555555;i++){ 5 int b= i; 6 } 7 //} 8 System.out.println("---get2--" + System.currentTimeMillis()); 9 return iTest; 10 } 11 12 public synchronized void setiTest(int iTest) { 13 System.out.println("---set1--" + System.currentTimeMillis()); 14 //synchronized(ClassA.class){ 15 for(int i=0;i<iTest;i++){ 16 int b= i; 17 } 18 //} 19 System.out.println("---set2--" + System.currentTimeMillis()); 20 this.iTest = iTest; 21 } 22 23 main方法调用: 24 public static void main(String[] args) { 25 ClassA a = new ClassA(); 26 Thread t1 = new Thread(new Runnable(){ 27 @Override 28 public void run() { 29 a.setiTest(600000000); 30 } 31 }); 32 Thread t2 = new Thread(new Runnable(){ 33 @Override 34 public void run() { 35 a.getiTest(); 36 } 37 }); 38 t1.start(); 39 t2.start(); 40 } 41 42 结果: 43 ---set1--1498790176386 44 ---set2--1498790176614 45 ---get1--1498790176615 46 ---get2--1498790176827 47 48 49 ------------------------------------------------------------------------------ 50 修改get方法为代码段形式 51 52 private final static Object obj = new Object(); 53 54 public int getiTest() { 55 synchronized(obj){ 56 System.out.println("---get1--" + System.currentTimeMillis()); 57 for(int i=0;i<555555555;i++){ 58 int b= i; 59 } 60 System.out.println("---get2--" + System.currentTimeMillis()); 61 } 62 return iTest; 63 } 64 65 这样get方法的锁对象是obj,而非this。因此不会和set冲突。 66 67 结果: 68 69 ---get1--1498790448547 70 ---set1--1498790448547 71 ---get2--1498790448826 72 ---set2--1498790448844
3.2 volatile 关键字(不稳定的意思)
volatile用于多个线程共享某一数据时,指示读取数据从最新的主存中查找数据,指示JVM,这个变量是不稳定的,每次使用它都到主存中进行读取。一般说来,多任务环境下各任务间共享的标志都应该加volatile修饰。Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
volatile不保证原子操作,很容易读到脏数据。多个线程中共有变量,在不使用同步块时可以考虑用volatile关键字。
因回写的速度较快,因此大量数据的测试,才能看出结果。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 private volatile String str = "abc"; 2 public static void main(String[] args) throws InterruptedException, ExecutionException { 3 finalVarTest fv = new finalVarTest(); 4 Thread t1 = new Thread(new Runnable(){ 5 @Override 6 public void run() { 7 try { 8 for(int i=0;i<100;i++){ 9 fv.setStr("cde" + i); 10 Thread.sleep(10); 11 } 12 System.out.println("---01-before--1-" + fv.getStr()); 13 } catch (InterruptedException e) { 14 // TODO Auto-generated catch block 15 e.printStackTrace(); 16 } 17 } 18 }); 19 Thread t2 = new Thread(new Runnable(){ 20 @Override 21 public void run() { 22 try { 23 for(int i=0;i<100;i++){ 24 Thread.sleep(10); 25 System.out.println("---02-before--2-" + fv.getStr()); 26 } 27 28 } catch (InterruptedException e) { 29 // TODO Auto-generated catch block 30 e.printStackTrace(); 31 } 32 33 } 34 }); 35 System.out.println("I main 1........"); 36 t1.start(); 37 t2.start(); 38 System.out.println("I main 2........"); 39 } 40 public String getStr() { 41 return str; 42 } 43 public void setStr(String str) { 44 this.str = str; 45 } 46 47 结果: 48 ---当使用volatile时,保证了100条数据t2线程获取到的都是顺序的,从0-99. 49 ---02-before--2-cde0 50 ---02-before--2-cde1 51 ---02-before--2-cde2 52 ---02-before--2-cde3 53 ---02-before--2-cde4 54 ---02-before--2-cde5 55 ---02-before--2-cde6 56 ---02-before--2-cde7 57 ---02-before--2-cde8 58 ---02-before--2-cde9 59 ---02-before--2-cde10 60 ---02-before--2-cde11 61 ---02-before--2-cde12 62 ---02-before--2-cde13 63 ---02-before--2-cde14 64 ---02-before--2-cde15 65 ---02-before--2-cde16 66 ---02-before--2-cde17 67 ---02-before--2-cde18 68 ---02-before--2-cde19 69 ---02-before--2-cde20 70 ---02-before--2-cde21 71 ---02-before--2-cde22 72 ---02-before--2-cde23 73 ---02-before--2-cde24 74 ---02-before--2-cde25 75 ---02-before--2-cde26 76 ---02-before--2-cde27 77 ---02-before--2-cde28 78 ---02-before--2-cde29 79 ---02-before--2-cde30 80 ---02-before--2-cde31 81 ---02-before--2-cde32 82 ---02-before--2-cde33 83 ---02-before--2-cde34 84 ---02-before--2-cde35 85 ---02-before--2-cde36 86 ---02-before--2-cde37 87 ---02-before--2-cde38 88 ---02-before--2-cde39 89 ---02-before--2-cde40 90 ---02-before--2-cde41 91 ---02-before--2-cde42 92 ---02-before--2-cde43 93 ---02-before--2-cde44 94 ---02-before--2-cde45 95 ---02-before--2-cde46 96 ---02-before--2-cde47 97 ---02-before--2-cde48 98 ---02-before--2-cde49 99 ---02-before--2-cde50 100 ---02-before--2-cde51 101 ---02-before--2-cde52 102 ---02-before--2-cde53 103 ---02-before--2-cde54 104 ---02-before--2-cde55 105 ---02-before--2-cde56 106 ---02-before--2-cde57 107 ---02-before--2-cde58 108 ---02-before--2-cde59 109 ---02-before--2-cde60 110 ---02-before--2-cde61 111 ---02-before--2-cde62 112 ---02-before--2-cde63 113 ---02-before--2-cde64 114 ---02-before--2-cde65 115 ---02-before--2-cde66 116 ---02-before--2-cde67 117 ---02-before--2-cde68 118 ---02-before--2-cde69 119 ---02-before--2-cde70 120 ---02-before--2-cde71 121 ---02-before--2-cde72 122 ---02-before--2-cde73 123 ---02-before--2-cde74 124 ---02-before--2-cde75 125 ---02-before--2-cde76 126 ---02-before--2-cde77 127 ---02-before--2-cde78 128 ---02-before--2-cde79 129 ---02-before--2-cde80 130 ---02-before--2-cde81 131 ---02-before--2-cde82 132 ---02-before--2-cde83 133 ---02-before--2-cde84 134 ---02-before--2-cde85 135 ---02-before--2-cde86 136 ---02-before--2-cde87 137 ---02-before--2-cde88 138 ---02-before--2-cde89 139 ---02-before--2-cde90 140 ---02-before--2-cde91 141 ---02-before--2-cde92 142 ---02-before--2-cde93 143 ---02-before--2-cde94 144 ---02-before--2-cde95 145 ---02-before--2-cde96 146 ---02-before--2-cde97 147 ---02-before--2-cde98 148 ---02-before--2-cde99 149 --当不使用volatile时,不能保证t2取到的数据都是齐的。 150 ---02-before--2-cde1 151 ---02-before--2-cde1 152 ---02-before--2-cde3 153 ---02-before--2-cde3 154 ---02-before--2-cde5 155 ---02-before--2-cde5 156 ---02-before--2-cde6 157 ---02-before--2-cde8 158 ---02-before--2-cde8 159 ---02-before--2-cde9 160 ---02-before--2-cde10 161 ---02-before--2-cde11 162 ---02-before--2-cde12 163 ---02-before--2-cde13 164 ---02-before--2-cde14 165 ---02-before--2-cde15 166 ---02-before--2-cde16 167 ---02-before--2-cde17 168 ---02-before--2-cde18 169 ---02-before--2-cde19 170 ---02-before--2-cde20 171 ---02-before--2-cde21 172 ---02-before--2-cde22 173 ---02-before--2-cde23 174 ---02-before--2-cde24 175 ---02-before--2-cde25 176 ---02-before--2-cde26 177 ---02-before--2-cde27 178 ---02-before--2-cde29 179 ---02-before--2-cde29 180 ---02-before--2-cde30 181 ---02-before--2-cde32 182 ---02-before--2-cde32 183 ---02-before--2-cde33 184 ---02-before--2-cde34 185 ---02-before--2-cde35 186 ---02-before--2-cde36 187 ---02-before--2-cde37 188 ---02-before--2-cde38 189 ---02-before--2-cde40 190 ---02-before--2-cde40 191 ---02-before--2-cde41 192 ---02-before--2-cde42 193 ---02-before--2-cde43 194 ---02-before--2-cde44 195 ---02-before--2-cde45 196 ---02-before--2-cde46 197 ---02-before--2-cde47 198 ---02-before--2-cde48 199 ---02-before--2-cde49 200 ---02-before--2-cde51 201 ---02-before--2-cde51 202 ---02-before--2-cde52 203 ---02-before--2-cde53 204 ---02-before--2-cde54 205 ---02-before--2-cde55 206 ---02-before--2-cde56 207 ---02-before--2-cde57 208 ---02-before--2-cde58 209 ---02-before--2-cde59 210 ---02-before--2-cde60 211 ---02-before--2-cde61 212 ---02-before--2-cde62 213 ---02-before--2-cde63 214 ---02-before--2-cde64 215 ---02-before--2-cde66 216 ---02-before--2-cde67 217 ---02-before--2-cde68 218 ---02-before--2-cde69 219 ---02-before--2-cde70 220 ---02-before--2-cde71 221 ---02-before--2-cde72 222 ---02-before--2-cde73 223 ---02-before--2-cde74 224 ---02-before--2-cde75 225 ---02-before--2-cde76 226 ---02-before--2-cde77 227 ---02-before--2-cde78 228 ---02-before--2-cde79 229 ---02-before--2-cde80 230 ---02-before--2-cde81 231 ---02-before--2-cde82 232 ---02-before--2-cde83 233 ---02-before--2-cde84 234 ---02-before--2-cde85 235 ---02-before--2-cde86 236 ---02-before--2-cde87 237 ---02-before--2-cde88 238 ---02-before--2-cde89 239 ---02-before--2-cde90 240 ---02-before--2-cde91 241 ---02-before--2-cde92 242 ---02-before--2-cde93 243 ---02-before--2-cde94 244 ---02-before--2-cde95 245 ---02-before--2-cde96 246 ---02-before--2-cde97 247 ---02-before--2-cde98 248 ---02-before--2-cde99
4.线程状态
sleep:
![](https://i-blog.csdnimg.cn/blog_migrate/90a9daa97f4d7ef23213762179af56f9.png)
4. sleep作用为当前的作用域,而非当前对象。
1 System.out.println("I main 1........"); 2 t1.start(); 3 t1.sleep(6000); --在主线程中加sleep会作用的主线程上。sleep是在哪加作用到哪地方 4 System.out.println("I main 2........"); 5 6 结果: 7 I main 1........ 8 ---01----Thread-0 9 I main 2........
interrupt:
1. 打断正在Sleep的线程,让其赶紧执行。但是会在sleep中抛出一个innterruptException异常,如果没有sleep,则不会报错。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 public static void main(String[] args) throws InterruptedException, ExecutionException { 2 Thread t1 = new Thread(new Runnable(){ 3 @Override 4 public void run() { 5 try { 6 Thread.sleep(1000); 7 } catch (InterruptedException e) { 8 System.out.println("--谁把我打断了----"); 9 } 10 System.out.println("---01----" + Thread.currentThread().getName()); 11 } 12 }); 13 System.out.println("I main 1........"); 14 t1.start(); 15 //Thread.sleep(100); 16 t1.interrupt(); 17 System.out.println("I main 2........"); 18 } 19 20 结果: 21 I main 1........ 22 I main 2........ 23 --谁把我打断了---- 24 ---01----Thread-0
join:
1. join和sleep一样,都是Thread类的方法。join作用是让“主线程”等待“子线程”结束之后才能继续运行。即当前线程挂起,等子线程运行完后,再继续主线程。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 public static void main(String[] args) throws InterruptedException, ExecutionException { 2 Thread t1 = new Thread(new Runnable(){ 3 @Override 4 public void run() { 5 try { 6 7 Thread.sleep(1000); 8 System.out.println("---01-before--1-"); 9 } catch (InterruptedException e) { 10 e.printStackTrace(); 11 } 12 } 13 }); 14 System.out.println("I main 1........"); 15 t1.start(); 16 t1.join(); 17 System.out.println("I main 2........"); 18 } 19 20 -------------------------------------------------------------- 21 结果: 22 I main 1........ 23 ---01-before--1-abc 24 I main 2........
join(long millis); join(long millis, int nanos); --设置时间
yeald(屈服): Thread.yield()
- Yield是一个静态的原生(native)方法
- Yield告诉当前正在执行的线程把运行机会交给线程池中拥有相同优先级的线程。
- Yield不能保证使得当前正在运行的线程迅速转换到可运行的状态。 //不能保证立即交付,因cpu中有可能已经存在正在执行的。
- 它仅能使一个线程从运行状态转到可运行状态,而不是等待或阻塞状态
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 public static void main(String[] args) throws InterruptedException, ExecutionException { 2 Thread producer = new Thread(new Runnable(){ 3 @Override 4 public void run() { 5 for(int i=0;i<15;i++){ 6 System.out.println("---producer--1-"); 7 } 8 9 } 10 }); 11 Thread customer = new Thread(new Runnable(){ 12 @Override 13 public void run() { 14 for(int i=0;i<15;i++){ 15 System.out.println("---customer--2-"); 16 } 17 } 18 }); 19 System.out.println("I main 1........"); 20 //producer.setPriority(Thread.MIN_PRIORITY); 21 //customer.setPriority(Thread.MAX_PRIORITY); 22 producer.start(); 23 customer.start(); 24 25 System.out.println("I main 2........"); 26 } 27 28 ------------------------------------------------------------- 29 不带优先级时: 打印中customer 和productor交叉出现。 30 带优先级时,先打印customer后打印productor
yield后customer的优先权就转出来,这时候productor就有了执行的机会。但customer比productor有高优先级,因此还是会有customer在中间执行。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 public static void main(String[] args) throws InterruptedException, ExecutionException { 2 Thread producer = new Thread(new Runnable(){ 3 @Override 4 public void run() { 5 for(int i=0;i<15;i++){ 6 System.out.println("---producer--1-"); 7 } 8 9 } 10 }); 11 Thread customer = new Thread(new Runnable(){ 12 @Override 13 public void run() { 14 for(int i=0;i<15;i++){ 15 if(i>8){ //当执行8个时让渡 16 Thread.yield(); 17 } 18 System.out.println("---customer--2-"); 19 } 20 } 21 }); 22 System.out.println("I main 1........"); 23 producer.setPriority(Thread.MIN_PRIORITY); 24 customer.setPriority(Thread.MAX_PRIORITY); 25 producer.start(); 26 customer.start(); 27 System.out.println("I main 2........"); 28 } 29 30 --------------------------------------------------------------------------- 31 结果: 32 ---customer--2-0 33 ---producer--1-0 34 ---customer--2-1 35 ---producer--1-1 36 ---customer--2-2 37 ---producer--1-2 38 ---customer--2-3 39 ---customer--2-4 40 ---customer--2-5 41 ---customer--2-6 42 ---customer--2-7 43 ---customer--2-8 44 ---customer--2-9 45 ---producer--1-3 46 ---producer--1-4 47 ---producer--1-5 48 ---producer--1-6 49 ---producer--1-7 50 ---producer--1-8 51 ---producer--1-9 52 ---producer--1-10 53 ---producer--1-11 54 ---producer--1-12 55 ---producer--1-13 56 ---producer--1-14 57 ---customer--2-10 58 ---customer--2-11 59 ---customer--2-12 60 ---customer--2-13 61 ---customer--2-14
wait和notify,notifyAll
wait() 与 notify/notifyAll() 是Object类的方法,在执行两个方法时,要先获得锁。因此他们大多都是写在synchronized中。
wait和notify的执行顺序不能错,不然先notify后wait则程序等待。
在main方法中写 customer.wait();报错 java.lang.IllegalMonitorStateException,因为wait和notify要拿到锁才可以,此时因没有拿到锁所以报错。
且wait和notify必须作用于同一个对象。要不然不起作用。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package jquery.test.interview; 2 3 public class ClassA { 4 private int iTest; 5 private final static Object obj = new Object(); 6 private ClassA classB; 7 8 public ClassA getClassB() { 9 return classB; 10 } 11 12 public void setClassB(ClassA classB) { 13 this.classB = classB; 14 } 15 16 public int getiTest() throws InterruptedException { 17 System.out.println("---get1--" + System.currentTimeMillis()); 18 synchronized(obj){ 19 obj.wait(); 20 for(int i=0;i<555555555;i++){ 21 int b= i; 22 } 23 System.out.println("---get2--" + System.currentTimeMillis()); 24 } 25 return iTest; 26 } 27 28 public synchronized void setiTest(int iTest) { 29 System.out.println("---set1--" + System.currentTimeMillis()); 30 for(int i=0;i<iTest;i++){ 31 int b= i; 32 } 33 System.out.println("---set2--" + System.currentTimeMillis()); 34 this.iTest = iTest; 35 36 } 37 }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 ClassA ca = new ClassA(); 2 Thread t1= new Thread(){ 3 @Override 4 public void run() { 5 ca.setiTest(50000); 6 }; 7 }; 8 9 Thread t2= new Thread(){ 10 @Override 11 public void run() { 12 try { 13 ca.getiTest(); 14 } catch (InterruptedException e) { 15 // TODO Auto-generated catch block 16 e.printStackTrace(); 17 } 18 }; 19 }; 20 t1.start(); 21 t2.start(); 22 23 24 ------------------------------------------------------------------------------
结果: t2执行wait后,释放了锁,进入等待状态,但是没有唤醒的线程来重新唤醒,因此程序处于一直运行等待中,而未结束,除非使用interrupt,或者stop强行停止。
多用于生产者和消费者模式:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package jquery.test.interview; 2 3 import java.util.Vector; 4 5 public class Productor<T> implements Runnable { 6 7 private volatile Vector<T> v; 8 9 public Productor(Vector<T> v){ 10 this.v = v; 11 } 12 13 /** 14 * 生产产品 15 * @return 16 */ 17 public void createProduct(){ 18 synchronized(v){ 19 while(true){ 20 System.out.println("库存产品数....1.." + v.size()); 21 if(v.isEmpty() || v.size() < 3){ 22 System.out.println("库存紧张,开始生产......"); 23 v.add((T)"product1....."); 24 v.add((T)"product2....."); 25 v.add((T)"product3....."); 26 v.add((T)"product4....."); 27 v.add((T)"product5....."); 28 } 29 //开始等待 30 try { 31 System.out.println("库存产品数...2..." + v.size()); 32 v.notifyAll(); 33 v.wait(); 34 } catch (InterruptedException e) { 35 e.printStackTrace(); 36 } 37 38 } 39 } 40 } 41 42 @Override 43 public void run() { 44 createProduct(); 45 } 46 47 }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package jquery.test.interview; 2 3 import java.util.Vector; 4 5 public class Customer<T> implements Runnable { 6 7 private volatile Vector<T> vector; 8 9 public Customer(Vector<T> vector){ 10 this.vector = vector; 11 } 12 13 public void getProduct(){ 14 synchronized(vector){ 15 while(true){ 16 if(null == vector || vector.isEmpty()){ 17 try { 18 System.out.println("--没有产品等待中....."); 19 vector.wait(); 20 } catch (InterruptedException e) { 21 e.printStackTrace(); 22 } 23 }else{ 24 System.out.println("--获取产品使用....." + vector.get(0)); 25 vector.remove(0); 26 vector.notify(); 27 } 28 } 29 } 30 } 31 32 @Override 33 public void run() { 34 getProduct(); 35 } 36 37 }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 测试及结果: 2 public static void main(String[] args) throws InterruptedException, ExecutionException { 3 Vector<String> product = new Vector<String>(); 4 ExecutorService pool = Executors.newCachedThreadPool(); 5 Productor<String> proc = new Productor<String>(product); 6 pool.submit(proc); 7 for(int i =0;i<5;i++){ 8 Customer<String> cus = new Customer<String>(product); 9 pool.submit(cus); 10 } 11 } 12 13 ------------------------------------------------------------------------------- 14 库存产品数....1..0 15 库存紧张,开始生产...... 16 库存产品数...2...5 17 --获取产品使用.....product1..... 18 --获取产品使用.....product2..... 19 --获取产品使用.....product3..... 20 --获取产品使用.....product4..... 21 --获取产品使用.....product5..... 22 --没有产品等待中..... 23 库存产品数....1..0 24 库存紧张,开始生产...... 25 库存产品数...2...5 26 --获取产品使用.....product1..... 27 --获取产品使用.....product2..... 28 --获取产品使用.....product3..... 29 --获取产品使用.....product4..... 30 --获取产品使用.....product5..... 31 --没有产品等待中..... 32 --没有产品等待中..... 33 --没有产品等待中..... 34 --没有产品等待中..... 35 库存产品数....1..0 36 库存紧张,开始生产...... 37 库存产品数...2...5 38 --获取产品使用.....product1..... 39 --获取产品使用.....product2..... 40 --获取产品使用.....product3..... 41 --获取产品使用.....product4..... 42 --获取产品使用.....product5..... 43 --没有产品等待中..... 44 库存产品数....1..0 45 库存紧张,开始生产...... 46 库存产品数...2...5 47 --获取产品使用.....product1..... 48 --获取产品使用.....product2..... 49 --获取产品使用.....product3..... 50 --获取产品使用.....product4..... 51 --获取产品使用.....product5..... 52 --没有产品等待中..... 53 --没有产品等待中..... 54 --没有产品等待中..... 55 --没有产品等待中..... 56 --没有产品等待中..... 57 库存产品数....1..0 58 库存紧张,开始生产...... 59 库存产品数...2...5 60 --获取产品使用.....product1..... 61 --获取产品使用.....product2..... 62 --获取产品使用.....product3..... 63 --获取产品使用.....product4..... 64 --获取产品使用.....product5..... 65 --没有产品等待中..... 66 --没有产品等待中..... 67 --没有产品等待中.....
新建线程:
1.new Thread
public static void main(String[] args) { ClassA a = new ClassA(); Thread t1 = new Thread(){ @Override public void run() { //TODO System.out.println("------Thread t1----"); a.getiTest(); } }; t1.start(); System.out.println("-----Main thread----"); } 结果:------------------------------------------------------------
优先级可以用从1到10的范围指定。10表示最高优先级,1表示最低优先级,5是普通优先级。也可用 MIN_PRIORITY,MAX_PRIORITY,NORM_PRIORITY来设定优先级
2. new Runnable或者实现runnable接口
public static void main(String[] args) { ClassA a = new ClassA(); Thread t1 = new Thread(new Runnable(){ @Override public void run() { //TODO System.out.println("------Runnable t1----"); a.getiTest(); } }); t1.start(); System.out.println("-----Main thread----"); }
实现接口的方式:
/** *实现Runnable接口 */ public class BankTreader implements Runnable { @Override public void run() { handlerBank(); } ----------------------------------------------- public static void main(String[] args) { BankTreader b1 = new BankTreader(); Thread t1 = new Thread(b1, "t1"); t1.start(); System.out.println("-----Main thread----"); }
实现接口的方式会束缚: 多线程调用代码被固定死。 比如在BankThreader中 只能调用 handlerBank();而不能调用其它方法。而第一种就不牵扯这个问题,在需要的时候构造Runnable,传入需要调用方法。
3. 线程池
有这样1个场景,现在有100个数据需要处理,如果我们为其开辟new 100个线程,一次开销太大,同时cpu也不可能同时消化的了,从资源占有和性能上反而是种浪费。因此这就需要合理的开辟线程,假设我们开辟10个线程,这样每个线程去处理10笔数,这种就解决了性能和资源占有的问题。但是线程的执行不是线性的,不是A线程执行1个后,B线程执行1个,然后。。。。。,而是无序的有可能C线程都执行了2笔数据但A线程1笔都没有抢到。因此,为了合理的调度10个线程,使其尽量都满负荷的完成任务。就引入了线程池管理。
ThreadPoolExecutor:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
详细的工作原理参考:(深入理解Java之线程池)http://www.cnblogs.com/dolphin0520/p/3932921.html
ThreadPoolExecutor 主要方法说明:
execute()方法实际上是Executor中声明的方法,在ThreadPoolExecutor进行了具体的实现,这个方法是ThreadPoolExecutor的核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行。
submit()方法是在ExecutorService中声明的方法,在AbstractExecutorService就已经有了具体的实现,在ThreadPoolExecutor中并没有对其进行重写,这个方法也是用来向线程池提交任务的,但是它和execute()方法不同,它能够返回任务执行的结果,去看submit()方法的实现,会发现它实际上还是调用的execute()方法,只不过它利用了Future来获取任务执行结果(Future相关内容将在下一篇讲述)。
shutdown()和shutdownNow()是用来关闭线程池的。
线程池使用:
Java通过Executors提供四种线程池,分别为:
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。这种是动态线程数的线程池,适合处理量不是特别大的线程数,比如100个数据就会造成在开始运行时开辟过多线程,但5个或10个时就负载小。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。这种和第一种相反,适合大量数据长时计算,在保证运算速率时,又不挤兑其它程序的运行。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。这种是定时运行,延迟运行程序用,定时任务这种就比较适合。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。这是线性运行,一次保证只有一个运行,且必须依照顺序进行,不会出现无序状况。
创建连接池,通过utils下的Executors创建连接池,java监控(C:\Program Files (x86)\Java\jdk1.8.0_121\bin\jconsole.exe)执行结果。
Thread.sleep(100);//线程睡眠
Thread.currentThread().getName(); //获取当前运行thread的名字
线程池用完要关闭,es.shutdown();
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 public static void main(String[] args) throws InterruptedException { 2 Thread.sleep(2000); //去监控器找到这个java,启用监控 3 Executor ex = Executors.newFixedThreadPool(10); 4 for(int i=0; i < 100; i++){ 5 final int fi = i; 6 ex.execute(new Runnable(){ 7 @Override 8 public void run() { 9 try { 10 Thread.sleep(100); //延缓,使得线程不能及时结束 11 } catch (InterruptedException e) { 12 e.printStackTrace(); 13 } 14 System.out.println("####" + Thread.currentThread().getName() + "------------" + fi); 15 } 16 }); 17 } 18 }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 // 延迟执行 2 public static void main(String[] args) throws InterruptedException { 3 ScheduledExecutorService ex = Executors.newScheduledThreadPool(8); 4 for(int i=0; i < 10; i++){ 5 final int fi = i; 6 ex.schedule(new Runnable(){ //延迟执行 7 @Override 8 public void run() { 9 10 System.out.println("####" + System.currentTimeMillis() + "------------" + fi); 11 } 12 }, 2, TimeUnit.SECONDS); //延迟2秒执行 13 } 14 } 15 16 // 定时执行 17 public static void main(String[] args) throws InterruptedException { 18 ScheduledExecutorService ex = Executors.newScheduledThreadPool(8); 19 for(int i=0; i < 2; i++){ 20 final int fi = i; 21 ex.scheduleAtFixedRate(new Runnable(){ //定时执行 22 @Override 23 public void run() { 24 25 System.out.println("####" + System.currentTimeMillis() + "------------" + fi); 26 } 27 }, 0, 2, TimeUnit.SECONDS); //每次隔2秒执行 28 } 29 }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
4. Callable<V> 接口,Future<V> 接口,FutureTask实现类
因实现Runnable和继承Thread方式创建的线程都是没有回调函数的,也就是不知道什么时候结束的,哪一个结束了等。没法满足对线程结束时的管理操作。
Java SE 5.0引入的Callable和Future,可以构建带回调函数的线程接口。
Callable<V> 带有回调方法的接口V call()。Runnable接口void run()方法。
@FunctionalInterface public interface Callable<V> { /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception; }
Future<V>接口是用来获取异步计算结果的,可判断是否线程执行结束,获取结果,取消等操作 。
![](https://i-blog.csdnimg.cn/blog_migrate/34402bfa95a8d29d8df2f84913fced38.png)
因此,这两个要在一起连用才可以获取多线程的结果。
cancel方法中的参数params ,如果为true则试图中断当前的操作,返回true,false则等待当前的操作完成返回false.
get()可阻塞当前主线程,等待子线程完成后进行。
isDone可判断子线程是否已经结束,可用 while(!f.isDone){system.out.println("还没完还没完")}等待并检查是否完成。
FutureTask实现Runnable和Future接口的实现类,但没有实现Callable接口,callable要自己创建实现类,通过FutureTask构造器传入使用。
public interface RunnableFuture<V> extends Runnable, Future<V> {
public class FutureTask<V> implements RunnableFuture<V> { FutureTask(Callable<V> callable) FutureTask(Runnable runnable, V result)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 public static void main(String[] args) throws InterruptedException, ExecutionException { 2 String result = "1234"; 3 FutureTask<String> ft0 = new FutureTask<String>(new Runnable(){ 4 @Override 5 public void run() { 6 try { 7 Thread.sleep(1); 8 } catch (InterruptedException e) { 9 e.printStackTrace(); 10 } 11 System.out.println("-------runnable----"); 12 }}, result); 13 14 15 FutureTask<String> ft = new FutureTask<String>(new Callable<String>(){ 16 @Override 17 public String call() throws Exception { 18 System.out.println("-------call----"); 19 return "hello:---call"; 20 } 21 }); 22 Thread t1 = new Thread(ft); 23 t1.start(); 24 Thread t0 = new Thread(ft0); 25 t0.start(); 26 if(ft.get() != null){ //主线程在此处会等待其它线程完成,获取结果 27 System.out.println(ft.get()); 28 } 29 while(!ft0.isDone()){ 30 System.out.println("----还没结束----"); 31 } 32 System.out.println(ft0.get()); 33 }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 public static void main(String[] args) throws InterruptedException, ExecutionException { 2 ExecutorService es = Executors.newFixedThreadPool(5); 3 List<Future<String>> list = new ArrayList<Future<String>>(); 4 for(int i=0;i<10;i++){ 5 final int fi = i; 6 Future<String> fu = es.submit(new Callable<String>(){ 7 @Override 8 public String call() throws Exception { 9 System.out.println("---ok call--" + fi); 10 return "I'm callable----" + fi; 11 } 12 }); 13 list.add(fu); 14 } 15 es.shutdown(); 16 // 结果输出 17 for(Future<String> fu : list){ 18 System.out.println(fu.get()); 19 } 20 }
最后用FutureTask来实现连接池调用
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 public static void main(String[] args) throws InterruptedException, ExecutionException { 2 ExecutorService es = Executors.newFixedThreadPool(5); 3 List<FutureTask<String>> list = new ArrayList<FutureTask<String>>(); 4 for(int i=0;i<10;i++){ 5 final int fi = i; 6 FutureTask<String> ft = new FutureTask<String>(new Callable<String>(){ 7 @Override 8 public String call() throws Exception { 9 System.out.println("---ok call--" + fi); 10 return "I'm callable----" + fi; 11 } 12 }); 13 es.submit(ft); 14 list.add(ft); 15 } 16 es.shutdown(); 17 // 结果输出 18 for(Future<String> fu : list){ 19 System.out.println(fu.get()); 20 } 21 }
参考:
Java中的多线程你只要看这一篇就够了http://www.cnblogs.com/wxd0108/p/5479442.html
Java多线程学习(吐血超详细总结)http://www.mamicode.com/info-detail-517008.html
(深入理解Java之线程池)http://www.cnblogs.com/dolphin0520/p/3932921.html
(Java四种线程池的使用)http://cuisuqiang.iteye.com/blog/2019372