进程和线程
操作系统有多个运行的软件
一个运行中的软件可能包含多个进程
一个运行中的进程可能包含多个线程
线程
按代码顺序执行下来,执行完毕就结束的一条线
UI线程不会结束,因为它在初始化完毕后就执行死循环,循环的内容是刷新界面
多线程使用
Thread
Thread thread1 = new Thread(){
@Override
public void run() {
System.out.println("线程执行了...");
}
};
thread1.start();
Runnable
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("线程执行了...");
}
};
Thread thread2 = new Thread(runnable);
thread2.start();
ThreadFactory
ThreadFactory threadFactory = new ThreadFactory() {
AtomicInteger count = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
count.incrementAndGet();
return new Thread(r, "Thread-" + count);
}
};
threadFactory.newThread(runnable1).start();
threadFactory.newThread(runnable2).start();
Executor线程池
常用:newCachedThreadPool() 可缓存的线程池
ExecutorService threadPool = Executors.newCachedThreadPool();
threadPool.execute(runnable1);
threadPool.execute(runnable2);
短时批量处理:newFixedThreadPool()
ExecutorService threadPool2 = Executors.newFixedThreadPool(20);
threadPool2.execute(runnable1);
threadPool2.execute(runnable2);
threadPool2.shutdown();// 关闭线程池
Callable 和 Future
Callable<String> callable = new Callable<String>(){
@Override
public String call() throws Exception {
Thread.sleep(2000);
return "Done";
}
};
ExecutorService threadPool = Executors.newCachedThreadPool();
Future<String> future = threadPool.submit(callable);// 把任务提交到线程池中执行,不会阻塞当前线程
try {
while(true) {
// 判断任务是否完成,如果完成再去获取结果
if (future.isDone()) {
String result = future.get();// 如果任务未完成,会阻塞
System.out.println("result:" + result);
}
// 执行其他任务
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
线程同步和线程安全
线程的同步控制是为了保护共有资源
synchronized
synchronized方法
private synchronized void method(){// 直接给方法上加synchronized,给方法加上当前对象的锁
}
synchrinized代码块
private void method(){
synchronized (this){// this指代当前对象,和直接在方法synchronized效果一样
}
}
private final Object monitor1 = new Object();// 监视器,当前代码没有执行完时,不会释放锁
private final Object monitor2 = new Object();
private void method(){
synchronized (monitor1){
synchronized (monitor2){
}
}
}
synchronized本质
保证方法内部或代码块内部资源(数据)的互斥访问。在同一时间、由同一个Monitor监视的代码,最多有一个线程在访问
保证线程之间对监视资源的数据同步。任何线程在获取到Monitor后的第一时间,会先将共享内存中的数据复制到自己的缓存中,任何线程在释放Monitor的第一时间,会先将缓存中的数据复制到共享内存中。
volatile
加了 volatile 关键字的操作具有同步性,因此 valatile 可以看做是简化版的 synchronized.
volatile 只对基本类型(byte、char、short、int、long、float、double、boolean)的赋值操作和对象的引用赋值操作有效,如果修改User.name是不能保证同步的。
volatile依然解决不了++的原子性问题。
AtomicInteger、AtomicBoolean等,作用和 volatile基本一致,可以看做是通用版的volatile
Lock/ReentrantReadWriteLock
Lock
使用起来更灵活,同时也麻烦一些
private int value;
private final Lock lock = new ReentrantLock();
private void count() {
lock.lock();
try {
value++;
} finally {
lock.unlock();
}
}
ReadWriteLock
private int value;
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
ReentrantReadWriteLock.ReadLock readLock = lock.readLock();// 读操作的锁
ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();// 写操作的锁
private void count(){
writeLock.lock();
try{
value++;
}finally {
writeLock.unlock();
}
}
private void print() {
readLock.lock();
try{
System.out.println(value);
} finally {
readLock.unlock();
}
}
线程安全问题的本质
在多个线程访问共同资源时,在某一个线程对资源进行写操作的中途,其他线程对这个写了一半的资源进行了读操作,或者基于这个写了一半的资源进行了写操作,导致出现了数据错误。
锁机制的本质
通过对共享资源进行访问控制,让同一时间只有一个线程可以访问资源,保证了数据的准确性。
不论是线程安全问题,还是针对线程安全问题所衍生出的锁机制,它们的核心都是在于共享资源,而不是某个方法或者某几行代码。
线程间通信
一个线程终止另外一个线程
Thread.stop() 不知道停止线程当前执行到哪里,造成结果不可预期
Thread.interrupt() 中断线程,给目标线程加了一个标记,不立即,不强制,需要目标线程配合处理,目标线程内部自己通过 isInterrupt() 或 Thread.interrupted() 来判断 当前线程是否被中断,但使用 Thread.interrupted() 判断会把中断标记重置为false
InterruptedException
如果在线程等待状态(Thread.sleep)时被中断(interrupt),直接结束等待过程,抛出InterruptedException,同时把中断标记重置为false
Object.wait() 和 Object.notify() / notifyAll()
在未达到目标时 wait()
设置完成后 notifyAll()
wait() 和 notify() / notifyAll() 都需要放在同步代码块里,在一个互斥的共享资源中wait等待,当操作结束后调用notify唤醒等待
Thread.join()
让一个线程插在自己前面,调用目标线程的join,就会等待目标完成后自己线程再执行
Thread.yield()
暂时让出自己的时间片给同优先级的线程
ConditionVariable
线程操作经常用到wait和notify,用起来比较繁琐,Android封装好了一个ConditionVariable类,用于提供线程同步
ConditionVariable cv = new ConditionVariable();
cv.block();// 锁住当前线程
cv.open();// 解锁被锁住的线程
CountDownLatch
是一个同步工具类,用来协调多个线程之间的同步,能够使一个线程在等待另外一些线程执行完后,再继续执行。
实现在某一线程等待n个线程执行完毕在运行
ExecutorService threadPool = Executors.newCachedThreadPool();
CountDownLatch countDownLatch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + " 运行了...");
Thread.sleep(2000);
countDownLatch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
threadPool.execute(runnable);
}
try {
countDownLatch.await();
System.out.println(Thread.currentThread().getName() + " 运行了...");
} catch (InterruptedException e) {
e.printStackTrace();
}
实现多个线程开始执行任务的并行性
ExecutorService threadPool = Executors.newCachedThreadPool();
CountDownLatch countDownLatch = new CountDownLatch(1);
for (int i = 0; i < 3; i++) {
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
countDownLatch.await();// 等待任务的执行
System.out.println(Thread.currentThread().getName() + " 运行了...");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
threadPool.execute(runnable);
}
try {
Thread.sleep(2000);
countDownLatch.countDown();// 唤醒其他线程开始执行任务
} catch (InterruptedException e) {
e.printStackTrace();
}
CyclicBarrier
循环屏障,等待所有的任务都到达再执行任务,屏障可以循环使用
public class CyclicBarrierDemo {
static class TaskThread extends Thread {
CyclicBarrier barrier;
public TaskThread(CyclicBarrier barrier) {
this.barrier = barrier;
}
@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println(getName() + " 到达栅栏 A");
barrier.await();
System.out.println(getName() + " 冲破栅栏 A");
Thread.sleep(2000);
System.out.println(getName() + " 到达栅栏 B");
barrier.await();
System.out.println(getName() + " 冲破栅栏 B");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
int threadNum = 5;
CyclicBarrier barrier = new CyclicBarrier(threadNum, new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 完成最后任务");
}
});
for(int i = 0; i < threadNum; i++) {
new TaskThread(barrier).start();
}
}
}
Semaphore
信号量,可以用来控制访问特定资源的线程数量
public class SemaphoreTest {
private static final int COUNT = 40;
private static Executor executor = Executors.newFixedThreadPool(COUNT);
private static Semaphore semaphore = new Semaphore(10);// 存储数据控制最大10个线程
public static void main(String[] args) {
for (int i=0; i< COUNT; i++) {
executor.execute(new ThreadTest.Task());
}
}
static class Task implements Runnable {
@Override
public void run() {
try {
//读取文件操作
semaphore.acquire();// 获取一个信号量
// 存数据过程
semaphore.release();// 释放一个信号量
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}