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

 

Java多线程简析——Synchronized(同步锁)、Lock以及线程池的更多相关文章

  1. Java多线程简析

    一.线程的状态: 线程共有下面4种状态: 1.新建状态(New): 新创建了一个线程对象,当你用new创建一个线程时,该线程尚未运行. 2.就绪状态(Runnable): 线程对象创建后,其他线程调用 ...

  2. java多线程(三)-Executors实现的几种线程池以及Callable

    从java5开始,类库中引入了很多新的管理调度线程的API,最常用的就是Executor(执行器)框架.Executor帮助程序员管理Thread对象,简化了并发编程,它其实就是在 提供了一个中间层, ...

  3. (CSDN 迁移) JAVA多线程实现-支持定时与周期性任务的线程池(newScheduledThreadPool)

    前几篇文章中分别介绍了 单线程化线程池(newSingleThreadExecutor) 可控最大并发数线程池(newFixedThreadPool) 可回收缓存线程池(newCachedThread ...

  4. Java多线程高并发学习笔记(三)——深入理解线程池

    线程池最核心的一个类:ThreadPoolExecutor. 看一下该类的构造器: public ThreadPoolExecutor(int paramInt1, int paramInt2, lo ...

  5. Java中String做为synchronized同步锁使用详解

    Java中使用String作同步锁 在Java中String是一种特殊的类型存在,在jdk中String在创建后是共享常量池的,即使在jdk1.8之后实现有所不同,但是功能还是差不多的. 借助这个特点 ...

  6. Java多线程6:Synchronized锁代码块(this和任意对象)

    一.Synchronized(this)锁代码块 用关键字synchronized修饰方法在有些情况下是有弊端的,若是执行该方法所需的时间比较长,线程1执行该方法的时候,线程2就必须等待.这种情况下就 ...

  7. Java多线程5:Synchronized锁机制

    一.前言 在多线程中,有时会出现多个线程对同一个对象的变量进行并发访问的情形,如果不做正确的同步处理,那么产生的后果就是“脏读”,也就是获取到的数据其实是被修改过的. 二.引入Synchronized ...

  8. python笔记9 线程进程 threading多线程模块 GIL锁 multiprocessing多进程模块 同步锁Lock 队列queue IO模型

    线程与进程 进程 进程就是一个程序在一个数据集上的一次动态执行过程.进程一般由程序.数据集.进程控制块三部分组成.我们编写的程序用来描述进程要完成哪些功能以及如何完成:数据集则是程序在执行过程中所需要 ...

  9. java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析

    java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java ...

随机推荐

  1. Integer IntegerCache源码

    先看一段测试结果: /*public static void main(String[] args) { Integer a = 128, b = 128; Integer c = 127, d = ...

  2. Debian+Pure-ftpd+MySQL+User manager for PureFTPd

    1. 安装pure-ftpd.MySQL apt-get purge vsftpd apt-get purge pure-ftpd apt-get purge pure-ftpd-common apt ...

  3. ZooKeeper安装和配置(转)

    原文链接:http://coolxing.iteye.com/blog/1871009 Zookeeper的安装和配置十分简单, 既可以配置成单机模式, 也可以配置成集群模式. 下面将分别进行介绍. ...

  4. 初见-TensorRT简介<转>

    下面是TensorRT的介绍,也可以参考官方文档,更权威一些:https://developer.nvidia.com/tensorrt 关于TensorRT首先要清楚以下几点: 1. TensorR ...

  5. JS性能优化之创建文档碎片(document.createDocumentFragment)

    讲这个方法之前,我们应该先了解下插入节点时浏览器会做什么.         在浏览器中,我们一旦把节点添加到document.body(或者其他节点)中,页面就会更新并反映出这个变化,对于少量的更新, ...

  6. 关于vs2013调试的偶然错误发现与总结(vs2013的承载进程)---ShinePans

    当项目的属性选择为 启用 vs2013承载进程 或出现一下错误: 尝试运行项目时出错:未能加载文件或程序集"GroupBoxTest" 或它的某一个依赖项.给定程序集名称" ...

  7. 正向代理/反向代理理解、Nginx概述、安装及配置详解

    一.Nginx概述 nginx是一款自由的.开源的.高性能的HTTP服务器和反向代理服务器:同时也是一个IMAP.POP3.SMTP代理服务器:nginx可以作为一个HTTP服务器进行网站的发布处理, ...

  8. IOS UITableView删除功能

    UITbableView作为列表展示信息,除了展示的功能,有时还会用到删除,比如购物车等.删除功能可以直接使用系统自带的删除功能,当横向轻扫cell时,右侧出现红色的删除按钮,点击删除当前cell. ...

  9. 即将来到: CSS Feature Queries (CSS特性查询)

    Feature Queries 是CSS3 Conditional Rules specification中的一部分,它支持“@supports”规则,“@supports”规则可以用来测试浏览器是否 ...

  10. HTML5 Canvas(画布)实战编程初级篇:基本介绍和基础画布元素

    欢迎大家阅读HTML5 Canvas(画布)实战编程初级篇系列,在这个系列中,我们将介绍最简单的HTML5画布编程.包括: 画布元素 绘制直线 绘制曲线 绘制路径 绘制图形 绘制颜色,渐变和图案 绘制 ...