1.传统的多线程解决方案:Threads
1,Thread类
2,Runnable接口
jdk1.0
主要缺点:容易滥用;性能不好;线程缺乏统一管理,相互竞争,倒是死机和oom(内存溢出);不能定时操作,定时执行;
oom内存溢出是超出了jvm的内存空间现象。
增加jvm 的内存空间。(OS)
程序申请内存过大,虚拟机无法满足我们,然后自杀了。这个现象通常出现在大图片的APP开发,或者需要用到很多图片的时候。通俗来讲就是我们的APP需要申请一块内存来存放图片的时候,系统认为我们的程序需要的内存过大,及时系统有充分的内存,比如1G,但是系统也不会分配给我们的APP,故而抛出OOM异常,程序没有捕捉异常,故而弹窗崩溃了。
发生OOM的四种情况:
-
java堆溢出:heap
Java堆的JVM参数:
-Xms:初始堆大小,默认值为物理内存的1/64(<1GB),默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制.
-Xmx:最大堆大小,默认值为物理内存的1/4(<1GB),默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制
-Xmn:年轻代大小(1.4or lator),此处的大小是(eden + 2 survivor space),与jmap -heap中显示的New gen是不同的。 -
栈溢出:stack
JVM配置参数:
-Xss: 每个线程的堆栈大小,JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K.
在相同物理内存下,减小这个值能生成更多的线程.但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。 -
运行时常量溢出:constant
JVM参数有:
-XX:PermSize:设置持久代(perm gen)初始值,默认值为物理内存的1/64
-XX:MaxPermSize:设置持久代最大值,默认为物理内存的1/4 -
方法区溢出:directMemory
方法区主要存储被虚拟机加载的类信息,如类名、访问修饰符、常量池、字段描述、方法描述等。
相关的JVM参数同运行时常量。
2.线程池
jdk:1.5
使用线程池可以有策略地管理线程
System.out.println(Runtime.getRuntime().availableProcessors());
//确定你的电脑多并发时,开几个任务最快,是处理器核数的两倍,也即是逻辑处理器数量;
newFixedThreadPool定长线程池:
可以指定并发量;
public class Test001 {
public static void main(String[] args) {
Thread1 t0=new Thread1();
Thread1 t1=new Thread1();
Thread1 t2=new Thread1();
Thread1 t3=new Thread1();
Thread1 t4=new Thread1();
Thread1 t5=new Thread1();
Thread1 t6=new Thread1();
Thread1 t7=new Thread1();
Thread1 t8=new Thread1();
ExecutorService executorService=Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); //可设置并发量,此时设定位最佳数;
executorService.execute(t0);
executorService.execute(t1);
executorService.execute(t2);
executorService.execute(t3);
executorService.execute(t4);
executorService.execute(t5);
executorService.execute(t6);
executorService.execute(t7);
executorService.shutdown();
}
}
class Thread1 extends Thread{
@Override
public void run() {
for (int i=0;i<100;i++){
System.out.println(this.getName()+":"+i);
}
}
}
这种线程池线程之间进行抢时间片竞争,输出情况不定;
newSingleThreadExrcutor单线程化线程池
并发量位1
。。。
Thread1 t0=new Thread1();
Thread1 t1=new Thread1();
Thread1 t2=new Thread1();
Thread1 t3=new Thread1();
Thread1 t4=new Thread1();
Thread1 t5=new Thread1();
Thread1 t6=new Thread1();
Thread1 t7=new Thread1();
Thread1 t8=new Thread1();
ExecutorService executorService=Executors.newSingleThreadExecutor(); //没有参数
executorService.execute(t0);
executorService.execute(t1);
executorService.execute(t2);
executorService.execute(t3);
executorService.execute(t4);
executorService.execute(t5);
executorService.execute(t6);
executorService.execute(t7);
executorService.shutdown();
进程不争不抢挨个顺序执行
newScheduledThreadPool调度线程池
可定时执行,延时执行,可设定并发量;
返回类型与其他不同
public class Test001 {
public static void main(String[] args) {
Thread1 t0=new Thread1();
Thread1 t1=new Thread1();
Thread1 t2=new Thread1();
Thread1 t3=new Thread1();
Thread1 t4=new Thread1();
Thread1 t5=new Thread1();
Thread1 t6=new Thread1();
Thread1 t7=new Thread1();
Thread1 t8=new Thread1();
ScheduledExecutorService executorService=Executors.newScheduledThreadPool(3);
executorService.schedule(t0,1, TimeUnit.SECONDS);//延时一秒
executorService.execute(t1);//立即执行
executorService.scheduleAtFixedRate(t2,3,5,TimeUnit.SECONDS);//延时3秒,并每5秒执行一次,定时执行
}
}
class Thread1 extends Thread{
@Override
public void run() {
for (int i=0;i<3;i++){
System.out.println(this.getName()+":"+i);
}
}
}
定时,延时减少线程竞争;
newCachedThreadPool缓冲线程池
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
try {
Thread.sleep(index * 100);
} catch (Exception e) {
e.printStackTrace();
}
cachedThreadPool.execute(new Thread1());
}
//==========
lass Thread1 extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"gg");
}
}
一个可根据需要创建新线程的线程池,如果现有线程没有可用的,则创建一个新线程并添加到池中,如果有被使用完但是还没销毁的线程,就复用该线程。
综合练习
public static void main(String[] args) {
System.out.println(Runtime.getRuntime().availableProcessors());
// 重用线程池
// ExecutorService threadPool = Executors.newCachedThreadPool();
// 定长多线程池(可指定最大并发执行线程的线程池)
//ExecutorService threadPool = Executors.newFixedThreadPool(4);
// 单线程队列池(同时只有一个线程执行,且有序执行)
// ExecutorService threadPool = Executors.newSingleThreadScheduledExecutor();
// 并发队列线程池(指定并发数量的有序线程池)
ExecutorService threadPool = Executors.newScheduledThreadPool(2);
threadPool.execute(newThread("一"));
threadPool.execute(newThread("二"));
threadPool.execute(newThread("三"));
threadPool.execute(newThread("四"));
threadPool.execute(newThread("五"));
threadPool.execute(newThread("六"));
threadPool.shutdown();
}
private static Runnable newThread(String flag) {
Runnable thread1 = new Runnable() {
public void run() {
int i = 0;
while (true) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我是线程" + flag + ",我还活着呢");
i++;
if (i >= 5)
break;
}
}
};
return thread1;
}
异步:线程调用与结果获取
Jdk1.5版本除了,提供了线程池技术,还提供了异步调用获取技术:
Jdk1.0线程模型,不能直接获取子线程的执行结果 !
使用中间一个static变量,作为子线程的结果存储!
以前定义一个线程,只能使用thread或runable,
现在又多了一个Callable{ call()}
使用Callable 接口 &Future 接口 组合,完成一个异步结果获:
(这个组合必须使用线程池)
public static void main(String[] args) {
Callable10 callable10=new Callable10();
ExecutorService executorService= Executors.newFixedThreadPool(8);
Future future=executorService.submit(callable10);
System.out.println("submit...");
try {
int val= (int) future.get();
System.out.println("从线程返回的值:"+val);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
executorService.shutdown();
}
}
class Callable10 implements Callable{
@Override
public Object call() throws Exception {
return 100;
}
}
使用Callable 接口 &FutureTsak 类 组合,完成一个异步结果获:
版本一:需要线程池提交任务,需要FutureTask类包装Callable线程类
public static void main(String[] args) {
Callable10 callable10=new Callable10();
ExecutorService executorService= Executors.newFixedThreadPool(8);
//用FutureTask类包装
FutureTask futureTask=new FutureTask(callable10);
executorService.submit(futureTask); //提交任务完成
System.out.println("submit...");
try {
int val= (int) futureTask.get();
System.out.println("从线程返回的值:"+val);
} catch (Exception e) {
e.printStackTrace();
}
executorService.shutdown();
}
}
class Callable10 implements Callable{
@Override
public Object call() throws Exception {
return 100;
}
}
版本二:不需要线程池,需要FutureTask类包装Callable线程类,Thread类包装FutureTask类,启动线程
public static void main(String[] args) {
Callable10 callable10=new Callable10();
//不需要线程池
//用FutureTask类包装
FutureTask futureTask=new FutureTask(callable10);
//用jdk1.0的Thread类包装
new Thread(futureTask).start();
System.out.println("submit...");
try {
int val= (int) futureTask.get();
System.out.println("从线程返回的值:"+val);
} catch (Exception e) {
e.printStackTrace();
}
}
class Callable10 implements Callable{
@Override
public Object call() throws Exception {
return 100;
}
}