java 多线程,
java 多线程,
线程实现方式
通过调用线程的start方法,使线程进入就绪状态,等待CPU调度
1、继承类Thread,重写run方法
public class MyThread extends Thread{
@Override
public void run() {
super.run();
System.out.println(Thread.currentThread().getName());
}
调用
public class TestThread {
@Test
public void testThread() {
MyThread myThread = new MyThread();
myThread.setName("testThread");
myThread.start();
}
2、实现接口Runable,重写run方法
public class MyRunable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
@Test
public void testRunable() {
Runnable runnable = new MyRunable();
Thread thread = new Thread(runnable);
thread.setName("testRunable");
thread.start();
}
注意点:Thread类内部也实现了Runable接口
3、实现Callable接口,重写call方法;使用FutureTask对象包裹Callable的实现对象,以此来创建线程
public class MyCallable implements Callable<Integer> {
private int i;
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName());
i++;
return i;
}
}
@Test
public void testCallable() {
MyCallable myCallable = new MyCallable();
FutureTask futureTask = new FutureTask<Integer>(myCallable);
Thread thread = new Thread(futureTask);
thread.setName("testCallable");
thread.start();
try {
System.out.println(futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
注意点:FutureTask同时继承了Runable和Future接口,使其可以作为线程的创建目标,然后通过Future接口获取call的返回值;futureTask.get()方法时获取子线程的返回值,会一直阻塞,直至子线程返回。
线程异常
1、所有线程不允许抛出异常,需要内部自行处理,即使抛出异常父进程也不会处理,异常线程会自动结束不会影响其他线程;
2、如果想抛出异常则需要实现线程的异常处理类(uncaughtExceptionHandler)。
public class ThreadExceptionHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("捕获到异常:" + e.getMessage());
}
}
线程加入异常处理器
@Test
public void testThread() {
MyThread myThread = new MyThread();
myThread.setName("testThread");
myThread.setUncaughtExceptionHandler(new ThreadExceptionHandler());
myThread.start();
}
线程方法
1、join,让一个线程等待另一个线程执行完成后再继续。
2、sleep,让一个线程休眠一定时间,并进入阻塞状态,等时间到了才继续执行。
3、yield,让一个线程进入就绪状态,CPU从就绪状态的队列中重新选择其他的线程执行,有可能是同一个。
4、wait,让一个线程暂停执行,直至有该对象调用notify/notifyAll才能唤醒线程。
线程同步方法
由于对共享资源的竞争,导致多线程之间出现错误。
1、synchronized关键字,可以修饰方法、修饰方法块,当修饰静态方法时,相当于锁类,和对象锁是两种不同类型的锁。
2、ReentrantLock锁,可以使的同步锁对象与共享资源解耦。
3、volatitle修饰符,修饰共享资源变量,每次访问或者请求该变量,都会重新获取最新的值,不是用寄存器中的值。
4、ThreadLocal类的使用,改变量是存储线程数据的线程存储类,它隔离了共享资源的争夺,让每个线程都保持有共享资源的一个副本,从而去除了竞争条件;经典的做法是针对线程不安全的SimpleDateFormmater。
注意点:其他同步机制考虑的是以时间换空间的做法,而ThreadLocal类采用空间换时间的做法;若是多个线程之间共享资源,已达到通信效果,则使用同步机制;若只是用来隔离线程关系则,使用ThreadLocal类。
@Override public void run() { super.run(); lock.lock(); if (books.getPrices() >= 60) { System.out.println("消费前价格:" + books.getPrices()); int price = books.getPrices() - 60; books.setPrices(price); System.out.println("消费后的价格:" + price); } lock.unlock(); }
@Test public void testThreadPool() { Books books = new Books(1, 100); Lock lock = new ReentrantLock(); ThreadPoolExecutor newThreadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2); MyThread myThread1 = new MyThread(books, lock); newThreadPoolExecutor.execute(myThread1); MyThread myThread2 = new MyThread(books, lock); newThreadPoolExecutor.execute(myThread2); }
线程池
在多线程编程中,大部分时间会消耗在线程的创建和销毁,因此考虑是否能复用线程,从而引入了线程池。
1、线程池变量:corePoolSize,表示线程池的大小;maximumPoolSize,表示线程池任务量过大时,提供的补助策略,可以增加到多少个。
2、线程池提交任务的方法:submit(有返回值)、execute(无返回值),最终都是调用execute方法,submit只不过是封装了Future对象。
3、线程池处理策略:若线程池中的数目小于corePoolSize,则来一个任务创建一个线程去执行;如果大于等于corePoolSize,则会将任务存入缓存队列中,等待有空闲线程时再去执行;如果加入队列失败,则会重新创建一个线程去执行任务;如果线程数目达到了maximumPoolSize则会采取拒绝策略进行处理。
4、超时机制:当线程数目大于corePoolSize时,如果某个线程超过keepalivedTime,则线程会被终止,直到线程数目不大于corePoolSize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。
5、线程初始化:默认情况下,创建线程池后是没有线程的,需要提交任务后才回去创建线程;初始化方法:prestartCoreThread()创建一个核心线程;prestartAllCoreThreads()创建所有线程。
6、任务缓存队列(workerQueue用来存放等待执行的任务):ArrayBlockingQueue(基于数组的先进先出)、LinkedBlockingQueue(基于链表的先进先出)、synchronousQueue(不会保存队列,直接创建一个新的线程去执行)。
7、缓存队列拒绝策略(线程池数量超过maximumPoolSize):ThreadPoolExecutor.AbortPolicy(丢弃任务,抛出拒绝异常)、ThreadPoolExecutor.DiscardPolicy(丢失任务,不抛出异常)、ThreadPoolExecutor.DiscardOldestPolicy(丢弃前面的任务)、ThreadPoolExecutor.CallerRunsPolicy(由线程调度处理该任务)。
8、线程创建:new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAlivedTime,TimeUnit,workQueue,threadFactory(可忽略),RejectedExecutionHandler (可忽略));推荐使用Executor的静态方法创建线程池:Executors.newFixedThreadPool(
int
);ForkJoinPool(jdk8引入)每个线程维护一个队列,适合有父子关系的任务,可用较小的线程数执行多数的任务。
@Test public void testThreadPool() { /*ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<Runnable>(5));*/ ThreadPoolExecutor newThreadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(5); for (int i = 0; i < 10; i++) { MyThread myThread = new MyThread(); newThreadPoolExecutor.execute(myThread); System.out.println( "线程池中线程数目:" + newThreadPoolExecutor.getPoolSize() + ",队列中等待执行的任务数目:" + newThreadPoolExecutor .getQueue().size() + ",已执行玩别的任务数目:" + newThreadPoolExecutor.getCompletedTaskCount()); } }
9、线程池大小设置:
如果是CPU密集型任务,就需要尽量压榨CPU,参考值可以设为 NCPU+1;如果是IO密集型任务,参考值可以设置为2*NCPU。
相关文章
- 暂无相关文章
用户点评