1.创建新线程常用的两种方法:第一个是从Thread类继承,另一个是实现接口runnable。
public class wdwtest{ public static void main(String args[]){ Runner1 r1 = new Runner1(); Thread t = new Thread(r1); t.start(); for(int i=0;i<10;i++){ System.out.println("maintheod:"+i); } }}class Runner1 implements Runnable{ public void run(){ for(int i=0;i<10;i++){ System.out.println("Runner1:"+i); } }}
2.线程的常用方法
Thread.yield() 让出cpu,当前线程进入就绪队列等待调度,不一定会被执行。 Thread.sleep(1000) 将当前线程睡眠指定毫秒数。 静态方法,可以由类名直接调用。 join() 如果在一段程序中调用t.join(),则当前线程将会挂起,直到线程t执行完毕。 也可以在t线程上调用interrupt(),中断调用。 wait() 当前线程进入对象的wait pool,会释放锁。 notify()/notifyAll() 唤醒等待的线程。 isAlive() 判断线程是否已终止。Thread subThread = new Thread(new Runnable(){ public void run(){ for(int i=0;i<10;i++){ System.out.println("Runner1:"+i); } }});subThread.start();subThread.join();//相当于顺序执行代码 for(int i=0;i<10;i++){ System.out.println("maintheod:"+i); }
3.Executor执行器
ExecutorService是具有服务周期的Executor。 shutdown()方法可以防止新的任务被提交给Executor,已经提交的方法会继续执行,直到执行完毕。 CachedThreadPool 在执行过程中创建所需的线程,然后在缓存线程。 FixedThreadPool 可以预先创建指定数量的线程。 SingleThreadExecutor 只创建一个线程,如果向其分配了多个线程,则这些线程会顺序执行。ExecutorService executor = Executors.newCachedThreadPool();for(int j=0;j<5;j++){ executor.execute(new Runnable() { public void run() { for (int i = 0; i < 10; i++) { System.out.println("Runner"+Thread.currentThread().getName()+":" + i); } } });}executor.shutdown();
4.获取返回值Callable接口
如果想在其他县城执行完后获取返回值,应该使用Callable接口,并使用ExecutorService.submit方法调用线程。 Callable接口会返回Future对象,调用Callable.get()方法可以获取到返回值, 如果Future还未返回,get()方法会阻塞,可以使用isDone()方法判断Future是否已经返回。ExecutorService executor = Executors.newCachedThreadPool();Futurefuture = executor.submit(new Callable () { public String call() throws Exception { return "this is result"; } });System.out.println(future.get());
5.守护线程daemon
在所有非守护线程都执行完成后,守护线程会立刻结束。守护线程中的finally并不一定会得到执行。ExecutorService executor = Executors.newCachedThreadPool(new ThreadFactory() { public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setDaemon(true); return t; }});executor.execute(new Runnable() { public void run() { for (int i = 0; i < 1000; i++) { System.out.println("Runner"+Thread.currentThread().getName()+":" + i); Thread.yield(); } }});for (int i = 0; i < 10; i++) { System.out.println("Runner"+Thread.currentThread().getName()+":" + i);}
6.线程异常处理 Thread.UncaughtExceptionHandler
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler(){ public void uncaughtException(Thread t, Throwable e) { System.out.println("异常处理完成"); }});ExecutorService executor = Executors.newCachedThreadPool();executor.execute(new Runnable() { public void run() { throw new RuntimeException("发生异常"); }});
7.线程锁 synchronized
①无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象; 如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。 ②synchronized不能被子类继承 子类需要显式说明synchronized ③使用并发时应将域设置为private,否则synchronized不能阻止其他任务直接访问域lock ①lock()和unlock()必须被显示的创建、锁定和释放 ②unlock()必须放置在finally块中,return语句必须放在try块中,确保unlock()不会过早发生。public int next(){ lock.lock(); try{ ++currentEvenValue; return currentEvenValue; }finally{ lock.unlock(); }}
8.结束任务
常用的关闭线程的方法 ①在run()方法设置退出标志,使线程正常退出。 ②通过Future.cancel(true)实现取消 ③关闭ExecutorService 使用shutdownNow强行关闭 ④使用interrupt()方法中断当前线程 a.线程处于阻塞状态 调用线程的interrupt()方法时,会抛出InterruptException异常。 并在InterruptException中执行相关退出处理 b.线程未处于阻塞状态 当使用interrupt()方法时,中断标志就会置true,在要被中断的任务中 , 使用isInterrupted()判断线程的中断标志来退出循环。这和使用自定义的标志来控制循环是一样的道理。//正确的使用方法:public class ThreadSafe extends Thread { public void run() { while (!isInterrupted()){ //非阻塞过程中通过判断中断标志来退出 try{ Thread.sleep(5*1000);//阻塞过程捕获中断异常来退出 }catch(InterruptedException e){ e.printStackTrace(); break;//捕获到异常之后,执行break跳出循环。 } } } }
⑤使用stop方法退出
从SUN的官方文档可以得知,调用Thread.stop()方法是不安全的,这是因为当调用Thread.stop()方法时,会发生下面两件事: 1. 即刻抛出ThreadDeath异常,在线程的run()方法内,任何一点都有可能抛出ThreadDeath Error,包括在catch或finally语句中。 2. 会释放该线程所持有的所有的锁,而这种释放是不可控制的,非预期的。 参考: https://blog.csdn.net/xu__cg/article/details/528311279.锁的释放 ①在调用sleep()和yield()时,锁不会被释放。 ②但调用wait(),线程将被挂起,线程上的锁会被释放掉。这是需要十分注意的一点。 ③由于要操作锁,所以wait(),notify(),notifyAll()只能在同步(synchronized)方法或同步块中调用。 如果在非同步块中调用,会发生java.lang.IllegalMonitorStateException异常 正确的调用方法,应先获取到对象的锁synchronized(this){ this.wait(2000);}
④await()和signal()就是其中用来同步的两种方法,功能基本上和wait()/notify()相同,
但是它们和新引入的锁定机制Lock直接挂钩,具有更大的灵活性。 await()和signal()一般与Lock Condition连用10.避免死锁 ①所谓系统是安全的,是指系统中的所有进程能够按照某一种次序分配资源,并且依次地运行完毕,这种进程序列{P1,P2,...,Pn}就是安全序列。 安全序列是指一个进程序列{P1,…,Pn}是安全的,即对于每一个进程Pi(1≤i≤n), 它以后尚需要的资源量不超过系统当前剩余资源量与所有进程Pj (j < i )当前占有资源量之和。 ②银行家算法 有四个客户C0,C1,C2,C3,向银行家借款,该银行家的资金总额为10个资金单位,银行家已经借给C0 5个单位资金, 其中C1客户要借9各资金单位,C2客户要借3个资金单位,C3客户要借8个资金单位,总计20个资金单位。按照安全序列的要求,我们选的第一个客户应满足该客户所需的贷款小于等于银行家当前所剩余的钱款,
可以看出只有C2客户能被满足:C2客户需3个资金单位,小银行家手中的5个资金单位, 于是银行家把3个资金单位借给C2客户,使之完成工作并归还所借的3个资金单位的钱。 以此类推,所有客户都可以借到钱。从上面分析看出,银行家算法允许死锁必要条件中的互斥条件,占有且申请条件,不可抢占条件的存在,
这样,它与预防死锁的几种方法相比较,限制条件少了,资源利用程度提高了。这是该算法的优点。其缺点是:
①这个算法要求客户数保持固定不变,这在多道程序系统中是难以做到的。 ②这个算法保证所有客户在有限的时间内得到满足,但实时客户要求快速响应,所以要考虑这个因素。 ③由于要寻找一个安全序列,实际上增加了系统的开销。11.CountDownLatch、CyclicBarrier、Semaphore 1)CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同: CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行; 而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行; 另外,CountDownLatch是不能够重用的,而CyclicBarrier是可以重用的。 2)Semaphore其实和锁有点类似,它一般用于控制对某组资源的访问权限。 http://www.importnew.com/21889.html①CountDownLatch用法
CountDownLatch类位于java.util.concurrent包下,利用它可以实现类似计数器的功能。 比如有一个任务A,它要等待其他4个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了。CountDownLatch类只提供了一个构造器:
public CountDownLatch(int count) { }; //参数count为计数值
下面这3个方法是CountDownLatch类中最重要的方法:
//调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行public void await() throws InterruptedException { }; //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行public boolean await(long timeout, TimeUnit unit) throws InterruptedException { }; //将count值减1public void countDown() { };
public static void main(String args[]) throws InterruptedException, ExecutionException{ final CountDownLatch latch = new CountDownLatch(2); //任务1 new Thread(){ public void run() { try { System.out.println("子线程"+Thread.currentThread().getName()+"正在执行"); Thread.sleep(3000); System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕"); latch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); //任务2 new Thread(){ public void run() { try { System.out.println("子线程"+Thread.currentThread().getName()+"正在执行"); Thread.sleep(3000); System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕"); latch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); try { System.out.println("等待2个子线程执行完毕..."); latch.await(); System.out.println("2个子线程已经执行完毕"); System.out.println("继续执行主线程"); } catch (InterruptedException e) { e.printStackTrace(); }}
②CyclicBarrier用法
字面意思回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行。 叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。 我们暂且把这个状态就叫做barrier,当调用await()方法之后,线程就处于barrier了。CyclicBarrier类位于java.util.concurrent包下,CyclicBarrier提供2个构造器:
public CyclicBarrier(int parties, Runnable barrierAction) {}public CyclicBarrier(int parties) {}
参数parties指让多少个线程或者任务等待至barrier状态;参数barrierAction为当这些线程都达到barrier状态时会执行的内容。
然后CyclicBarrier中最重要的方法就是await方法,它有2个重载版本:public int await() throws InterruptedException, BrokenBarrierException { };public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException { };
public static void main(String[] args) { int N = 3; CyclicBarrier barrier = new CyclicBarrier(N); for(int i=0;i
③Semaphore用法
Semaphore翻译成字面意思为 信号量,Semaphore可以控制同时访问的线程个数,通过 acquire() 获取一个许可, 如果没有就等待,而 release() 释放一个许可。Semaphore类位于java.util.concurrent包下,它提供了2个构造器:
public Semaphore(int permits) { //参数permits表示许可数目,即同时可以允许多少线程进行访问 sync = new NonfairSync(permits);}public Semaphore(int permits, boolean fair) { //这个多了一个参数fair表示是否是公平的,即等待时间越久的越先获取许可 sync = (fair)? new FairSync(permits) : new NonfairSync(permits);}
下面说一下Semaphore类中比较重要的几个方法,首先是acquire()、release()方法:
public void acquire() throws InterruptedException { } //获取一个许可public void acquire(int permits) throws InterruptedException { } //获取permits个许可public void release() { } //释放一个许可public void release(int permits) { } //释放permits个许可
acquire()用来获取一个许可,若无许可能够获得,则会一直等待,直到获得许可。
release()用来释放许可。注意,在释放许可之前,必须先获得许可。 这4个方法都会被阻塞,如果想立即得到执行结果,可以使用下面几个方法: public boolean tryAcquire() { }; //尝试获取一个许可,若获取成功,则立即返回true,若获取失败,则立即返回false public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException { }; //尝试获取一个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false public boolean tryAcquire(int permits) { }; //尝试获取permits个许可,若获取成功,则立即返回true,若获取失败,则立即返回false public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException { }; //尝试获取permits个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false 另外还可以通过availablePermits()方法得到可用的许可数目。public static void main(String[] args) { int N = 8; //工人数 Semaphore semaphore = new Semaphore(5); //机器数目 for(int i=0;i
12.其他 ①Future
在Future接口中声明了5个方法,下面依次解释每个方法的作用:public interface Future{ boolean cancel(boolean mayInterruptIfRunning); boolean isCancelled(); boolean isDone(); V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;}
cancel方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。
isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。 isDone方法表示任务是否已经完成,若任务完成,则返回true; get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回; get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。②FutureTask FutureTask实现了Runnable和Future两个接口。所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。public class FutureTaskimplements RunnableFuture public interface RunnableFuture extends Runnable, Future { void run();}
③DelayQueue
DelayQueue是一个无界阻塞队列。其存储延时的元素,只有延时耗尽元素才能被取出。 队列头元素就是最先耗尽延时的元素,如果没有元素耗尽延时,poll操作会返回null。 同样的,该队列不允许空元素。针对延时的特性,可以用户定时任务,到达时间就能取出任务执行,设计有时效的缓存,超时就清除。④PriorityBlockingQueue PriorityBlockingQueue是基于优先级的无界阻塞队列。 在这个数据结构,元素是按照顺序储存的。元素们必须实现 带有 compareTo() 方法的 Comparable 接口。⑤ScheduledExecutorService 执行的线程池服务public static void main(String[] args) { ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); //每隔100毫秒执行一次。 executorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { System.out.println("run "+ System.currentTimeMillis()); } }, 0, 100, TimeUnit.MILLISECONDS);}
⑥Exchanger
Exchanger可以让两个线程交换数据