java多线程:线程同步(二)

时间:2022-12-22 23:25:28

一、示例

模拟简单的售票业务:

public class ThreadMain1 {
public static void main(String[] args){
TestThread tt = new TestThread();
new Thread(tt).start();
new Thread(tt).start();
}
}

class TestThread implements Runnable{
int tickets = 100;//票数100
public void run(){
while(true){
if(tickets>0){
try {
//模拟并发
Thread.sleep(10);
} catch (Exception e) {
// TODO: handle exception
}
tickets--;//票数减1
System.out.println("run" + Thread.currentThread().getName() + "--" + tickets);
}
}
}
}

截取部分输出:



runThread-0--10
runThread-1--9
runThread-0--8
runThread-0--7
runThread-0--6
runThread-0--5
runThread-1--4
runThread-0--3
runThread-0--2
runThread-0--1
runThread-1--0

  最后我们看到票数输出了0,根据我们的条件if(tickets>0),票数是不能为0的,其中的原因就在于线程间产生了并发

在线程一执行tickets--代码时,线程二也进入了线程等待执行tickets--(因为中间我们设置了Thread.sleep(10)),最后出现了tickets0的现象。原因在于newThread(tt).start();只是线程进入就绪态,并没有执行run()。


二、如何解决线程间的同步问题?

在数据库多表操作中,我们通常用事务的原子性保证数据完成性正确性,多线程中我们通过保证线程执行代码的原子性来解决同步问题。

即保证此段代码的原子性:

if(tickets>0){
try {
//模拟并发
Thread.sleep(10);
} catch (Exception e) {
// TODO: handle exception
}
tickets--;
System.out.println("run" + Thread.currentThread().getName() + "--" + tickets);
}

要保证它的原子性,多线程不能同时执行这段代码,也即使我们线程的同步问题。

 

java中提供了synchronized关键字来设置同步语句块:

class TestThread implements Runnable{
int i = 100;
String str = new String(""); //写在方法外,监视器必须监视同一个对象
public void run(){
while(true){
//线程同步
synchronized(str){
if(i >0)
try {
//模拟并发
Thread.sleep(10);
} catch (Exception e) {
// TODO: handle exception
}
tickets--;
System.out.println("run" + Thread.currentThread().getName() + "--" + tickets);
}
}
}
}

  synchronized必须要有对象,会对这个对象设置标志位,为0时标识已被一个线程占用,其他需要等待,完成时置为1,其他线程可进入代码块。synchronized作为同步监视器对其对象进行监视。

 

执行过程中我们发现使用synchronized监视器比不使用时的执行速度要慢,因为系统要不停的对监视器进行检查,但为了线程的安全也必须这样做。


除了以上通过synchronized监视器同步代码外,还可以同步方法:

class TestThread implements Runnable{
int i = 100;
String str = new String(""); //写在方法外,监视器必须监视同一个对象
public void run(){
while(true){
sale();
}
}
//同步方法
public synchronized void sale(){
if(i >0){
try {
//模拟并发
Thread.sleep(10);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println("run" + Thread.currentThread().getName()+"--"+i--);
}
}

}


  代码块中使用的是str同步对象

  同步方法中使用的是this对象

  不管是同步代码块还是同步方法,线程同步靠的是检查某一对象的标识位来实现线程间的同步,要同时实现代码块和同步方法使用同一个监视对象,那么代码块和同步方法之间就是可以同步的。即都用this对象。


当同时使用多个synchronized对同一对象进行监视时产生的死锁现象。详见《java多线程:线程同步通信》