Java 并发基础

来源:互联网 发布:zigbee协议栈编程 编辑:程序博客网 时间:2024/06/16 12:21

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();          }      }  } 
原创粉丝点击