zoukankan      html  css  js  c++  java
  • Java多线程简析——Synchronized(同步锁)、Lock以及线程池

    Java多线程

    Java中,可运行的程序都是有一个或多个进程组成。进程则是由多个线程组成的。
    最简单的一个进程,会包括mian线程以及GC线程。

    线程的状态

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

    在图中,红框标识的部分方法,可以认为已过时,不再使用。
    (1)wait、notify、notifyAll是线程中通信可以使用的方法。线程中调用了wait方法,则进入阻塞状态,只有等另一个线程调用与wait同一个对象的notify方法。
    这里有个特殊的地方,调用wait或者notify,前提是需要获取锁,也就是说,需要在同步块中做以上操作。
    (2)join方法。该方法主要作用是在该线程中的run方法结束后,才往下执行。如以下代码:

    public class ThreadJoin {  
    
        public static void main(String[] args) {  
      
            Thread thread= new Thread(new Runnable() {  
                @Override  
                public void run() {  
                    System.err.println("线程"+Thread.currentThread().getId()+" 打印信息");  
                }  
            });  
            thread.start();  
              
            try {  
                thread.join();  
            } catch (InterruptedException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }
            
            System.err.println("主线程打印信息");  
            
        }
    }

    该方法显示的信息是:
    线程8 打印信息
    主线程打印信息

    如果去掉其中的join方法,则显示如下:
    主线程打印信息
    线程8 打印信息
    (3)yield方法。这个是线程本身的调度方法,使用时你可以在run方法执行完毕时,调用该方法,告知你已可以出让内存资源。
    其他的线程方法,基本都会在日常中用到,如start、run、sleep,这里就不再介绍。

    Synchronized(同步锁)

    在Java中使用多线程,你就不能绕过同步锁这个概念。这在多线程中是十分重要的。
    在Java多线程的使用中,你必然会遇到一个问题:多个线程共享一个或者一组资源,这资源包括内存、文件等。
    很常见的一个例子是,张三在银行账户存有9999元,经过多次的取100,存100后,账户还有多少钱?
    看代码:
    以下表示账户信息:

    import java.sql.Time;  
    import java.util.concurrent.TimeUnit;  
      
    public class Account {  
      
        private String name;  
        private float amt;  
        public Account(String name,float amt) {  
            this.name=name;  
            this.amt=amt;  
        }  
      
        public  void  increaseAmt(float increaseAmt){  
            try {  
                TimeUnit.SECONDS.sleep(1);  
            } catch (InterruptedException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
            amt+=increaseAmt;  
        }  
          
        public  void decreaseAmt(float decreaseAmt){  
            try {  
                TimeUnit.SECONDS.sleep(1);  
            } catch (InterruptedException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
            amt-=decreaseAmt;  
        }  
          
        public void printMsg(){  
            System.out.println(name+"账户现有金额为:"+amt);  
        }  
    }  

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

    final int NUM=100;  
    
    Thread[] threads=new Thread[NUM];  
    for(int i=0;i<NUM;i++){  
        if(threads[i]==null){  
            threads[i]=new Thread(new Runnable() {  
                  
                @Override  
                public void run() {  
                    account.increaseAmt(100f);  
                    account.decreaseAmt(100f);  
                }  
            });  
            threads[i].start();  
        }  
    }  
      
    for(int i=0;i<NUM;i++){  
        try {  
            threads[i].join();  
        } catch (InterruptedException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
    }  
      
    account.printMsg();  

    你会发现,每次打印出来的账户余额都不一定是一样的。这就是同步锁的必要性。
    java中,提供了多种使用同步锁的方式。

    (1)对动态方法的修饰。
    作用的是调用该方法的对象(或者说对象引用)。

    public synchronized void doSomething(){}  

    (2)对代码块的修饰。
    作用的是调用该方法的对象(或者说对象引用)。

    public void increaseAmt(float increaseAmt){  
              
        try {  
            TimeUnit.SECONDS.sleep(1);  
        } catch (InterruptedException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
        synchronized (this) {  
            System.out.println(this);  
            amt+=increaseAmt;  
        } 
        
    }

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

    public synchronized static void increaseAmt(float increaseAmt){  
        try {  
            TimeUnit.SECONDS.sleep(1);  
        } catch (InterruptedException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
        amt+=increaseAmt;  
    }

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

    synchronized (AccountSynchronizedClass.class) {  
        amt-=decreaseAmt;  
    }

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

    import java.util.concurrent.TimeUnit;  
    /**  
     * Synchronized 代码块  
     * @author 战国  
     *  
     */  
    public class AccountSynchronizedBlock {  
      
        private String name;  
        private float amt;  
        public AccountSynchronizedBlock(String name,float amt) {  
            this.name=name;  
            this.amt=amt;  
        }  
      
        public  void  increaseAmt(float increaseAmt){  
              
            try {  
                TimeUnit.SECONDS.sleep(1);  
            } catch (InterruptedException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
            synchronized (this) {  
                System.out.println(this);  
                amt+=increaseAmt;  
            }  
        }  
          
        public  void decreaseAmt(float decreaseAmt){  
            try {  
                TimeUnit.SECONDS.sleep(1);  
            } catch (InterruptedException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
            synchronized (this) {  
                System.out.println(this);  
                amt-=decreaseAmt;  
            }  
              
        }  
          
        public void printMsg(){  
            System.out.println(name+"账户现有金额为:"+amt);  
        }  
    }  
    //多线程synchronized修饰代码块 ,每次计算的值都一样  
    final AccountSynchronizedBlock account=new AccountSynchronizedBlock("张三", 9999.0f);  
    final int NUM=50;  
    
    Thread[] threads=new Thread[NUM];  
    for(int i=0;i<NUM;i++){  
        if(threads[i]==null){  
            threads[i]=new Thread(new Runnable() {  
                  
                @Override  
                public void run() {  
                    account.increaseAmt(100f);  
                    account.decreaseAmt(100f);  
                }  
            });  
            threads[i].start();  
        }  
    }  
    
    for(int i=0;i<NUM;i++){  
        try {  
            threads[i].join();  
        } catch (InterruptedException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
    }  
    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的简单操作代码如下:

    import java.util.ArrayList;  
    import java.util.List;  
    import java.util.concurrent.locks.Lock;  
    import java.util.concurrent.locks.ReadWriteLock;  
    import java.util.concurrent.locks.ReentrantLock;  
    import java.util.concurrent.locks.ReentrantReadWriteLock;  
      
    public class LockImp {  
      
          
        private Lock lock=new ReentrantLock();  
        private ReadWriteLock rwLock=new ReentrantReadWriteLock();  
          
        private List<Integer> list=new ArrayList<Integer>();  
          
        public void doReentrantLock(Thread thread){  
            lock.lock();  
            System.out.println(thread.getName()+"获取锁");  
            try {  
                  for(int i=0;i<10;i++){  
                        list.add(i);  
                    }  
            } catch (Exception e) {  
                  
            }finally{  
                lock.unlock();  
                System.out.println(thread.getName()+"释放锁");  
            }  
              
        }  
        public void doReentrantReadLock(Thread thread){  
            rwLock.readLock().lock();  
            System.out.println(thread.getName()+"获取读锁");  
            try {  
                for(int i=0;i<10;i++){  
                    list.add(i);  
                }  
            } catch (Exception e) {  
                  
            }finally{  
                rwLock.readLock().unlock();  
                System.out.println(thread.getName()+"释放读锁");  
            }  
              
        }  
        public void doReentrantWriteLock(Thread thread){  
            rwLock.writeLock().lock();  
            System.out.println(thread.getName()+"获取写锁");  
            try {  
                for(int i=0;i<10;i++){  
                    list.add(i);  
                }  
            } catch (Exception e) {  
                  
            }finally{  
                rwLock.writeLock().unlock();  
                System.out.println(thread.getName()+"释放写锁");  
            }  
              
        }  
          
          
          
        /**  
         * @param args  
         */  
        public static void main(String[] args) {  
      
            final LockImp lockImp=new LockImp();  
              
            final Thread thread1=new Thread();  
            final Thread thread2=new Thread();  
            final Thread thread3=new Thread();  
              
            new Thread(new Runnable() {  
                  
                @Override  
                public void run() {  
                    lockImp.doReentrantLock(thread1);  
                }  
            }).start();  
              
            new Thread(new Runnable() {  
                          
                        @Override  
                        public void run() {  
                            lockImp.doReentrantLock(thread2);  
                        }  
                    }).start();  
              
            new Thread(new Runnable() {  
                  
                @Override  
                public void run() {  
                    lockImp.doReentrantLock(thread3);  
                }  
            }).start();  
          
              
            lockImp.doReentrantReadLock(thread1);  
            lockImp.doReentrantReadLock(thread2);  
            lockImp.doReentrantReadLock(thread3);  
              
            lockImp.doReentrantWriteLock(thread1);  
            lockImp.doReentrantWriteLock(thread2);  
            lockImp.doReentrantWriteLock(thread3);  
        }  
      
    }  

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

    线程池的使用

    为什么使用线程池?
    因为使用它有好处:(1)在界面上,简化了写法,代码更简洁(2)对程序中的线程可以进行适度的管理(3)有效较低了多个线程的内存占有率等。
    这是一篇讲述线程池非常好的文章:http://www.cnblogs.com/dolphin0520/p/3932921.html
    如果对线程池有不了解的同学,可以参考链接中的文章,讲的深入浅出。
    在这里只是简单的封装一个线程池的工具类,仅供参考:

    import java.util.concurrent.ExecutorService;  
    import java.util.concurrent.Executors;  
      
    public class ThreadPoolUtil {  
      
         private volatile static ThreadPoolUtil instance;  
         private ThreadPoolUtil(){}  
         private static ExecutorService threadPool;  
           
           
         public static ThreadPoolUtil getInstance(){  
             if(instance==null){  
                 synchronized (ThreadPoolUtil.class) {  
                      instance=new ThreadPoolUtil();  
                     threadPool=Executors.newCachedThreadPool();  
                }  
             }  
             return instance;  
         }  
           
        public void excute(Runnable runnable){  
            threadPool.execute(runnable);  
        }  
          
        public void shutdown(){  
            threadPool.shutdown();  
        }  
          
        public boolean isActive(){  
            if(threadPool.isTerminated()){  
                return false;  
            }  
            return true;  
        }  
    }  

    转载至 http://blog.csdn.net/yangzhaomuma/article/details/51236976

     

  • 相关阅读:
    some tips
    ORA00847: MEMORY_TARGET/MEMORY_MAX_TARGET and LOCK_SGA cannot be set together
    Chapter 01Overview of Oracle 9i Database Perfomrmance Tuning
    Chapter 02Diagnostic and Tuning Tools
    变量与常用符号
    Chapter 18Tuning the Operating System
    标准输入输出
    Trace files
    DBADeveloped Tools
    Chapter 03Database Configuration and IO Issues
  • 原文地址:https://www.cnblogs.com/rinack/p/7832119.html
Copyright © 2011-2022 走看看