为什么使用多线程?
有多段代码任务需要同时执行。如何做到?
多处理器,多任务分别到各处理器,同时运行。(parallel并行)
单处理器,多任务排队到单处理器,分时运行。(concurrent并发)
进程:(Process)
一个程序就是一个进程。一个进程拥有独立的运行空间和资源。进程之间通信通过pipes或sockets进行。
线程:(Thread)
一个进程可含有多个线程。同一个进程下的线程,可共享资源。但需解决资源争抢的问题。
Java中的多线程:
Java中的线程都是Thread类的实例。
如果算上资源管理,每一个Java程序都是多线程。但在程序员眼里,都只有一个主线程,这个主线程可以创建多个子线程。
如何定义一个线程:
创建任何一个线程的目的,都是为了要同时执行某些任务。
所以,创建一个线程,就是编写该任务代码。
两种方法:一是继承Thread类,重写run()方法,将任务写在run()方法中。二是实现Runnable接口,实现run()方法,将任务写在run()方法中。
如何启动和中断一个线程:`
启动:`
调用Thread对象的start()方法。
中断:
调用Thread对象的方法interrupt(),该对象即可捕获到InterruptedException异常,捕获异常并退出执行。注意,要手动退出。被调用interrupt()方法的线程不会停止执行run()方法。
查看是否中断:
可通过Thread类的interrupted()方法和Thread对象的isInterrupted()方法查看该线程是否被中断。不同的是,前者会清除该线程的中断状态,后者不会。
当多线程争夺CPU运行时间时:
尝试退出占用:
在run方法中调用Thread类的静态方法yield(),当前线程将尝试放弃执行机会,但还有可能被CPU选中执行。
休眠退出占用:
在run方法中调用Thread类的静态方法sleep(),当前线程休眠并放弃执行机会。
让给特定的线程先执行:
在run方法中调用Thread对象的方法join(),当前线程将等待该Thread对象先执行完毕,再获得执行机会。
共享带来的麻烦:
多个线程可共享资源,意味着有可能同时需要操作同一个对象,即共享对象。将会造成该对象的数据不一致,而导致操作错误。
synchronized:在共享对象上加锁,来排队操作同一个对象。
在共享对象的方法前加入synchronized,多个线程调用该方法时,就会排队等待对象锁的释放,分别依次执行。问题是会引起线程活跃度下降。
在共享对象的一段代码前加入synchronized(Object 锁对象),多线程在调用这段synchronized代码块时,就会排队等待锁对象的释放。与上面的区别是该方式粒度更小,且锁对象可以不为自身。
当共享对象的属性是ThreadLocal类型的,如ThreadLocal<String>,在set该属性时,会将当前线程作为key,属性值作为value,存放到Map中。
这样,多线程在get该属性时,拿到的是当前线程自己独有的一份值。
http://blog.csdn.net/dada360778512/article/details/8138901
volatile:
每一个线程在执行时,都有自己专属的内存空间,一个属性在该内存中的值,与其他线程中的内存空间所存的值,是不同的备份。
被注明为volatile的属性,该值会被写道公共内存中,于是所有的线程所看到的值,就是同一个。但却不可保证非原子性的操作依然是同步的,如i++。所以使用时应避免有依据自身值的操作。
死锁问题:
当使用了synchronized后,会有两个线程同时等待对方释放锁对象的可能。
使用obj.wait()、obj.notify()和obj.notifyAll()来释放锁,wait()会释放obj的锁,进入obj的等待队列,notifyAll会释放obj锁,并通知所有obj等待队列中的所有线程。
设计多线程的程序的思路:
两种策略,一是需要同时执行任务时,就创建线程对象,并自己控制管理;一是将线程控制管理功能抽取出来,作为执行器。应用程序只需将任务创建好,交给执行器,由执行器负责线程任务的管理和执行。
执行器接口:
Executor接口。定义了execute(r)方法,r是一个Runnable对象。
ExecutorService接口。添加submit(c)方法,c是一个Callable对象,任务代码写在call()方法中,与run()方法不同的是,此方法可返回值,并被submit()方法返回到Future对象中。
ScheduledExecutorService接口。添加了schedule(r,long delay, TimeUnit unit)方法,任务可被delay后执行。还添加了scheduleAtFixedRate方法和scheduleWithFixedDelay方法,任务可定期重复执行。
执行器的获得:
通过Executors的静态方法,可获得不同特性执行器:
newSingleThreadExecutor()返回单线程执行器;
newCachedThreadPool()返回多线程执行器;
newFixedThreadPool(nThreads)返回固定线程数量的多线程执行器;
newSingleThreadScheduledExecutor()返回单线程可定期执行器;
newScheduledThreadPool(corePoolSize)返回固定线程数量的多线程可定期执行器;
如果不满足则可尝试ThreadPoolExecutor或ScheduledThreadPoolExecutor。
后台线程:
线程分为用户线程和后台进程,setDaemon将线程设置为后台进程,需要在start方法之前设置。
后台线程设计为为用户线程提供服务,如果用户线程全部结束,后台线程会自动结束。