java多线程的难点是在:处理多个线程同步与并发运行时线程间的通信问题。java在处理线程同步时,常用方法有:

1、synchronized关键字。

2、Lock显示加锁。

3、信号量Semaphore。

 

线程同步问题引入:

      创建一个银行账户Account类,在创建并启动100个线程往同一个Account类实例里面添加一块钱。在没有使用上面三种方法的情况下:

代码:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class AccountWithoutSync {
private static Account account = new Account(); //实例化一个账户 public static void main(String[] args)
{
long start = System.currentTimeMillis();
//使用ExecutorService创建线程池
ExecutorService executor = Executors.newCachedThreadPool(); for(int i=0;i<100;i++)
{
executor.execute(new AddPennyTask());
}
//关闭线程池 即使线程池中还有未完成的线程 返回未完成的清单
executor.shutdown(); //关闭之后还是要保证未完成的线程继续完成 如果线程池中所有任务都完成了,isTerminated返回true
while(!executor.isTerminated())
{}
long end = System.currentTimeMillis(); //balance有余额的意思
System.out.println("现在账户里面的余额是:" + account.getBalance());
System.out.println("花费的时间以微秒为单位:"+(end-start)+"微秒");
} //这个线程只调用了一个方法
public static class AddPennyTask implements Runnable
{
@Override
public void run() {
account.deposit(1);
}
} //一个内部类 用于 账户的相关处理
public static class Account
{
private int balance =0;
public int getBalance()
{
return balance;
}
public void deposit(int amount)
{
int newBalance = balance + amount;
//为了让错误体现的更明显
try {
Thread.sleep(4); //5毫秒
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
balance= newBalance;
//其实就是balance +=amount;//不过换成这一段代码结果就在100和99左右
}
}
}

 

运行截图

 

       错的不是一般的明显,明明存入了100块,显示只有2块,原因也很简单,就是100个线程同时对acount进行修改,当第100该线程把钱改成100时,第2个线程最后修改,把它改成了2,这群不听话的线程,那就好好管管他们吧,让他们乖乖听话,在这之前,还是要记住以下一些名词:

竞争状态:当多个线程访问同一公共资源并引发冲突造成线程不同步,我们称这种状态为竞争状态。

线程安全:是针对类来说的,如果一个类的对象在多线程程序中不会导致竞争状态,我们就称这类为线程安全的,上面的Account是线性不安全的,而又如StringBuffer是线程安全的,而StringBuilder则是线程不安全的。

 

临界区:造成竞争状态的原因是多个线程同时进入了程序中某一特定部分,如上面程序中的deposit方法,我们称这部分为程序中的临界区,我们要解决多线程不同步问题,就是要解决如何让多个线程有序的访问临界区。

 

使用synchronized关键字

1、同步方法:在deposit方法前加synchronized关键字,使得该方法变成同步方法:public synchronized void deposit(double amount){}

2、同步语句:对某一代码块加synchronized关键字,常用格式:

synchronized(exper) {statement}   其中exper是对象的引用,如上面的程序,在要在调用depsoit方法时,改成这样:  synchronized(account){account.deposit(1);}

      使用synchronized,其实是隐式地给方法或者代码块加了锁,任何同步的实例方法都可以转换为同步语句:

public synchronized void  method(){}

转换为同步语句: public  void  method{sysnchronized(this){}}

 

利用lock加锁同步

       java也可以用Lock显示的对临界区代码加锁以及解锁,这比用synchronized关键字更加直观灵活。

       一个锁是一个Lock接口的实例,该接口定义了加锁解锁的方法,且一个锁可以多次调用其newCondition()方法创建名为Condition对象的实例,以此进行线程间的通信(在后面用到)。

     有了Lock接口,我们还要实现它,java提供了RenentrantLock类,该类是为创建相互排斥锁而实现了Lock接口,由此就好办了,下面看一下书上的图:

代码如下:

public static class Account2
{
private static Lock lock = new ReentrantLock();
private int balance =0;
public int getBalance()
{
return balance;
}
public void deposit(int amount)
{
lock.lock();
try{
int newBalance = balance + amount;
Thread.sleep(4);
balance= newBalance;
}catch (InterruptedException e) {
e.printStackTrace();
}
finally{
lock.unlock();
}
}
}

 

给个运行截图:

         100块存入,且时间明显比之前久了,100个线程都乖乖的排队访问临界区。另外注意在对lock()的调用后,紧跟随try catch finnaly语句,这是个好习惯。

 

利用信号量同步

            信号量是个好东西,信号量机制在操作系统方面有着广泛的应用,如linux进程同步信号量,而在java里,Semaphore包包含了一些访问信号量的方法。

信号量可以用来限制访问共享资源的线程数,在访问临界区资源前,线程必须获取一个信号量,在访问完之后返回一个信号量。下图是关于类Semaphore,该类包含访问信号量的方法:

利用信号量Semaphorre,可以将上面的Account类改成这样:

public static class Account
{
private static Semaphore semaphore = new Semaphore(1); //创建一个信号量
private int balance =0;
public int getBalance()
{
return balance;
}
public void deposit(int amount)
{
try {
semaphore.acquire();
int newBalance = balance + amount;
Thread.sleep(4);
balance= newBalance;
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
semaphore.release(); //返回一个信号量
}
}
}

 

      网上有很多关于java多线程的文章,有不少写成系列的,但个人觉得像多线程这么有有意思的部分,还是得好好静下心来品位,上面的三种方法,可以解决多线程同步问题,后面还会继续学习,嗯,加油。

     本文出自于博客园兰幽,转载请说明出处。

java多线程二之线程同步的三种方法的更多相关文章

  1. Java中实现线程同步的三种方法

    实现同步的三种方法 多线程共享数据时,会发生线程不安全的情况,多线程共享数据必须同步. 实现同步的三种方法: 使用同步代码块 使用同步方法 使用互斥锁ReetrantLock(更灵活的代码控制) 代码 ...

  2. JAVA之线程同步的三种方法

    最近接触到一个图片加载的项目,其中有声明到的线程池等资源需要在系统中线程共享,所以就去研究了一下线程同步的知识,总结了三种常用的线程同步的方法,特来与大家分享一下.这三种方法分别是:synchroni ...

  3. 【转】 Linux 线程同步的三种方法

    线程的最大特点是资源的共享性,但资源共享中的同步问题是多线程编程的难点.linux下提供了多种方式来处理线程同步,最常用的是互斥锁.条件变量和信号量. 一.互斥锁(mutex) 通过锁机制实现线程间的 ...

  4. Linux 线程同步的三种方法(互斥锁、条件变量、信号量)

    互斥锁 #include <cstdio> #include <cstdlib> #include <unistd.h> #include <pthread. ...

  5. IOS 多线程,线程同步的三种方式

    本文主要是讲述 IOS 多线程,线程同步的三种方式,更多IOS技术知识,请登陆疯狂软件教育官网. 一般情况下我们使用线程,在多个线程共同访问同一块资源.为保护线程资源的安全和线程访问的正确性. 在IO ...

  6. 归纳一下:C#线程同步的几种方法

    转自原文 归纳一下:C#线程同步的几种方法 我们在编程的时候,有时会使用多线程来解决问题,比如你的程序需要在后台处理一大堆数据,但还要使用户界面处于可操作状态:或者你的程序需要访问一些外部资源如数据库 ...

  7. Linux下线程同步的几种方法

    Linux下提供了多种方式来处理线程同步,最常用的是互斥锁.条件变量和信号量. 一.互斥锁(mutex) 锁机制是同一时刻只允许一个线程执行一个关键部分的代码.  1. 初始化锁 int pthrea ...

  8. Java多线程学习总结--线程同步(2)

    线程同步是为了让多个线程在共享数据时,保持数据的一致性.举个例子,有两个人同时取钱,假设用户账户余额是1000,第一个用户取钱800,在第一个用户取钱的同时,第二个用户取钱600.银行规定,用户不允许 ...

  9. java核心知识点----创建线程的第三种方式 Callable 和 Future CompletionService

    前面已经指出通过实现Runnable时,Thread类的作用就是将run()方法包装成线程执行体,那么是否可以直接把任意方法都包装成线程执行体呢?Java目前不行,但其模仿者C#中是可以的. Call ...

随机推荐

  1. oracle 的分页查询与mysql 的分页查询

    oracle 的分页查询: select * from (select o.*,rownum rn  from Persons o) where rn >40 and rn <=60 : ...

  2. Redis工具之Jedis

    //jedis的连接池 public void test1(){ //创建连接池配置对象 JedisPoolConfig poolConfig = new JedisPoolConfig(); poo ...

  3. 查找mysql中未提交的事务

    1.查找未提交事务 在mysql中运行: select t.trx_mysql_thread_id from information_schema.innodb_trx t 2.删除线程 kill   ...

  4. Java反射的两种使用方法

    1.创建User.java package com.tao.test; public class User { private String name; private int id; public ...

  5. Co. - VMware - vSphere

    VMware vSphere 组件 VMware vSphere是VMware推出的基于云计算的新一代数据中心虚拟化套件,它由VMware ESXi.VMware vCenter Server.VMw ...

  6. Java OOP——第四章 异常

    1. 接口:接口就是给出一些没有内容的方法,封装到一起,到某个类要使用的时候,在根据具体情况把这些方法写出来. 接口是更加抽象的抽象的类, 抽象类里的方法可以有方法体, 接口里的所有方法都没有方法体. ...

  7. 初次了解MVC框架模式

    MVC框架:即Model.View.Controller即模型.视图.控制器. View层是界面,Model层是业务逻辑,Controller层用来调度View层和Model层,将显示界面和业务逻辑合 ...

  8. springmvc处理器拦截器

    处理器拦截器(interceptor)是做什么用的? 想知道处理拦截器做什么用的,你要先了解下处理·流程链·. 前端控制器(dispatcherServlet)接收到请求,通过handleMappin ...

  9. thinkphp 3.2中依靠关联模型来关联三个表

    这里说的是用thinkphp3.2关联模型关联三个表 根据用户表查询出三个表的数据,需要两个model来配合,第一个model是根据user表来查询到班级的信息,然后第二个model是根绝banji中 ...

  10. php开发aes加密总结

    <?php class Aes { /** * aes 加密 解密类库 * @by singwa * Class Aes *说明:本类只适用于加密字符串 * */ private $key = ...