Java 并发
基本线程
1、定义任务
实现Runnable接口并编写run()方法
class PrimeRun implements Runnable {
long minPrime;
PrimeRun(long minPrime) {
this.minPrime = minPrime;
}
//实现run方法
public void run() {
}
}
继承Thread类
class PrimeThread extends Thread {
long minPrime;
PrimeThread(long minPrime) {
this.minPrime = minPrime;
}
//重写Thread类的run方法
public void run() {
. . .
}
}
2、使用Executor
在JDK5中,在java.util.concurrent包中引入了Executors线程池,使得创建多线程更加方便高效
public class CachedThreadPool{
public static void main(String[] args){
//创建一个缓冲线程池服务
ExecutorService exec = Executors.newCachedThreadPool();
for(int i=0; i<5; i++){
//线程池服务启动线程
exec.execute(new PrimeRun());
}
//线程池服务停止
exec.shoutdown();
}
}
- Executors.newCachedThreadPool()在程序执行过程中通常会创建与所需数量相同的线程,然后在它回收旧线程时停止创建新线程,因此它是首选
- Executors.newFixedThreadPool(intsize)方法创建固定数目的线程池,即程序会创建指定数量的线程,每次同时运行intsize个线程
- Executors.newSingleThreadPool()创建单线程池,即固定数目为1的线程池相当于(Executors.newFixedThreadPool(1))
3、任务返回值
Runnable是执行工作的独立的任务,但它不好返回任何值,在Java SE5中引入的Callabel是一个具有类型参数的泛型,从方法call()(不是run())中返回的值
import java.util.concurrent.*;
import java.util.*;
class TaskWithResult implements Callable<String>{
private int id;
public TaskWithResult(int id){
this.id = id;
}
public String call(){
return “result of TaskWithResult ” + id;
}
public static void main(String[] args){
ExecutorService exec = Executors.newCachedThreadPool();
List<Future<String>> results = new ArrayList<Future<String>>();
for(int i=0; i<5; i++){
//将线程返回值添加到List中
results.add(exec.submit(new TaskWithResult(i)));
}
//遍历获取线程返回值
for(Future<String> fs : results){
try{
System.out.println(fs.get());
}catch(Exception e){
System.out.println(e);
}finally{
exec.shutdown();
}
}
}
}
输出结果(可能的结果,由于多线程执行顺序不确定,结果不固定):
result of TaskWithResult 0
result of TaskWithResult 1
result of TaskWithResult 3
result of TaskWithResult 4
result of TaskWithResult 5
注解:使用线程池服务的submit()方法执行线程池时,会产生Future对象,其参数类型是线程Callable的call()方法返回值的类型,使用Future对象的get()方法可以获取线程返回值。
休眠
简单的方法使用sleep(),使任务中止执行给定的时间
注意:不论是Thread.sleep还是TimeUnit的sleep线程休眠方法,都要捕获InterruptedExecutors。
4、优先级
线程的优先级将该线程的重要性传递给了调度器,CPU处理现有线程集的顺序使不确定的,但是调度器将倾向于让优先级最高的线程先执行。
可以通过Thread线程对象的getPriority()方法获取线程的优先级,可以通过线程对象的setPriority()方法设置线程的优先级。
Java的线程优先级总共有10级,最低优先级为1,最高为10,Windows的线程优先级总共有7级并且不固定,而Sun的Soloaris操作系统有23级,因此java的线程优先级无法很好地和操作系统线程优先级映射,所有一般只使用MAX_PRIORITY(10),NORM_PRIORITY(5)和MIN_PRIORITY(1)这三个线程优先级。
public class SimplePriorities implements Runnable{
private int countDown=5;
private volatile double d;
private int priority;
public SimplePriorities(int priority){
this.priority=priority;
}
public String toString(){
return Thrad.currentThread()+":"+countDown;
}
public void run(){
Thread.currentThread().setPriority(priority);
while(true){
for(int i=0;i<100000;i++){
d+=(Math.PI+Math.E)
if(i%1000==0){
Thread.yield();
}
}
}
System.out.println(this);
if(--countDown==0) return;
}
}
5、守护(daemon)线程
守护线程(DaemonThread)是某些提供通用服务的在后台运行的程序,是优先级最低的线程。当所有的非守护线程执行结束后,程序会结束所有的守护线程而终止运行。如果当前还有非守护线程的线程在运行,则程序不会终止,而是等待其执行完成。
public class SimpleDaemons implements Runnable{
public void run{
try{
System.out.println(“Start daemons”);
TimeUtil.SECONDS.sleep(1);
}catch(Exception e){
System.out.println(“sleep() interrupted”);
}finally{
System.out.println(“Finally is running”);
}
}
public static void main(String[] args) throws Exception{
Thread daemon = new Thread(new SimpleDaemons());
daemon.setDaemon(true);
daemon.start();
}
}
输出结果:
Start daemons
Finally没有执行,如果注释掉daemon.setDaemon(true)设置守护进程这一句代码。
输出结果:
Start daemons
Finally is running
之所以产生这样的结果原因是,main()是这个程序中唯一的非守护线程,当没有非守护线程在运行时,JVM强制推出终止守护线程的运行。
通过Thread对象的setDaemon方法可以设置线程是否为守护线程,通过isDaemon方法可以判断线程对象是否为守护线程。
由守护线程创建的线程对象不论有没有通过setDaemon方法显式设置,都是守护线程。
6、synchronized
编程中的共享资源问题会引起多线程的竞争,为了确保同一时刻只有一个线程独占共享资源,需要使用线程同步机制,即使用前对共享资源加锁,使用完毕之后释放锁。
Java中通过synchronized关键字实现多线程的同步,线程同步可以分为以下几种:
1、对象方法同步:
public synchronized void methodA(){
System.out.println(this);
}
2、类所有对象方法同步:
public synchronized static void methodB(){
System.out.println(this);
}
静态方法的线程同步锁对类的所有对象都起作用,即所有对象的线程在同一时刻只能有一个类的一个线程调用该方法。
3、对象同步代码块
public void methodC(){
synchronized(this){
System.out.println(this);
}
}
使用当前对象作为线程同步锁,同一个对象的不同线程在同一时刻只能有一个线程调用methodC方法中的代码块。
4、类同步代码块:
public void methodD(){
synchronized(className.class){
System.out.println(this);
}
}
使用类字节码对象作为线程同步锁,类所有对象的所有线程在同一时刻只能有一个类的一个线程调用methodD的同步代码块。
注意:线程的同步是针对对象的,不论是同步方法还是同步代码块,都锁定的是对象,而非方法或代码块本身。每个对象只能有一个线程同步锁与之关联。
7、捕获异常
线程产生的异常会传播到run()方法的外部,并且main()展示,但将放入try-catch语句块中是没有作用的还会是未捕获异常
public class ExceptionThread implements Runnable {
@Override
public void run() {
throw new RuntimeException("这个线程就干了这么一件事,抛出一个运行时异常");
}
public static void main(String[] args) {
try {
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new ExceptionThread());
System.out.println("该干嘛干嘛去");
} catch (RuntimeException e) {
System.out.println("能不能捕获到异常?");
}
}
}
运行结果
该干嘛干嘛去
Exception in thread "pool-1-thread-1" java.lang.RuntimeException: 这个线程就干了这么一件事,抛出一个运行时异常
at ExceptionThread.run(ExceptionThread.java:8)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:619)
Thread.UncaughtExceptionHandlersh是Java SE5的新接口,运行你在每个Thread对象上都附着一个异常处理器
public class MyUnchecckedExceptionhandler implements UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("捕获到异常:" + e);
}
}
main()
public class HandlerThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
System.out.println("创建一个新的线程");
Thread t = new Thread(r);
t.setUncaughtExceptionHandler(new MyUnchecckedExceptionhandler());
System.out.println("eh121 = " + t.getUncaughtExceptionHandler());
return t;
}
}
如果想让每个线程都默认异常处理器那么需要添加以下代码
Thread.setDefaultUncaughtExceptionHandler(new MyUnchecckedExceptionhandler());
这将产生和上面一样的结果
public class HandlerThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
System.out.println("创建一个新的线程");
Thread.setDefaultUncaughtExceptionHandler(new MyUnchecckedExceptionhandler());
Thread t = new Thread(r);
System.out.println("eh121 = " + t.getUncaughtExceptionHandler());
return t;
}
}
8、显式锁lock
JDK5之后,在java.util.concurrent.locks包中引入了显式锁机制
Lock对象必须显示的创建、锁定、释放
public class Locking{
//创建锁
private ReentrantLock lock = new ReentrantLock();
public void untimed(){
boolean captured = lock.tryLock();
try{
System.out.println(“tryLock(): ” + captured);
}finally{
if(captured){
lock.unlock();
}
}
}