`
shuai1234
  • 浏览: 972002 次
  • 性别: Icon_minigender_1
  • 来自: 山西
社区版块
存档分类
最新评论

Java多线程简析——Synchronized(同步锁)、Lock以及线程池

    博客分类:
  • java
 
阅读更多

Java多线程

Java中,可运行的程序都是有一个或多个进程组成。进程则是由多个线程组成的。

最简单的一个进程,会包括mian线程以及GC线程。

线程的状态

线程状态由以下一张网上图片来说明:

在图中,红框标识的部分方法,可以认为已过时,不再使用。

(1)wait、notify、notifyAll是线程中通信可以使用的方法。线程中调用了wait方法,则进入阻塞状态,只有等另一个线程调用与wait同一个对象的notify方法。这里有个特殊的地方,调用wait或者notify,前提是需要获取锁,也就是说,需要在同步块中做以上操作。

(2)join方法。该方法主要作用是在该线程中的run方法结束后,才往下执行。如以下代码:

 

[html] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. package com.thread.simple;  
  2.   
  3. public class ThreadJoin {  
  4.   
  5.       
  6.     public static void main(String[] args) {  
  7.   
  8.         Thread threadnew Thread(new Runnable() {  
  9.               
  10.             @Override  
  11.             public void run() {  
  12.                 System.err.println("线程"+Thread.currentThread().getId()+" 打印信息");  
  13.             }  
  14.         });  
  15.         thread.start();  
  16.           
  17.         try {  
  18.             thread.join();  
  19.         } catch (InterruptedException e) {  
  20.             // TODO Auto-generated catch block  
  21.             e.printStackTrace();  
  22.         }  
  23.           
  24.         System.err.println("主线程打印信息");  
  25.           
  26.     }  
  27.   
  28. }  

该方法显示的信息是:

 

线程8 打印信息

主线程打印信息

如果去掉其中的join方法,则显示如下:

主线程打印信息
线程8 打印信息

(3)yield方法。这个是线程本身的调度方法,使用时你可以在run方法执行完毕时,调用该方法,告知你已可以出让内存资源。

其他的线程方法,基本都会在日常中用到,如start、run、sleep,这里就不再介绍。

Synchronized(同步锁)

在Java中使用多线程,你就不能绕过同步锁这个概念。这在多线程中是十分重要的。

在Java多线程的使用中,你必然会遇到一个问题:多个线程共享一个或者一组资源,这资源包括内存、文件等。

很常见的一个例子是,张三在银行账户存有9999元,经过多次的取100,存100后,账户还有多少钱?

看代码:

以下表示账户信息:

 

[html] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. package com.thread.simple;  
  2.   
  3. import java.sql.Time;  
  4. import java.util.concurrent.TimeUnit;  
  5.   
  6. public class Account {  
  7.   
  8.     private String name;  
  9.     private float amt;  
  10.     public Account(String name,float amt) {  
  11.         this.name=name;  
  12.         this.amt=amt;  
  13.     }  
  14.   
  15.     public  void  increaseAmt(float increaseAmt){  
  16.         try {  
  17.             TimeUnit.SECONDS.sleep(1);  
  18.         } catch (InterruptedException e) {  
  19.             // TODO Auto-generated catch block  
  20.             e.printStackTrace();  
  21.         }  
  22.         amt+=increaseAmt;  
  23.     }  
  24.       
  25.     public  void decreaseAmt(float decreaseAmt){  
  26.         try {  
  27.             TimeUnit.SECONDS.sleep(1);  
  28.         } catch (InterruptedException e) {  
  29.             // TODO Auto-generated catch block  
  30.             e.printStackTrace();  
  31.         }  
  32.         amt-=decreaseAmt;  
  33.     }  
  34.       
  35.     public void printMsg(){  
  36.         System.out.println(name+"账户现有金额为:"+amt);  
  37.     }  
  38. }  


以下是我们操作账户的方法:

 

 

[html] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. <span style="white-space:pre">        </span>final int NUM=100;  
  2.           
  3.         Thread[] threads=new Thread[NUM];  
  4.         for(int i=0;i<NUM;i++){  
  5.             if(threads[i]==null){  
  6.                 threads[i]=new Thread(new Runnable() {  
  7.                       
  8.                     @Override  
  9.                     public void run() {  
  10.                         account.increaseAmt(100f);  
  11.                         account.decreaseAmt(100f);  
  12.                     }  
  13.                 });  
  14.                 threads[i].start();  
  15.             }  
  16.         }  
  17.           
  18.         for(int i=0;i<NUM;i++){  
  19.             try {  
  20.                 threads[i].join();  
  21.             } catch (InterruptedException e) {  
  22.                 // TODO Auto-generated catch block  
  23.                 e.printStackTrace();  
  24.             }  
  25.         }  
  26.           
  27.         account.printMsg();  

你会发现,每次打印出来的账户余额都不一定是一样的。这就是同步锁的必要性。

 

java中,提供了多种使用同步锁的方式。

(1)对动态方法的修饰。

作用的是调用该方法的对象(或者说对象引用)。

 

[html] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public synchronized void doSomething(){}  

 

  ( 2)  对代码块的修饰。

作用的是调用该方法的对象(或者说对象引用)。

 

[html] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public  void  increaseAmt(float increaseAmt){  
  2.           
  3.         try {  
  4.             TimeUnit.SECONDS.sleep(1);  
  5.         } catch (InterruptedException e) {  
  6.             // TODO Auto-generated catch block  
  7.             e.printStackTrace();  
  8.         }  
  9.         synchronized (this) {  
  10.             System.out.println(this);  
  11.             amt+=increaseAmt;  
  12.         }  
  13.     }  

 

(3)对静态方法的修饰。

 

作用的是静态方法所在类的所有对象(或者说对象引用)。

 

[html] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public synchronized static  void  increaseAmt(float increaseAmt){  
  2.         try {  
  3.             TimeUnit.SECONDS.sleep(1);  
  4.         } catch (InterruptedException e) {  
  5.             // TODO Auto-generated catch block  
  6.             e.printStackTrace();  
  7.         }  
  8.         amt+=increaseAmt;  
  9.     }  

 

 

(4)对类的修饰。

 

作用的是静态方法所在类的所有对象(或者说对象引用)。

 

[html] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. synchronized (AccountSynchronizedClass.class) {  
  2.         amt-=decreaseAmt;  
  3.     }  


以修饰代码块的方式为例,我们重新运行以上代码后,得到了正确的结果。代码如下:

 

 

[html] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. package com.thread.simple;  
  2.   
  3. import java.util.concurrent.TimeUnit;  
  4. /**  
  5.  * Synchronized 代码块  
  6.  * @author 战国  
  7.  *  
  8.  */  
  9. public class AccountSynchronizedBlock {  
  10.   
  11.     private String name;  
  12.     private float amt;  
  13.     public AccountSynchronizedBlock(String name,float amt) {  
  14.         this.name=name;  
  15.         this.amt=amt;  
  16.     }  
  17.   
  18.     public  void  increaseAmt(float increaseAmt){  
  19.           
  20.         try {  
  21.             TimeUnit.SECONDS.sleep(1);  
  22.         } catch (InterruptedException e) {  
  23.             // TODO Auto-generated catch block  
  24.             e.printStackTrace();  
  25.         }  
  26.         synchronized (this) {  
  27.             System.out.println(this);  
  28.             amt+=increaseAmt;  
  29.         }  
  30.     }  
  31.       
  32.     public  void decreaseAmt(float decreaseAmt){  
  33.         try {  
  34.             TimeUnit.SECONDS.sleep(1);  
  35.         } catch (InterruptedException e) {  
  36.             // TODO Auto-generated catch block  
  37.             e.printStackTrace();  
  38.         }  
  39.         synchronized (this) {  
  40.             System.out.println(this);  
  41.             amt-=decreaseAmt;  
  42.         }  
  43.           
  44.     }  
  45.       
  46.     public void printMsg(){  
  47.         System.out.println(name+"账户现有金额为:"+amt);  
  48.     }  
  49. }  

 

 

[html] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. //多线程synchronized修饰代码块 ,每次计算的值都一样  
  2.           
  3.         final AccountSynchronizedBlock account=new AccountSynchronizedBlock("张三", 9999.0f);  
  4.         final int NUM=50;  
  5.           
  6.         Thread[] threads=new Thread[NUM];  
  7.         for(int i=0;i<NUM;i++){  
  8.             if(threads[i]==null){  
  9.                 threads[i]=new Thread(new Runnable() {  
  10.                       
  11.                     @Override  
  12.                     public void run() {  
  13.                         account.increaseAmt(100f);  
  14.                         account.decreaseAmt(100f);  
  15.                     }  
  16.                 });  
  17.                 threads[i].start();  
  18.             }  
  19.         }  
  20.           
  21.         for(int i=0;i<NUM;i++){  
  22.             try {  
  23.                 threads[i].join();  
  24.             } catch (InterruptedException e) {  
  25.                 // TODO Auto-generated catch block  
  26.                 e.printStackTrace();  
  27.             }  
  28.         }  
  29.         account.printMsg();  


以上是同步锁的简单说明。

在JDK5中,Java又引入了一个相似的概念Lock,也就是锁。功能与synchronized是类似的。

Lock

Lock对比synchronized有高手总结的差异如下:

 

总结来说,Lock和synchronized有以下几点不同:

  1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;

  2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;

  3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;

  4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。

  5)Lock可以提高多个线程进行读操作的效率。

  在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。

(参考http://www.cnblogs.com/dolphin0520/p/3923167.html)。

Lock的操作与synchronized相比,灵活性更高,而且Lock提供多种方式获取锁,有Lock、ReadWriteLock接口,以及实现这两个接口的ReentrantLock类、ReentrantReadWriteLock类。

对Lock的简单操作代码如下:

 

[html] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. package com.thread.simple;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5. import java.util.concurrent.locks.Lock;  
  6. import java.util.concurrent.locks.ReadWriteLock;  
  7. import java.util.concurrent.locks.ReentrantLock;  
  8. import java.util.concurrent.locks.ReentrantReadWriteLock;  
  9.   
  10. public class LockImp {  
  11.   
  12.       
  13.     private Lock lock=new ReentrantLock();  
  14.     private ReadWriteLock rwLock=new ReentrantReadWriteLock();  
  15.       
  16.     private List<Integer> list=new ArrayList<Integer>();  
  17.       
  18.     public void doReentrantLock(Thread thread){  
  19.         lock.lock();  
  20.         System.out.println(thread.getName()+"获取锁");  
  21.         try {  
  22.               for(int i=0;i<10;i++){  
  23.                     list.add(i);  
  24.                 }  
  25.         } catch (Exception e) {  
  26.               
  27.         }finally{  
  28.             lock.unlock();  
  29.             System.out.println(thread.getName()+"释放锁");  
  30.         }  
  31.           
  32.     }  
  33.     public void doReentrantReadLock(Thread thread){  
  34.         rwLock.readLock().lock();  
  35.         System.out.println(thread.getName()+"获取读锁");  
  36.         try {  
  37.             for(int i=0;i<10;i++){  
  38.                 list.add(i);  
  39.             }  
  40.         } catch (Exception e) {  
  41.               
  42.         }finally{  
  43.             rwLock.readLock().unlock();  
  44.             System.out.println(thread.getName()+"释放读锁");  
  45.         }  
  46.           
  47.     }  
  48.     public void doReentrantWriteLock(Thread thread){  
  49.         rwLock.writeLock().lock();  
  50.         System.out.println(thread.getName()+"获取写锁");  
  51.         try {  
  52.             for(int i=0;i<10;i++){  
  53.                 list.add(i);  
  54.             }  
  55.         } catch (Exception e) {  
  56.               
  57.         }finally{  
  58.             rwLock.writeLock().unlock();  
  59.             System.out.println(thread.getName()+"释放写锁");  
  60.         }  
  61.           
  62.     }  
  63.       
  64.       
  65.       
  66.     /**  
  67.      * @param args  
  68.      */  
  69.     public static void main(String[] args) {  
  70.   
  71.         final LockImp lockImp=new LockImp();  
  72.           
  73.         final Thread thread1=new Thread();  
  74.         final Thread thread2=new Thread();  
  75.         final Thread thread3=new Thread();  
  76.           
  77.         new Thread(new Runnable() {  
  78.               
  79.             @Override  
  80.             public void run() {  
  81.                 lockImp.doReentrantLock(thread1);  
  82.             }  
  83.         }).start();  
  84.           
  85.         new Thread(new Runnable() {  
  86.                       
  87.                     @Override  
  88.                     public void run() {  
  89.                         lockImp.doReentrantLock(thread2);  
  90.                     }  
  91.                 }).start();  
  92.           
  93.         new Thread(new Runnable() {  
  94.               
  95.             @Override  
  96.             public void run() {  
  97.                 lockImp.doReentrantLock(thread3);  
  98.             }  
  99.         }).start();  
  100.       
  101.           
  102.         lockImp.doReentrantReadLock(thread1);  
  103.         lockImp.doReentrantReadLock(thread2);  
  104.         lockImp.doReentrantReadLock(thread3);  
  105.           
  106.         lockImp.doReentrantWriteLock(thread1);  
  107.         lockImp.doReentrantWriteLock(thread2);  
  108.         lockImp.doReentrantWriteLock(thread3);  
  109.     }  
  110.   
  111. }  


Lock的使用中,务必需要lock、unlock同时使用,避免死锁。

 

 

线程池的使用

为什么使用线程池?

 

因为使用它有好处:(1)在界面上,简化了写法,代码更简洁(2)对程序中的线程可以进行适度的管理(3)有效较低了多个线程的内存占有率等。

这是一篇讲述线程池非常好的文章:http://www.cnblogs.com/dolphin0520/p/3932921.html

如果对线程池有不了解的同学,可以参考链接中的文章,讲的深入浅出。

在这里只是简单的封装一个线程池的工具类,仅供参考:

 

[html] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. package com.thread.simple;  
  2.   
  3. import java.util.concurrent.ExecutorService;  
  4. import java.util.concurrent.Executors;  
  5.   
  6. public class ThreadPoolUtil {  
  7.   
  8.      private volatile static ThreadPoolUtil instance;  
  9.      private ThreadPoolUtil(){}  
  10.      private static ExecutorService threadPool;  
  11.        
  12.        
  13.      public static ThreadPoolUtil getInstance(){  
  14.          if(instance==null){  
  15.              synchronized (ThreadPoolUtil.class) {  
  16.                   instance=new ThreadPoolUtil();  
  17.                  threadPool=Executors.newCachedThreadPool();  
  18.             }  
  19.          }  
  20.          return instance;  
  21.      }  
  22.        
  23.     public void excute(Runnable runnable){  
  24.         threadPool.execute(runnable);  
  25.     }  
  26.       
  27.     public void shutdown(){  
  28.         threadPool.shutdown();  
  29.     }  
  30.       
  31.     public boolean isActive(){  
  32.         if(threadPool.isTerminated()){  
  33.             return false;  
  34.         }  
  35.         return true;  
  36.     }  
  37. }  
分享到:
评论

相关推荐

    java多线程案例——未完成

    在这个未完成的案例中,我们可能正在探讨如何在Java中创建和管理线程,以及处理多线程环境下的并发问题。下面是对Java多线程基础知识的详细解释: 1. **线程的创建方式**: - 继承`Thread`类:自定义一个新的类,...

    java多线程教程——一个课件彻底搞清多线程

    本教程将深入讲解Java线程的相关知识,包括进程与线程的基本概念、线程的创建和启动、多线程的互斥与同步、线程状态和线程控制以及死锁的概念。 首先,我们要理解进程与线程的区别。进程是操作系统资源分配的基本...

    java 多线程synchronized互斥锁demo

    标题中的"java 多线程synchronized互斥锁demo"指的是一个示例,展示了如何在多线程环境下使用`synchronized`关键字创建互斥锁,确保同一时间只有一个线程可以访问特定的代码块或方法。 描述中的"一个多线程访问的同...

    基于Java synchronized同步锁实现线程交互.pdf

    Java synchronized同步锁可以解决多线程带来的问题,保证同一时刻只有一个线程操作同一资源。使用wait()和notify()方法可以切换线程状态,实现线程交互。因此,在使用Java多线程的场景中,请充分理解Java线程各状态...

    Java多线程——线程八锁案例分析.docx

    除了`synchronized`关键字,Java还提供了其他并发控制工具,如`java.util.concurrent`包下的`Semaphore`、`Lock`接口(包括`ReentrantLock`)、`Condition`等,可以根据具体需求选择合适的方式实现线程间的协调。

    java多线程查询数据库

    本文将详细探讨如何利用Java的多线程技术和线程池来实现并发查询数据库,以及相关的文件`BatchDataUtil.java`和`BatchDataRunnable.java`可能涉及的关键知识点。 ### 1. 多线程并发查询 多线程并发查询允许我们将一...

    Java多线程(Synchronized+Volatile+JUC 并发工具原理+线程状态+CAS+线程池)

    Java 多线程(Synchronized+Volatile+JUC 并发工具原理+线程状态+CAS+线程池) Java 多线程是 Java 语言中的一种并发编程机制,允许程序同时执行多个线程,以提高程序的执行效率和响应速度。 Java 多线程机制提供了...

    java多线程实现大批量数据导入源码

    此外,需要注意的是,多线程环境下可能存在竞态条件、死锁等问题,需要合理使用`synchronized`关键字或`Lock`接口来保证数据的一致性和安全性。另外,适当的错误处理和日志记录也是必不可少的,以确保程序的健壮性。...

    Java多线程知识点总结

    总之,掌握Java多线程的生命周期、创建、启动、同步以及线程池的使用是编写高效、稳定并发程序的基础。理解这些知识点对于解决并发编程中的问题,比如资源竞争、死锁、线程安全性等问题,至关重要。在实际开发中,...

    JAVA多线程编程技术PDF

    总结起来,“JAVA多线程编程技术PDF”涵盖了多线程的基本概念、同步机制、线程通信、死锁避免、线程池以及线程安全的集合类等内容。通过深入学习这份资料,开发者可以全面掌握Java多线程编程技术,提升程序的并发...

    java多线程的讲解和实战

    - 同步机制:为了防止多个线程访问同一资源时产生数据不一致的问题,Java提供了`synchronized`关键字,可以锁定代码块或整个方法,确保同一时刻只有一个线程执行。 - 等待/通知机制:`wait()`, `notify()`, `...

    java多线程,对多线程,线程池进行封装,方便使用

    Java提供了synchronized关键字、Lock接口(如ReentrantLock)以及各种并发容器(如ConcurrentHashMap)来保证线程安全。 10. **线程池的最佳实践** - 核心线程数应根据系统资源和业务需求设定,一般可设为CPU核心...

    java多线程、并发及线程池介绍收藏的几篇文档

    Java多线程、并发以及线程池是Java编程中至关重要的概念,特别是在处理高并发、高性能的系统设计时。以下是对这些主题的详细说明: 1. **Java 程序中的多线程** - 多线程允许一个程序同时执行多个任务,提高程序...

    Java多线程下载网络图片

    Java提供了多种同步机制,如`synchronized`关键字、`Lock`接口以及`ReentrantLock`等,确保在同一时刻只有一个线程能执行特定代码块,防止竞争条件。 2. **线程安全的数据结构**:为了确保线程安全,可以使用Java的...

    java上机报告5——Java的多线程编程1

    Java 多线程编程应用场景 —— 电影院售票系统设计 本资源摘要信息将对 Java 多线程编程在电影院售票系统设计中的应用进行详细介绍。该系统模拟了电影院三个售票窗口同时出售电影票的过程,通过 Java 多线程编程...

    java多线程之并发锁

    Java 多线程之并发锁 Java 中的多线程编程是指在一个程序中同时运行多个线程,以提高程序的执行效率和响应速度。在多线程编程中,线程间的同步是非常重要的,因为不同的线程可能会同时访问同一个共享资源,导致数据...

    Java多线程的小例子——吃包子

    4. **同步机制**:为了防止多个线程对共享资源的不正确访问,Java提供了多种同步机制,如`synchronized`关键字、`Lock`接口以及相关的同步工具类(如`Semaphore`信号量)。在这个例子中,包子的数量可能是共享资源,...

    java多线程经典案例

    Java提供了多种同步机制,如synchronized关键字、Lock接口(ReentrantLock、ReentrantReadWriteLock)以及Semaphore信号量。synchronized用于方法或代码块,可以保证同一时间只有一个线程执行特定代码,避免数据冲突...

Global site tag (gtag.js) - Google Analytics