java线程同步的三种方法[synchronized关键字,Lock加锁,信号量Semaphore]
来源:互联网 发布:韩国制衣软件 编辑:程序博客网 时间:2024/04/30 20:21
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线程同步的三种方法[synchronized关键字,Lock加锁,信号量Semaphore]
- synchronized 线程同步的三种方法
- java线程同步:synchronized关键字,Lock接口以及可重入锁ReentrantLock
- java多线程三种方式区别,java多线程,线程同步方式,线程同步加锁的方法,wait与sleep区别
- 线程同步----信号量(Semaphore)
- 线程同步----信号量(Semaphore)
- 线程同步--信号量Semaphore
- 线程同步:信号量Semaphore的使用
- java 线程同步:synchronized 关键字
- Java线程同步的解决方案——synchronized与Lock
- synchronized关键字加锁-线程可重入
- Java线程同步synchronized,Lock,Condition
- java线程同步synchronized与lock
- 【Java基础之线程同步(二)】java线程同步:synchronized关键字,Lock接口以及可重入锁ReentrantLock
- java中线程sleep和加锁synchronized 关键字的一些区别
- java线程同步——信号量(Semaphore)
- java的线程同步机制synchronized关键字的理解
- java的线程同步机制synchronized关键字的理解
- for update 数据库锁机制(mysql下测试)
- bzoj 4402 Claris的剑 组合数学
- GitHub查找优秀的开源项目和一些资源福利
- JAVA基础
- 树链剖分 — 轻重边路径剖分
- java线程同步的三种方法[synchronized关键字,Lock加锁,信号量Semaphore]
- JS跨域请求
- 拯救你的数据 通过日志恢复MSSQL数据
- CentOS7安装配置mysql5.7
- 更新UI操作需要在主线程完成
- 基于Java Socket的自定义协议
- 进程与线程的区别与联系
- 八种排序算法
- Executors创建固定线程demo