Java学习系列(十五)Java面向对象之细谈线程、线程通信(下)

来源:互联网 发布:单片机复位原理 编辑:程序博客网 时间:2024/05/29 11:24

竞争资源(共享资源):如果有多条线程需要并发访问、并修改某个对象,该对象就是“竞争资源”。为了避免多个线程"自由竞争”修改共享资源所导致的不安全问题。

线程同步(像Vector、Hashtable等都是线程安全的):
解决线程异步有两种方式:

1)同步代码块(需要显式的指定同步监视锁);
2).同步方法(相当于使用方法的调用者,如果方法是实例方法,相当于this为同步监视锁;如果方法是类方法,相当于类作为同步监视锁)。
它们的实现机制是完全相同的,当线程要进入被"同步监视锁"监视的代码之前,线程必须先获得“同步监视锁”,这样就可以保证在任意一个时刻,只有一条线程能进入被"同步监视锁"监视的代码。从程序逻辑来看,选择“竞争资源”作为同步监视锁。
举例说明1--同步代码块(由于代码都是些属性,所以这里只抽取了片段,掌握思想即可):

 

Java代码  收藏代码
  1. // 同步代码块,synchronized后的括号中的对象被称为同步锁  
  2.         synchronized (account) {  
  3.             if (account.getBalance() > drawAmount) {  
  4.                 System.out.println("取钱成功,吐出钱数:" + drawAmount);  
  5.                 account.setBalance(account.getBalance() - drawAmount);  
  6.                 System.out.println("还剩余额为:" + account.getBalance());  
  7.             } else {  
  8.                 System.out.println("取钱失败,余额不足!");  
  9.             }  
  10.         }  


举例说明2:--同步方法,以this作为同步监视锁。

Java代码  收藏代码
  1. public synchronized void draw(double drawAmount){  
  2.         if (getBalance() > drawAmount) {  
  3.             System.out.println("取钱成功,吐出钱数:" + drawAmount);  
  4.             setBalance(getBalance() - drawAmount);  
  5.             System.out.println("还剩余额为:" + getBalance());  
  6.         } else {  
  7.             System.out.println("取钱失败,余额不足!");  
  8.         }  
  9.     }  

 

线程同步的关键:任何同步监视器监视的代码之前,必须先对同步监视器加锁。

何时释放对【同步监视器】的加锁?
1.同步代码块或同步方法执行完成。
2.在代码中遇到了break、return语句跳出该代码块。
3.执行同步代码块或同步方法时遇到未捕获的异常时。
4.调用了同步监视器的wait()方法。

【注意】使用sleep()、yield()都不会释放。

线程通信:
1.如果不加控制,多个线程“自由”地并发执行。
2.可以通过同步,来解决多个线程并发访问竞争资源的问题。线程安全,必然降低性能。
3.如果希望线程之间能更有序地执行。

线程组ThreadGroup与未处理的异常:
怎样把线程放入指定的线程组中呢? --在创建一个Thread实例时,通过传入的ThreadGroup对象,即可将该线程放入指定的线程组中,进而通过线程组对这批线程进行整体的管理。
ThreadGroup提供了如下两个方法:setDaemon(boolean daemon) 控制将线程组本身都设置为后台线程组,并不是将它所包含的线程设为后台线程。setMaxPriority(优先级):它是设置该线程组已有的线程的优先级不会受影响,对以后新添加的线程的优先级才会有影响。

对线程异常进行处理:在JDK1.5之前,系统会自动回调它所在线程组的uncaughtException(Thread t,Throwable e)方法来修复该异常;在JDK1.5之后,线程允许自行设置“异常处理器”,无需线程组。public static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh):为所有线程设置默认的异常处理器。public void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh):为当前线程实例设置异常处理器。

举列说明1(jdk1.5之前):

 

Java代码  收藏代码
  1. public class ThreadExceptionTest implements Runnable {  
  2.     @Override  
  3.     public void run() {  
  4.         for (int i = 0; i < 100; i++) {  
  5.             System.out.println(Thread.currentThread().getName() + "--->" + i  
  6.                     / (i - 20));  
  7.         }  
  8.     }  
  9.   
  10.     public static void main(String[] args) {  
  11.         // 创建一个线程组  
  12.         ThreadGroup tg = new ThreadGroup("mytg") {  
  13.             @Override  
  14.             public void uncaughtException(Thread t, Throwable e) {  
  15.                 System.out.println(t.getName() + "出现了异常,信息是:" + e.getMessage());  
  16.             }  
  17.         };  
  18.         ThreadExceptionTest test = new ThreadExceptionTest();  
  19.         new Thread(tg, test).start();  
  20.     }  
  21.   
  22. }  

举列说明2(jdk1.5之后):

 

Java代码  收藏代码
  1. public class ThreadExceptionTest implements Runnable {  
  2.     @Override  
  3.     public void run() {  
  4.         for (int i = 0; i < 100; i++) {  
  5.             System.out.println(Thread.currentThread().getName() + "--->" + i  
  6.                     / (i - 20));  
  7.         }  
  8.     }  
  9.   
  10.     public static void main(String[] args) {  
  11.         // 为所有线程设置默认的异常处理器  
  12.         Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {  
  13.             @Override  
  14.             public void uncaughtException(Thread t, Throwable e) {  
  15.                 System.out.println(t.getName() + "出现了异常,信息是:" + e.getMessage());  
  16.             }  
  17.         });  
  18.         ThreadExceptionTest test = new ThreadExceptionTest();  
  19.         new Thread(test).start();  
  20.     }  
  21. }  

线程池(Pool):池的本质,就是一种“缓存”技术。是否要缓存一个对象,要看该对象的创建成本。
Executors --创建线程池,线程工厂的工具类。ExecutorService:它就是线程池。
缓存的本质:牺牲空间(内存)来换取时间。线程对象的创建成本比较大(尽管比创建进程的成本小的多,但相对普通java对象,Thread的创建成本依然较大),为解决这个问题,我们用线程池。

编程步骤:
1.通过Executors的静态工厂方法创建ExecutorService或ScheduledExecutorService
2.调用ExecutorService的方法提交线程即可。
3.调用线程池的.shutdown方法关闭线程池。

举例说明1:

Java代码  收藏代码
  1. public class ThreadPoolTest implements Runnable {  
  2.     @Override  
  3.     public void run() {  
  4.         for (int i = 0; i < 100; i++) {  
  5.             System.out.println(Thread.currentThread().getName() + ",i=" + i);  
  6.         }  
  7.     }  
  8.   
  9.     public static void main(String[] args) {  
  10.         ExecutorService es = Executors.newFixedThreadPool(10);  
  11.         es.submit(new ThreadPoolTest());  
  12.         es.shutdownNow();// 关闭线程池  
  13.     }  
  14. }  

举例说明2:

 

Java代码  收藏代码
  1. public class ThreadPoolTest implements Runnable {  
  2.     @Override  
  3.     public void run() {  
  4.         for (int i = 0; i < 100; i++) {  
  5.             System.out.println(Thread.currentThread().getName() + ",i=" + i);  
  6.         }  
  7.     }  
  8.   
  9.     public static void main(String[] args) {  
  10.         ScheduledExecutorService es = Executors.newScheduledThreadPool(10);  
  11.         // 延迟5s,以后每隔2s执行一次run方法  
  12.         es.scheduleAtFixedRate(new ThreadPoolTest(), 52, TimeUnit.SECONDS);  
  13.     }  
  14. }  

0 0
原创粉丝点击