2019.3.19 多线程开发相关
进程与线程
基本概念
-
什么是进程?什么是线程?
- 进程(Process):是OS进行资源分配和调度的基本单位。一个进程包含1到n个线程。
- 线程(Thread):是CPU进行资源分配和调度的最小单位。线程负责处理进程中无数的小任务。
-
进程和线程有什么特点?
- 如果要做进程之间的数据交换和切换,开销巨大;
- 线程的切换开销很小;
- 线程之间共享打码和数据空间;
-
进程创建有哪两种方式?
- 1.继承
- 2.接口
-
注意事项
- 1.在一个进程中 至少会有一个主线程;
- 2.除了主线程之外的 都可以叫子线程;
- 3.子线程可以有多个,如果没有限制,所有的子线程和主线程是并行的;
- 4.线程之间如果没有资源冲突,就各自执行互不相干;
使用继承方式创建进程
新建MyThread类:
public class MyThread extends Thread {
@Override
public void run() {
//在run中写要在线程中执行的代码
//获取当前线程名字
System.out.println("当前线程是:" + Thread.currentThread().getName());
for(int i = 0;i <100 ;i++) {
System.out.println("thread1:" + i);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
MainClass中:
MyThread myThread = new MyThread();
myThread.setName("thread1");
System.out.println("当前线程是:" + Thread.currentThread().getName());
myThread.start(); //启动线程使用start
样例测试:
使用接口创建进程
-
创建思路
- 使用接口形式创建一个任务类,要交给一个线程去执行该任务。
-
使用接口的优势
- 1.使用接口的形式创建进程,可以让程序更灵活;
- 2.强程序的健壮性,多次创建,可以被多个线程共享代码。
新建任务类MyTask:
public class MyTask implements Runnable{ @Override public void run() { //在run中写要在线程中执行的代码 //获取当前线程名字 System.out.println("当前线程是:" + Thread.currentThread().getName()); for(int i = 0;i <100 ;i++) { System.out.println(Thread.currentThread().getName() + i); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } }
MainCass中新添加代码:Thread t2 = new Thread(new MyTask(),"t2:"); t2.start();
匿名内部类
//匿名内部类:可以使用接口创建一个没有类名的类 new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } },"t3: ").start();
线程池
-
为什么要使用线程池?
- 1.节省资源 减少线程的数量和创建销毁进程的开销;
- 2.合理的管理线程的分配;
-
分类
1.newCatchedThreadPool
创建一个带有缓存的线程池
优点:
很灵活,弹性的线程管理,需要多少线程就给多大的线程池;
缺点:
如果线程无限增长,会导致内存溢出;
2.newFixedThreadPool
优点:
创建一个固定大小的线程池,超出的任务会在队列中等待;
缺点:
难以拓展,不支持自定义拒绝策略;
3.newScheduledThreadPool
优点:
支持周期性的执行任务,固定大小;
缺点:
单线程执行,一旦一个事务失败会影响其他任务;
4.newSingleThreadPool
优点:
能够顺序执行任务;
缺点:
不明白怎么设计的,可以使用对列来实现;
5.ThreadPoolExecutor
优点:上面所有的;
缺点:没有,只是定义比较复杂;
//这个地方填上面的分类
ExecutorService fixPool = Executors.newFixedThreadPool(5);
for (int i = 0; i < 20; i++) {
//execute:执行一个任务
fixPool.execute(new MyRunnable());
}
//关闭线程池
fixPool.shutdown();
- 线程池中参数的意义:
- corePOOLSize:核心池的大小
- maximumPoolSize:池内线程的最大数量
- keepAliveTime:(最大数量-核心池大小数量的)线程的存活时间
- TimeUnit unit的unit:上述时间的单位
- BlockingQueue:对列,小括号里是对列的大小 <>中是泛型.如果有任务需要等待线程执行,就会临时存储在这个对列中
- RejectedExecutionHandler:拒绝策略
ThreadPoolExecutor pool = new ThreadPoolExecutor(3,5,5, TimeUnit.SECONDS,new LinkedBlockingDeque<>(20));
new ThreadPoolExecutor.CallerRunsPolicy() {
};
pool.execute(new MyRunnable());
- 拒绝策略
- 1.AbortPolicy:丢弃任务,并且抛出异常
- 2.DiscardPolicy:丢弃任务,不抛异常
- 3.DiscardOldestPolicy:丢弃对列最前面的任务,尝试重新执行
- 4.CallerRunsPolicy:谁把任务给我的谁来执行
锁(sychronized)
- 锁相关
- 可以给类、方法、代码块加锁;
- 加的是“互斥锁(排它锁)”
加锁
-
如果关键字修饰方法:
- 1.被修饰的方法称为同步方法,作用范围是整个方法;
- 2.作用对象是调用这个方法的对象;
- 3.当不同线程都访问同一个同步方法时,后来的会等待锁的释放;
-
如果关键字修饰代码块:
- 1.作用范围是被包住的代码;
- 2.作用对象是调用这个代码段的对象;
-
如果关键字修饰类或者静态方法:
- 作用对象是这个类的所有对象;
样例测试:
新建Sthread类调用Runnable接口:
public class SThread implements Runnable {
private static int count = 0;
@Override
//synchronized不会被继承
public /*synchronized*/void run() {
System.out.println("111111");
synchronized (SThread.class) {//括号里SThread.class是当前的类 作用类型所有对象,前两个都是this
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ":" + (count++));
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
新建SClass类作为主函数:
public class SClass {
public static void main(String[] args) {
SThread sThread = new SThread();
new Thread(sThread,"t1").start();
new Thread(sThread,"t2").start();
}
}
工程及工具
工程
链接:https://pan.baidu.com/s/1i_y9emVMW70W1JPqyuN38Q
提取码:h3ci