/**
-
1.概念
-
进程:运行中的程序,是操作系统资源调配的独立单位
-
线程:
-
1.是进程中的执行分支(子任务);
-
2.一个进程只是有一个线程;
-
3.产生其他线程的线程叫做主线程;
-
4.单CPU环境下通过竞争CPU时间片实现多线程,同一时刻只能有一个线程被调度;
-
5.在多CPU(多核)环境下才真正实现多个线程同时执行(并发执行的线程数受CPU核数约束)。
-
2.创建
-
(1)继承Thread类,重写run方法
-
(2)实现Runnable接口,实现run方法
-
3.相关操作
-
(1) 启动
-
线程实例.start,进入运行队列,竞争并获取CPU时间片执行线程
-
(2)休眠
-
Thread.sleep(毫秒数)
-
(3)中断
-
线程执行完成,自动结束,也可以人为中断: 线程实例.interrupt();
3、同步
- List item
同步问题的产生:当多个线程访问同一个共享变量时。
每个对象默认含有一个监视器锁,当某个线程调用了该对象的同步方法或同步代码块时,则这个线程就拥有了(独占)该对象锁,其它线程均不能访问该对象的任意一个同步方法或同步代码块的;
但同步方法、同步代码块与ReentrantLock锁是互不影响的。
(即一个线程访问对象的同步方法或同步代码块时,另一个线程可以同时访问该对象由ReentrantLock锁住的代码块)
获得同一个对象锁的线程,可以同时访问该对象另个加锁的方法或代码块(可重入锁)
(1)同步方法(隐式锁)
即在方法前加synchronized关键字,(加锁范围是整个方法)
例:
1) public synchronized void foo(int i){
//非静态同步方法使用的是this锁
//代码
}
2) public static synchronized void foo(int i){
//静态同步方法使用的锁是该方法所在类的字节码文件对象,类名.class。
//代码
}
(2)同步块(隐式锁),(加锁范围是方法内部部分代码)
例:
1) synchronized(obj) {
//非静态同步块使用的是this锁
//代码
}
2)static synchronized(类名.class) {
//静态同步块或静态同步方法使用的锁是该方法所在类的字节码文件对象,类 名.class。
//代码
}
(3)ReentrantLock(显式锁)
例:
private ReentrantLock lock = new ReentrantLock();
public void foo(){
try {
// 加锁
lock.lock();
//其他代码
}finally {
// 释放锁
lock.unlock();
}
}
4.协作
(1) synchronized同步下的协作
1)等待锁
Object.wait()或者 Object.wait(long timeout)
2)唤醒等待锁的线程
Object.notify()或者Object.notifyAll()
3)wait,notify,notifyAll必须用在同步方法或同步块中,否则抛:
java.lang.IllegalMonitorStateException
2) ReentrantLock同步下的协作
ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
1)等待锁
condition.await()或 condition.await(long time,TimeUnit unit)
2)唤醒等待锁的线程
condition.signal()或condition.signalAll(long time,TimeUnit unit)
5.synchronized与ReentrantLock的区别
(1)synchronized与ReentrantLock都是可重入锁
(2)synchronized是基于JVM实现的,加锁,释放锁自动完成;
ReentrantLock是基于JDK实现的,需要通过代码手动加锁和释放锁(try……finally)
(3)synchronized同步方法是粗粒度的(锁住整个方法),但比ReentrantLock方便和简洁
ReentrantLock是细粒度的(锁住部分代码),比synchronized要灵活;
(4)synchronized只有非公平锁
ReentrantLock默认是非公平锁,但可设置为公平锁(ReentrantLock(boolean fair),当fair为true时)
[
公平锁:当持有锁的线程释放锁时,可以保证在等待锁队列中排在前面的线程优先获得锁
非公平锁:当持有锁的线程释放锁时,不可以保证在等待锁队列中排在前面的线程优先获得锁
]
(5)ReentrantLock可以创建多个条件(Condition),实现多组等待与唤醒;
synchronized只有一个隐含的条件,如果要实现多条件等待与唤醒,只能额外添加锁。
6.线程的6种状态
(1) 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
(2) 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的成为“运行”。
线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得cpu 时间片后变为运行中状态(running)。
(3)阻塞(BLOCKED):表线程阻塞于锁。
(4).等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
(5)超时等待(TIME_WAITING):该状态不同于WAITING,它可以在指定的时间内自行返回。
(6) 终止(TERMINATED):表示该线程已经执行完毕。
7.线程池
任务与线程分离,线程由Executor框架创建并管理(核心接口:Executor、ExecutorService)。
(1) ExecutorService threadPool = Executors.newSingleThreadExecutor();
创建只有一个线程的线程池,如果有多个任务,则按顺序依次执行。
1) void execute(Runnable command);
Executor接口中的方法,无返回值。
execute()方法是异步的。
2) Future<?> submit(Runnable task);
ExecutorService接口中的方法,无返回值。
submit()方法是异步的。
3)<T> Future<T> submit(Callable<T> task);
ExecutorService接口中的方法,T指定返回值类型,返回值封装在Future实例中。
submit()方法是异步的,Future.get()是同步的。
4)<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks);
ExecutorService接口中的方法,
通过线程池中的线程执行一组任务,所有任务执行完成,返回一组Future实例。
invokeAll方法是同步的。
5)<T> T invokeAny(Collection<? extends Callable<T>> tasks)
ExecutorService接口中的方法,
一旦有一个线程任务执行完成,其他线程将终止执行,返回完成任务线程的执行结果。
6) shutdown()
停止接受新任务,等所有正在执行任务的线程执行完成后关闭线程池。
7) shutdownNow()
立即停止所有正在执行任务的线程,关闭线程池。
(2) ExecutorService threadPool = Executors.newFixedThreadPool(nThreads);
创建一个指定线程数大小的线程池,任务被分配到空闲的线程中执行。
(3) ExecutorService threadPool = Executors.newCachedThreadPool();
创建一个按需的、灵活的、可伸缩的线程池;
当线程池中没有可用的线程时,将创建新的线程;
当线程池中的线程空闲超过60秒,将会被终止并从池中移除。
(4) ExecutorService threadPool = Executors.newScheduledThreadPool(size);
创建一个可让任务定时或按固定周期执行的线程池。
// 1) 延迟(delay)多长时间(TimeUnit,指定时间单位)后执行,只执行一次
// 参数依次:Runnable command, long delay, TimeUnit unit
//threadPool.schedule(task, 5, TimeUnit.SECONDS);
// 2) 按固定周期(period,指定执行周期)执行)
// 周期时间 < 任务耗时,则下次任务执行时间 = 上次任务开始时间 + 任务耗时
// 周期时间 > 任务耗时,下次任务执行时间 = 上次任务开始时间 + 周期时间
// 参数依次:Runnable command, long initialDelay, long period, TimeUnit unit
// threadPool.scheduleAtFixedRate(task, 1, 10, TimeUnit.SECONDS);
// 3) 下次任务执行时间 = 上次任务开始时间 + (耗时 + 延时时间)
// 受任务执行时间的影响
// 参数依次:Runnable command, long initialDelay, long delay, TimeUnit unit
threadPool.scheduleWithFixedDelay(task, 1, 6, TimeUnit.SECONDS);