多线程
1 创建多线程的4种方式:
1.1 继承Thread类
- 优点:代码简单
- 缺点:不能继承其他类(Java单继承多实现)
//继承Thread类
public class ThreadDemo {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
t1.setName("线程1:");
t2.setName("线程2:");
t1.start();
t2.start();
}
}
class MyThread extends Thread{
int i=100;
@Override
public void run() {
while (i>0){
System.out.println(Thread.currentThread().getName()+i--);
}
}
}
1.2 实现runnable接口
- 优点:可以继承其他类,统一实现该接口的实例可以共享资源(成员变量)
- 缺点:代码复杂
public class RunnableDemo {
public static void main(String[] args) {
RunableThread runableThread = new RunableThread();
Thread thread1 = new Thread(runableThread,"线程1:");
Thread thread2 = new Thread(runableThread,"线程2:");
thread1.start();
thread2.start();
}
}
class RunableThread implements Runnable{
int i;
@Override
public void run() {
for (int i = 0; i <100 ; i++) { System.out.println(Thread.currentThread().getName()+i);
}
}
}
1.3 实现callable接口(通过FutureTask包装类)
- 优点:里面实现接口的call( )方法是有返回值的。
- 调用返回值:通过调用FuturTask的get( )方法。
public class CallableDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CallableThread callableThread = new CallableThread();
FutureTask futureTask1 = new FutureTask(callableThread);
FutureTask futureTask2 = new FutureTask(callableThread);
Thread t1 = new Thread(futureTask1,"线程1");
Thread t2 = new Thread(futureTask2,"线程2");
t1.start();
t2.start();
System.out.println(futureTask1.get());
System.out.println(futureTask2.get());
}
}
class CallableThread implements Callable{
int i=100;
@Override
public Object call() throws Exception {
while (i>0){ System.out.println(Thread.currentThread().getName()+i--);
}
return Thread.currentThread().getName()+"线程的返回值,执行完毕时打印";
}
}
1.4 使用线程池
(Executors类提供了一系列的工厂方法来创建线程池,返回的方法都实现了ExecutorService的接口)
- 优点:实现的自动化装配,易于管理,循环利用资源
- 缺点:阿里巴巴java开发手册中明确规定不允许使用Executors创建线程池
- 原因:Executors创建线程池没有传入阻塞队列的长度,阻塞队列就是一个无边界队列,对于一个无边界队列来说是可以向其中无限添加任务的,这种情况下可能由于任务数太多而导致内存溢出。
- 推荐一使用:自定义ThreadPoolExecutor 类来创建线程,但是不能定义线程名字
- 强烈推荐使用Guava的ThreadFactoryBuilder来创建线程池,可以设置线程名字
//JDK里面的创建线程池的方法,容易OOM。不推荐使用
public class ThreadPoolDemo {
public static void main(String[] args) {
//创建一个指定数量的线程池对象
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.submit(new RunableThread2());
pool.submit(new RunableThread2());
//关闭线程池
pool.shutdown();
}
}
class RunableThread2 implements Runnable{
int i=100;
@Override
public void run() {
for (int i = 0; i <10 ; i++) { System.out.println(Thread.currentThread().getName()+i);
}
}
}
//为什么不能用Executors创建线程池,阻塞队列默认大小为Integer.Max_Value;
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
//自定义ThreadPoolExecutor,设置阻塞队列大小为5
ExecutorService pool = new ThreadPoolExecutor(5, 10, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5))
//阿里的推荐使用Guava的ThreadFactoryBuilder类来创建
//设置线程名字
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("线程%d:").build();
//创建线程池
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(5, 10, 10, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(10), threadFactory, new ThreadPoolExecutor.AbortPolicy());
2 Lock接口比synchronized代码块的优势
-
可以显示的获取和释放锁,锁的运用更灵活;
- Lock中的方法:
- lock( );加锁
- unlock( );释放锁
- Lock中的方法:
-
可以方便的实现公平锁
- 公平锁:先来先得,先进先出;(效率没有非公平锁高)
- 非公平锁:一种获取锁的抢占机制,先来不一定拿到锁,有可能线程一直拿不到锁。
-
Lock是synchronized的扩展版,Lock提供了无条件的、可轮循的(tryLock方法)、定时的(tryLock带参的方法)、可中断的(lockInterruptibly)、可多条件队列(newCondition方法)的锁操作。Lock的实现类基本都支持公平锁和非公平锁(默认),synchronized只支持非公平锁。