一、创建线程
-
1.继承 Thread 类,重写
run()
方法栗子:
public class TestThread extends Thread { @Override public void run() { System.out.println("Hello World"); } public static void main(String[] args) { TestThread t = new TestThread(); t.start(); } }
注意啦!!!注意啦!!!注意啦!!!
调用
start()
方法并不会立即执行多线程的代码,而是将该线程变为可运行状态,什么时候运行多线程代码又操作系统决定。 -
2.实现 Runnable 接口,实现该接口的
run()
方法栗子:
public class MyRunnable implements Runnable{ @Override public void run() { System.out.println("Hello World"); } public static void main(String[] args) { Thread t = new Thread(new MyRunnable()); t.start(); } }
-
3.实现 Callable 接口,实现该接口的
call()
方法Callable 接口和 Runnable 接口功能类似,它是 Executor 框架的功能类,它比 Runnable 更强大的地方在于:
- Callable 可以在任务接受后提供一个返回值,Runnable 无法提供这个功能
- Callable 中的
call()
可以抛出异常,Runnable 的run()
遇到异常线程就停止了,不能抛出 - 运行 Callable 能拿到一个 Future 对象,这个对象表示异步计算的结果。通过调用 Future 对象的
get()
方法,就能获取线程的计算结果。
栗子:
public class MyCallable implements Callable
{ @Override public Integer call() throws Exception { return 10*1093; } public static void main(String[] args) { MyCallable c = new MyCallable(); ExecutorService executorService = Executors.newSingleThreadExecutor(); Future future = executorService.submit(c); try { System.out.println(future.get()); }catch (Exception e) { e.printStackTrace(); } } }
推荐使用第二种方法,直接实现 Runnable 接口。
二、volatile
- vloatile 无法保证变量的原子性
- 可见性
- 有序性
可用于双重检查的单例模式
public class Singleton {
private volatile static Singleton instance = null;
public static Singleton getInstance() {
if(instance == null) {
synchronized (Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
三、java 中的阻塞队列
1.ArrayBlockingQueue: 由数组结构组成的有界阻塞队列
2.LinkedBlockingQueue: 基于链表的有界阻塞队列
3.PriorityBlockingQueue: 支持优先级排序的无界阻塞队列
4.DelayQueue: 使用优先级队列实现的无界阻塞队列,支持延时获取元素
**5.SynchronousQueue: ** 不存储元素的阻塞队列
**6.LinkedTransferQueue: ** 由链表组成无界阻塞队列
**7.LinkedBlockingDqueue: ** 由链表组成的双向阻塞队列
四、线程池
- 1.FixedThreadPool: 可重用固定线程数的线程池。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
这种线程池只有核心线程,并且数量是固定的,没有非核心线程。
工作流程:当执行 execute()
时,如果线程池的核心线程数还没达到 corePoolSize ,就创建核心线程执行任务,否则就将任务丢进无界的阻塞队列中,当有核心线程空闲时再去执行任务。
- 2.CachedThreadPool: 根据需要创建线程的线程池
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
这种线程池没有核心线程,非核心线程是无界的。空闲线程等待新任务的最长时间是 60s。在这里用了 SynchronousQueue 不存储元素的阻塞队列,每个插入操作必须等待另一个线程的移除操作,同样任何一个移除操作都必须等待另一个线程的插入操作。
- 3. SingleThreadExecutor:使用单个工作线程的线程池
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
这种线程池能按照顺序逐一执行任务。
- 4. ScheduleThreadPool: 实现定时和周期性任务的线程池
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
这里使用的是优先级队列实现的无界阻塞队列,线程启动时需要延时获取任务,所以能实现延时或者周期执行。