线程同步,多个线程操作共同的变
分享于 点击 2962 次 点评:32
线程同步,多个线程操作共同的变
线程同步
线程安全
要保证线程安全有两个前提:
- 程序调用了多线程。
- 多个线程操作共同的变量
以上两个条件满足后,程序就有可能触犯线程不安全的问题
什么是线程不安全?
举例说明:假如一场演唱会需要售卖门票,有三个售票口,A,B,C。它们会同时售票,假如一共只有100张票,那么当100张票售卖完后,售票口就需要停止工作
以下是是实现代码
public class SaleTicket extends Thread {
//统计剩余票数
private int countticket = 100;
@Override
public void run() {
while (true) {
if (countticket <= 0) {
break;
} else {
countticket--;
System.out.println(getName() + "售票成功,剩余票数:" + countticket);
}
}
}
}
class Test {
public static void main(String[] args) {
SaleTicket s1 = new SaleTicket();
SaleTicket s2 = new SaleTicket();
SaleTicket s3 = new SaleTicket();
s1.setName("窗口A");
s2.setName("窗口B");
s3.setName("窗口C");
//启用三条线程
s1.start();
s2.start();
s3.start();
}
}
运行结果如下:
可以发现结果出现了重复的数据,这是由于这三个线程一直在反复抢占CPU的执行权导致的,解决办法有三个:
1. synchronized()
同步代码块
synchronized(任意对象) {
多条语句操作共享数据的代码
}
此代码块的功能是锁住要执行的代码块,代码锁住后,其他线程即使抢到CPU的执行权,也不能进入锁定代码块进行执行,也就是说,同一时间锁里面只能有一条线程在操作,操作完后解锁。
修改后代码:
public class SaleTicket implements Runnable {
//统计剩余票数
private int countTicket = 100;
//给代码加锁,同步代码块的锁对象可以是任何对象,但必须唯一
private Object obj= new Object();
@Override
public void run() {
while (true) {
synchronized (obj){
if (countTicket <= 0) {
break;
} else {
countTicket--;
System.out.println(Thread.currentThread().getName() + "售票成功,剩余票数:" + countTicket);
}
}
}
}
}
class Test {
public static void main(String[] args) {
SaleTicket s1 = new SaleTicket();
Thread t1 = new Thread(s1);
Thread t2 = new Thread(s1);
Thread t3 = new Thread(s1);
t1.setName("窗口A");
t2.setName("窗口B");
t3.setName("窗口C");
//启用三条线程
t1.start();
t2.start();
t3.start();
}
}
结果如下:
2. 同步方法
修饰符 synchronized 返回值类型 方法名(方法参数) {
方法体;
}
将要锁住的方法提取到方法中,并用synchronized修饰
public class SaleTicket implements Runnable {
//统计剩余票数
private int countTicket = 100;
private Object obj = new Object();
@Override
public void run() {
while (true) {
boolean result = syMethod();
if (result) {
break;
}
}
}
//同步方法
private synchronized boolean syMethod() {
if (countTicket <= 0) {
return true;
} else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
countTicket--;
System.out.println(Thread.currentThread().getName() + "售票成功,剩余票数:" + countTicket);
return false;
}
}
}
class Test {
public static void main(String[] args) {
SaleTicket s1 = new SaleTicket();
Thread t1 = new Thread(s1);
Thread t2 = new Thread(s1);
Thread t3 = new Thread(s1);
t1.setName("窗口A");
t2.setName("窗口B");
t3.setName("窗口C");
//启用三条线程
t1.start();
t2.start();
t3.start();
}
}
3.Lock
在JDK5以后新增了一个锁对象Lock, Lock是接口不能实例化,所有调用它的实现类ReentrantLock来实例化
public class SaleTicket implements Runnable {
//统计剩余票数
private int countTicket = 100;
//实例化一个ReentrantLock对象
private ReentrantLock rtl = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
rtl.lock();
if (countTicket <= 0) {
break;
} else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
countTicket--;
System.out.println(Thread.currentThread().getName() + "售票成功,剩余票数:" + countTicket);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
rtl.unlock();
}
}
}
}
class Test {
public static void main(String[] args) {
SaleTicket s1 = new SaleTicket();
Thread t1 = new Thread(s1);
Thread t2 = new Thread(s1);
Thread t3 = new Thread(s1);
t1.setName("窗口A");
t2.setName("窗口B");
t3.setName("窗口C");
//启用三条线程
t1.start();
t2.start();
t3.start();
}
}
相关文章
- 暂无相关文章
用户点评