Java 并发基础

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();  
        }  
    }  
} 

猜你喜欢

转载自blog.csdn.net/qq_22989777/article/details/77338432