多线程的概念:略
多线程的目的:提高效率
主线程:
package demo; //主线程 public class Demo { public static void main(String[] args) { function(); System.out.println(1); } public static void function(){ for (int i = 0; i < 10000; i++) { System.out.println(i); } } }
这段简单的代码,我们发现:
必须要先执行方法输出完10000次的数字后才可以打印第二行的数字1
那么有没有方法,可以做到在执行方法的同时执行第二行的输出?
Thread类
创建新线程的两种方法:
第一种:
package demo; public class SubThread extends Thread { //重写run方法 public void run(){ for(int i = 0 ; i< 50 ; i++){ System.out.println(i+"run"); } } }
package demo; public class ThreadDemo { public static void main(String[] args) { SubThread st1 = new SubThread(); st1.start(); for (int i = 0; i < 50; i++) { System.out.println(i+"main"); } } }
这里输出时候,发现打印的run和main随机出现,交错出现,而不像以前那样按顺序打印
原因:创建了新的线程,两条线程由cpu选择执行,我们无法控制
start方法开启新的线程,继承了Thread类因为只有继承了它才可以操作线程
重写run方法因为,Thread类本身没有写入有意义的run方法,相当于一个模板,供开发者使用
线程名:
每个线程都有自己的名字,主线程名:main,其他新建线程默认名:Thread-n
获取、修改线程名:
package demo1; public class NameThread extends Thread { public void run(){ System.out.println(super.getName()); //输出:默认是Thread-0,如果修改了,就是hello } }
package demo1; public class ThreadDemo { public static void main(String[] args) { NameThread nt1 = new NameThread(); nt1.setName("hello"); //修改线程名为hello,主线程不能改名 nt1.start(); Thread t = Thread.currentThread(); System.out.println(t.getName()); //输出:main } }
Thread类的一个实用方法:
package demo1; public class ThreadDemo { public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 10; i++) { Thread.sleep(1000); System.out.println(i); } //每次打印都会等待一秒,参数是毫秒值 } }
第二种创建线程方法:
package demo1; public class SubRunnable implements Runnable { public void run() { for (int i = 0; i < 50; i++) { System.out.println(i + "run"); } } }
package demo1; public class ThreadDemo { public static void main(String[] args) { SubRunnable sr1 = new SubRunnable(); Thread t1 = new Thread(sr1); t1.start(); for (int i = 0; i < 50; i++) { System.out.println(i + "main"); } } }
这种方式好处:
1.接口可以多实现,避免了单继承的局限性
2.线程和方法分离,更符合面向对象的特点
3.资源实现共享
这两种方式可以实用匿名内部类实现:
package demo1; public class ThreadDemo { public static void main(String[] args) { // 继承方式 new Thread() { public void run() { System.out.println("1"); } }.start(); // 实现接口方式 new Thread(new Runnable() { public void run() { System.out.println(2); } }).start(); } }
线程的状态:
1.新建状态:new Thread()创建线程对象
2.运行状态:使用了start()方法进入运行状态
3.退出状态:run方法结束,或者调用了stop方法(已过时,不建议实用)
4.阻塞状态:有时候使用了start方法,但不一定立即运行,或者运行之后CPU由于一些原因不再分配,由运行状态转到阻塞状态
5.休眠状态:前边提到的sleep方法就是这种状态,也有可能转到阻塞状态或者运行状态
6.等待状态:wait方法,无限等待,notify方法可以唤醒线程,可能转到运行或阻塞状态
注意:受阻塞是等待CPU的资源,休眠等待是放弃CPU的执行
线程池的概念:
一个容器,存入多个线程,需要时候,拿出执行,
运行结束后线程再回到容器中,这种方式可以提高效率
实现线程池:
package demo1; public class ThreadPoolRunnable implements Runnable { public void run(){ System.out.println(Thread.currentThread().getName()+"线程提交任务"); //输出: pool-1-thread-1线程提交任务 // pool-1-thread-2线程提交任务 }
package demo1; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolDemo { public static void main(String[] args) { //调用工厂类的方法创建线程池 ExecutorService es1 = Executors.newFixedThreadPool(2); es1.submit(new ThreadPoolRunnable()); es1.submit(new ThreadPoolRunnable()); //运行后不会停 es1.shutdown();//销毁线程池,不常用 } }
实现线程的Callable接口方式:
它弥补了Runnable方式的缺陷:无法抛出异常,并且有返回值
使用方法和Runnable方式基本一致:
示例:
package demo1; import java.util.concurrent.Callable; public class ThreadPoolCallable implements Callable<String>{ public String call(){ return "a"; } }
package demo1; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class ThreadPoolDemo { public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService es1 = Executors.newFixedThreadPool(2); Future<String>f1 = es1.submit(new ThreadPoolCallable()); String s1 = f1.get(); System.out.println(s1); //得到返回值,输出a } }
简单应用:多线程异步计算
使用两个线程计算求和:
package demo1; import java.util.concurrent.Callable; public class GetSumCallable implements Callable<Integer> { private int a; public GetSumCallable(int a) { this.a = a; } public Integer call() { int sum = 0; for (int i = 0; i <= a; i++) { sum += i; } return sum; } }
package demo1; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class ThreadPoolDemo { public static void main(String[] args) throws Exception { ExecutorService es1 = Executors.newFixedThreadPool(2); Future<Integer> f1 = es1.submit(new GetSumCallable(100)); Future<Integer> f2 = es1.submit(new GetSumCallable(300)); System.out.println(f1.get()); System.out.println(f2.get()); es1.shutdown(); } }