Java多线程简析——Synchronized(同步锁)、Lock以及线程池
Java多线程
Java中,可运行的程序都是有一个或多个进程组成。进程则是由多个线程组成的。
最简单的一个进程,会包括mian线程以及GC线程。
线程的状态
线程状态由以下一张网上图片来说明:

在图中,红框标识的部分方法,可以认为已过时,不再使用。
(1)wait、notify、notifyAll是线程中通信可以使用的方法。线程中调用了wait方法,则进入阻塞状态,只有等另一个线程调用与wait同一个对象的notify方法。这里有个特殊的地方,调用wait或者notify,前提是需要获取锁,也就是说,需要在同步块中做以上操作。
(2)join方法。该方法主要作用是在该线程中的run方法结束后,才往下执行。如以下代码:
-
package com.thread.simple; -
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后,账户还有多少钱?
看代码:
以下表示账户信息:
-
package com.thread.simple; -
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); -
} -
}
以下是我们操作账户的方法:
-
<span style="white-space:pre"> </span>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; -
}
以修饰代码块的方式为例,我们重新运行以上代码后,得到了正确的结果。代码如下:
-
package com.thread.simple; -
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的简单操作代码如下:
-
package com.thread.simple; -
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
如果对线程池有不了解的同学,可以参考链接中的文章,讲的深入浅出。
在这里只是简单的封装一个线程池的工具类,仅供参考:
-
package com.thread.simple; -
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; -
} -
}
2万+



被折叠的 条评论
为什么被折叠?



