- 同步:
问题的引出:
public
class
Test {
public
static
void
main(String[] args) {
MyThread t =
new
MyThread();
new
Thread(t,
"---001---"
).start();
new
Thread(t,
"---002---"
).start();
new
Thread(t,
"---003---"
).start();
}
}
class
MyThread
implements
Runnable {
int
num
= 5;
public
void
run() {
for
(
int
i = 0; i < 10; i++) {
if
(
num
> 0) {
try
{
Thread.
sleep
(1000);
}
catch
(Exception e) {
e.printStackTrace();
}
System.
out
.println(Thread.
currentThread
().getName() +
" : num= "
+
num
--);
}
}
}
}
运行结果:
那么看结果出现了负数,并且数据是三个三个出现的,为什么呢?
再看程序,在判断num>0以后又出现了延迟,那么在延迟中,其他的进程可能正在执行,执行的时候将num-1,那么在打印后就出现了负数的情况。
那么这里就出现了同步的状况,为了让其延迟的时间里,其他的进程不再执行,那么需要将进程锁住,执行完了再让其他进程执行。
- 同步的概念:
同步是指多个操作在同一个时间段内只能有一个线程进行,其它线程要等待此线程完成后才可以继续执行。
同步可以解决资源共享时的问题,如果两个或多个线程共同享有一个对象,那么当我们执行修改对象的操作时,我们就需要将这些操作同步(锁)起来。
我们可以给共享资源加一把锁,这把锁只有一把钥匙。哪个线程获取了这把钥匙,才有权利访问该共享资源。
锁的原理:
Java中每个对象都有一个内置锁
当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例(this实例)有关的锁。获得一个对象的锁也称为获取锁、锁定对象、在对象上锁定或在对象上同步。
当程序运行到synchronized同步方法或代码块时才该对象锁才起作用。
一个对象只有一个锁。所以,如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放(或返回)锁。这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放。
用哪个对象的锁:
一般使用正在执行代码类的当前实例(this)的锁,也可以用别的对象的锁,但要确保共享资源的线程们用的是同一把锁(这也说明:锁本身也一定是线程们之间的共享对象)。
错误例子如下:
public
void
f()
{
private
Object
obj
=
new
Object
();
synchronized
(
obj
) {
。。。。
}
}
上面这段代码没有任何意义。因为那个同步锁是在函数体内部产生的。每个线程调用这段代码的时候,都会产生一个新的同步锁。那么多个线程之间,使用的是不同的同步锁。根本达不到同步的目的。
为了确保线程用的是同一把锁,你可以把同步对象声明为
static
P
ublic static final
Object
obj
=
new
Object
();
public
void
f()
{
synchronized
(
obj
) {
。。。。
}
}
- 同步的方法:
通过同步代码的方式进行代码的加锁操作,同步方式有两种:
- 同步代码块
- 同步方法
- 同步代码块:
使用synchronized关键字进行同步代码块的声明,但在使用此操作时必须明确的指出到底要锁定的是哪个对象,一般都是以当前对象(this)为主。
synchronized
(
同步对象
){
需要同步的代码
}
看如下代码:
public
class
Test {
public
static
void
main(String[] args) {
MyThread t =
new
MyThread();
new
Thread(t,
"---001---"
).start();
new
Thread(t,
"---002---"
).start();
new
Thread(t,
"---003---"
).start();
}
}
class
MyThread
implements
Runnable {
int
num
= 5;
public
void
run() {
for
(
int
i = 0; i < 10; i++) {
synchronized
(
this
) {
if
(
num
> 0) {
try
{
Thread.
sleep
(300);
}
catch
(Exception e) {
e.printStackTrace();
}
System.
out
.println(Thread.
currentThread
().getName()
+
"name : num= "
+
num
--);
}
}
}
}
}
结果如下:
由输出可见:
数据是一个一个出来的,而且没有0和负数出现。但是其运行效率要低于异步处理的。
- 同步方法:
同步方法锁定的是正在执行代码类的当前实例(this实例)。
synchronized
返回值 方法名称(参数列表){}
将上面代码改改就变成如下程序:
class
MyThread
implements
Runnable {
int
num
= 5;
public
void
run() {
for
(
int
i = 0; i < 100; i++) {
fun();
}
}
public
synchronized
void
fun() {
if
(
num
> 0) {
try
{
Thread.
sleep
(300);
}
catch
(Exception e) {
e.printStackTrace();
}
System.
out
.println(Thread.
currentThread
().getName()
+
"name : num= "
+
num
--);
}
}
}
执行结果和上面一样。
二、死锁:
资源共享时需要进行同步操作!
程序中过多的同步会产生死锁!
死锁一般就是表示进程间互相等待对方执行,现在谁也不执行的情况。
例如:
public
class
Test {
public
static
void
main(String[] args) {
DeadLock_Run zsRun =
new
DeadLock_Run();
//
控制张三
DeadLock_Run lsRun =
new
DeadLock_Run();
//
控制李四
zsRun.
firstSpeak
=
true
;
new
Thread(zsRun).start();
new
Thread(lsRun).start();
}
}
class
DeadLock_Run
implements
Runnable{
private
static
Zhangsan
zs
=
new
Zhangsan();
//
实例化张三对象,static很重要
private
static
Lisi
ls
=
new
Lisi();
//
实例化李四对象,static很重要
public
boolean
firstSpeak
=
false
;
//
声明标志位,判断那个先说话
public
void
run() {
if
(
firstSpeak
) {
synchronized
(
zs
) {
//
同步张三
zs
.say();
try
{
Thread.
sleep
(500);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
synchronized
(
ls
) {
//
等待李四的答复(要获取李四的锁的钥匙),让李四给他画
ls
.give();
}
}
}
else
{
synchronized
(
ls
) {
//
同步李四
ls
.say();
try
{
Thread.
sleep
(500);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
synchronized
(
zs
) {
//
等待张三的答复(要获取张三的锁的钥匙),让张三给他书
zs
.give();
}
}
}
}
}
class
Zhangsan {
//
定义张三类
public
void
say() {
System.
out
.println(
"
张三对李四说:
“
你给我画,我就把书给你。
”
"
);
}
public
void
give() {
System.
out
.println(
"
张三给出了书。"
);
}
};
class
Lisi {
//
定义李四类
public
void
say() {
System.
out
.println(
"
李四对张三说:
“
你给我书,我就把画给你
”
"
);
}
public
void
give() {
System.
out
.println(
"
李四给出了画。"
);
}
};
张三说了话等待李四回应,李四说了话也等待张三回应,于是。。。。。死锁了。