线程和进程的本质:由CPU进行调度的并发式执行任务,多个任务被快速轮换执行,使得宏观上具备多个线程或者进程同时执行的效果。html
线程独立拥有本身的线程ID,堆栈,程序计数器,局部变量,寄存器组值,优先级,信号屏蔽码,错误返回码等等,线程是独立运行的,其执行是抢占式的。线程共享进程资源,线程之间的通讯要进程之间的通讯来得容易得多。此外,线程的建立和销毁的开销也远远小于进程的系统开销。java
public class FirstThread extends Thread{ private int i; @Override public void run() { for(i=0;i<10;i++) System.out.println(getName()); // 继承自Thread } public static void main(String[] args) { new FirstThread().start(); new FirstThread().start(); // 注意启动线程须要用Start } }
public class SecondThread implements Runnable{ private int i; @Override public void run() { for(;i<10;i++) { System.out.println(Thread.currentThread().getName() + " "+ i); } } public static void main(String[] args) { SecondThread targetRunnable = new SecondThread(); new Thread(targetRunnable,"线程1").start(); new Thread(targetRunnable).start(); } }
public class ThridThread { public static void main(String[] args) { // lambda 表达式 + functionInterface 类型转换 // Callbable: 有返回值 FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>)()->{ int i =0; for(;i<100;i++) { System.out.println(Thread.currentThread().getName() + " "+ i); } return i; }); new Thread(task,"有返回值的线程").start(); try { System.out.println("子线程的返回值"+task.get()); }catch(Exception e) { e.printStackTrace(); } } }
注意:抢占式策略系统:系统会给每一个执行的线程一个小的时间段来处理任务,当该时间段用完以后,系统会剥夺该线程所占用的资源,让其余线程得到执行的机会。在系统调度时,还会考虑到线程的优先级问题。android
join()-线程:让一个线程等待另外一个线程,当在某个线程执行流中调用其余线程的join()方法,该线程将被阻塞,知道join线程执行完毕为止。缓存
public class SleepThread { public static void main(String[] args) throws Exception{ // 注意异常 for(int i =0;i<5;i++) { System.out.println("当前时间"+new Date()); Thread.sleep(1000); } } }
yield():线程让步,也是Thread的静态方法,使得正在执行的线程暂停,但不会阻塞线程,只是交出CPU的控制权,将线程转为就绪状态,让系统调度器从新调度一次。当某个线程调用yield方法暂停后,只有优先级与当前线程相同,或者优先级比当前线程更高的线程才有可能得到执行机会。安全
改变线程优先级:setPriority(int newPriority),高优先级的线程能得到更多的执行机会。网络
线程的同步的意义在于线程安全,也就是说有多个线程并发访问同一个对象,而线程调度的不肯定性可能带来潜在的安全问题。多线程
同步监视器:java多线程引入同步监视器来解决同步问题,任什么时候刻只能有一个线程得到对同步监视器的锁定,当同步代码块执行完成后,该线程会释放对同步监视器的锁定。java容许任何对象做为同步监视器,一般咱们使用可能被并发访问的共享资源做为同步监视器。并发
public class DrawThread extends Thread { private Account account; private double drawaccout; public DrawThread(String name,Account account,double drawaccount) { super(name); this.account = account; this.drawaccout= drawaccount; } public void run() { synchronized(account) { if(account.getBlance()>=drawaccount) { System.out.println(getName()+"取钱成功"); try { Thread.sleep(1); }catch(InterruptedException e) { e.printStackTrace(); } } } } }
public synchronized void draw(double amount) { …… }
class X{ private final ReentrantLock lock = new ReentrantLock(); //须要定义线程安全的方法 public void foo() { lock.lock();//加锁 try { // 须要保证线程安全的代码 } finally { lock.unlock();//使用finally块保证释放锁 } } }
死锁的问题:
产生死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已得到的资源保持不放。
(3) 不剥夺条件:进程已得到的资源,在末使用完以前,不能强行剥夺。
(4) 循环等待条件:若干进程之间造成一种头尾相接的循环等待资源关系。app
在系统中已经出现死锁后,应该及时检测到死锁的发生,并采起适当的措施来解除死锁。目前处理死锁的方法可归结为如下四种:
1) 预防死锁:这是一种较简单和直观的事先预防的方法。方法是经过设置某些限制条件,去破坏产生死锁的四个必要条件中的一个或者几个,来预防发生死锁。预防死锁是一种较易实现的方法,已被普遍使用。可是因为所施加的限制条件每每太严格,可能会致使系统资源利用率和系统吞吐量下降。jvm
2) 避免死锁:该方法一样是属于事先预防的策略,但它并不须事先采起各类限制措施去破坏产生死锁的的四个必要条件,而是在资源的动态分配过程当中,用某种方法去防止系统进入不安全状态,从而避免发生死锁。
3) 检测死锁:这种方法并不须事先采起任何限制性措施,也没必要检查系统是否已经进入不安全区,此方法容许系统在运行过程当中发生死锁。但可经过系统所设置的检测机构,及时地检测出死锁的发生,并精确地肯定与死锁有关的进程和资源,而后采起适当措施,从系统中将已发生的死锁清除掉。
4) 解除死锁:这是与检测死锁相配套的一种措施。当检测到系统中已发生死锁时,须将进程从死锁状态中解脱出来。经常使用的实施方法是撤销或挂起一些进程,以便回收一些资源,再将这些资源分配给已处于阻塞状态的进程,使之转为就绪状态,以继续运行。死锁的检测和解除措施,有可能使系统得到较好的资源利用率和吞吐量,但在实现上难度也最大。
java中应该避免死锁的出现。
线程安全是以牺牲程序运行效率为代价的,所以在注意线程安全的同时,也要注意不要滥用锁和同步方法,尽可能只对那些会改变竞争资源的方法进行同步。同时还要根据单线程和多线程运行环境来提供线程不安全和线程安全两种版本,JDK提供的StringBuilder,StringBuffer就是一个例子。
private final Lock lock = new ReentrantLock(); // Condition实例绑定在一个Lock对象上 private final Condition cond = lock.newCondition(); public void Draw(double drawamount) { lock.lock(); try { if(!flag) cond.await();//致使当前线程等待 else { // ... cond.signalAll();// 唤醒其余线程 } }catch(InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } }
public class Testjava{ public static void main(String[] args) throws Exception{ ExecutorService pool = Executors.newFixedThreadPool(6); Runnable target = ()->{ for(int i=0;i<100;i++) { System.out.println(Thread.currentThread().getName() + "的i值为:"+ i); } }; // 向线程池中提交两个线程 pool.submit(target); pool.submit(target); pool.shutdown(); } }
【Java8源码分析】线程池-Executor与ExecutorService的全面剖析
class Accout{ private ThreadLocal<String> name = new ThreadLocal<>(); public Accout(String str) { this.name.set(str); } public String getname() { return name.get(); } public void setname(String str) { this.name.set(str); } }
注意:ThreadLocal与其余同步机制都是为了解决访问同一资源冲突问题而出现的,可是侧重的领域不一样,同步机制为实现多个线程对相同资源访问的并发安全性,ThreadLocal则是隔离多个线程之间的数据共享,从而避免竞争。
Android中的多线程本质上也是Java的多线程,同时添加了一些不一样的特性和使用的场景。其中,最主要的一个区别就是Android中主线程和子线程中的区分,Android中的主线程是UI线程,负责运行四大组件并与用户实现交互,须要保持较高的反应速度,因此主线程不容许进行耗时的操做(好比说网络请求和访问),不然容易出现ANR现象,子线程则负责处理 一些耗时的任务,而若是子线程中想要实现对UI的操做,则须要经过Android的handler消息机制。
为何子线程中不容许对UI进行操做呢
由于Android的UI控件并非线程安全,多线程的并发访问会带来UI控件的不可预期的状态,且考虑到加锁机制会带来性能上的问题,所以Android在设计初期就禁止子线程处理UI。UI操做时ViewRootImpl会对操做者所在的线程进行checkThread,若是非主线程,会抛出CalledFromWrongThreadException。
那么Android除了java原生的Thread/Runnable等线程形态,还有哪些包装过了的有特色的线程形式?
很棒的参考:你真的了解AsyncTask
AsyncTask 是一个轻量级的一部任务类,在线程池中执行后台任务。而后将任务的进度和最终结果传递给主线程,并在主线程中更新UI。
// 三个泛型参数 不须要传递参数时,能够用void代替 public abstract class AsyncTask<Params,Progress,Result>
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> { //在线程池中执行 该方法必须返回计算结果给onPostExecute() protected Long doInBackground(URL... urls) { int count = urls.length; long totalSize = 0; for (int i = 0; i < count; i++) { totalSize += Downloader.downloadFile(urls[i]); // 可使用该方法返回任务的进度,该方法会调用onProgressUpdate() publishProgress((int) ((i / (float) count) * 100)); // Escape early if cancel() is called if (isCancelled()) break; } return totalSize; } // 被主线程调用执行 所以这里能够有UI操做 protected void onProgressUpdate(Integer... progress) { setProgressPercent(progress[0]); } // 在主线程中调用执行 任务执行结束后,会调用该方法,所以这里能够有UI操做 protected void onPostExecute(Long result) { showDialog("Downloaded " + result + " bytes"); } } // 主线程调用execute方法,执行任务前,会调用[1] onPreExecute()完成一些准备工做 // onPreExecute()是在主线程中执行 new DownloadFilesTask().execute(url1, url2, url3); // 也能够调用cancel来取消任务的执行
onPreExecute()
,onPostExecute(Result)
,doInBackground(Params...)
, onProgressUpdate(Progress...)
public abstract class AsyncTask<Params, Progress, Result> { private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();//CPU数 private static final int CORE_POOL_SIZE = CPU_COUNT + 1; private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; private static final int KEEP_ALIVE = 1; private static final ThreadFactory sThreadFactory = new ThreadFactory() { private final AtomicInteger mCount = new AtomicInteger(1); public Thread newThread(Runnable r) { return new Thread(r, "AsyncTask #" + mCount.getAndIncrement()); } }; private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(128); public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); } // 核心线程数 = CPU数+1 // 最大线程数 = CPU数*2 + 1 // 非核心线程的超时时间为1秒 // 任务队列的容量为128
Android 开发手册写明:AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the
java.util.concurrent
package such asExecutor
,ThreadPoolExecutor
andFutureTask
public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized(this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid =-1; }
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
- corePoolSize: 线程池的核心线程数,默认状况下, 核心线程会在线程池中一直存活, 即便处于闲置状态. 但若是将allowCoreThreadTimeOut设置为true的话, 那么核心线程也会有超时机制, 在keepAliveTime设置的时间事后, 核心线程也会被终止.
- maximumPoolSize: 最大的线程数, 包括核心线程, 也包括非核心线程, 在线程数达到这个值后,新来的任务将会被阻塞.
- keepAliveTime: 超时的时间, 闲置的非核心线程超过这个时长,讲会被销毁回收, 当allowCoreThreadTimeOut为true时,这个值也做用于核心线程.
- unit:超时时间的时间单位.
- workQueue:线程池的任务队列, 经过execute方法提交的runnable对象会存储在这个队列中.
- threadFactory: 线程工厂, 为线程池提供建立新线程的功能.
- handler: 任务没法执行时,回调handler的rejectedExecution方法来通知调用者.
若是线程池中线程的数目少于corePoolSize,就算线程池中有其余的没事作的核心线程,线程池仍是会从新建立一个核心线程;直到核心线程数目到达corePoolSize(常驻线程就位)
若是工做队列满了,而且线程池中线程的数目到达了最大数目maximumPoolSize,那么就会用最后一个构造参数handler处理;**默认的处理方式是直接丢掉任务,而后抛出一个异常。