线程的同步与安全:
在很多情况下,我们都会遇到线程的安全与同步问题,例如下面网上售票的系统:
如果不对不加线程锁的话,会出现不同的窗口买出重票,而现实生活中例如(1-100)每个票就一张总共100张,不可能重复.
这里有线程安全同步的三个方法:
1.同步代码块:
Object obj = new Object();
synchronized (obj) {}
这里我自己要明白为啥这样写,
首先object类说有类的父类,这里就是用obj来代替所有的对象
二者,下面才是重要的,就是锁住你觉得会发生同步完全的代码,例如这里就是我觉得买票的时候就只能有一个窗口在买票,其他的就不能进来.
2.同步锁,锁的是方法:
//重写Run方法
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
sellticket();//方法
}
}
private void sellticket() {
// TODO Auto-generated method stub
synchronized (TicketImpl.class) {
if(ticketNum>0){
ticketNum–;
System.out.println(Thread.currentThread().getName()+“卖出”+ticketNum+“票”);
}
}
这里自己要明白的一点这是在run方法里面写的一个方法,然后在下面锁方法
值得我们注意的是这句话synchronized (TicketImpl.class)锁的是当前的类(我也不太明白,搞懂了在给自己解释)
3.开启锁,释放锁lock
//拿到对象
Lock lo = new ReentrantLock(); //这里查看源码可以明白更多,自己记住
lo.lock();
要锁的代码
lo.unlock();
首先我要知道要想调用lock()和unlock(),必须要对象
接口 对象名 = new 类名;
类名 对象名 = new 类名;
实例化对象调用方法,你就会发现使用接口 对象名 = new 类名; 方式实例化的对象只能调用接口中有的方法,而不能调用类中特有的方法。而使用类名 对象名 = new 类名;方式创建出来的对象可以调用所有的方法
使用接口编程的好处是统一规范化。
第一种方法:同步代码块:
/**
*
- 网上售票系统
- @author dch
*/
public class Ticket implements Runnable {
//票的总数
static int ticket = 20;
Object obj = new Object();
//线程名字
private String name ;
//无参构造
public Ticket(){
}
//构造方法
public Ticket(String name){
this.name = name ;
}
//重写Run方法
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
synchronized (obj) {
if(ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--卖出"+(21-ticket)+"张票");
ticket--;
}
}
}
}
}
/**
*
- @author dch
*/
//测试类
public class TicketTest {
public static void main(String[] args) {
Ticket t = new Ticket();
//创建线程对象
Thread t1 = new Thread(t, "窗口1");
Thread t2 = new Thread(t, "窗口2");
Thread t3 = new Thread(t, "窗口3");
//准备运行
t1.start();
t2.start();
t3.start();
}
}
第二种方法:同步锁,锁的是方法:
/**
*
- @author dch
*/
public class TicketImpl implements Runnable {
//定义一个票数
private static int ticketNum=100;
//重写Run方法
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
sellticket();
}
}
//同步锁,锁的是方法
private void sellticket() {
// TODO Auto-generated method stub
synchronized (TicketImpl.class) {
if(ticketNum>0){
ticketNum--;
System.out.println(Thread.currentThread().getName()+"卖出"+ticketNum+"票");
}
}
}
}
/**
*
- @author dch
*/
//测试类
public class TicketTest {
public static void main(String[] args) {
//创建对象
TicketImpl ti = new TicketImpl();
//借用Thread
Thread t1 = new Thread(ti, "窗口1");
Thread t2 = new Thread(ti, "窗口2");
Thread t3 = new Thread(ti, "窗口3");
//进入运行状态
t1.start();
t2.start();
t3.start();
}
}
第三种 lock锁
/**
*
- @author dch
*/
//线程用接口实现
public class TicketImpl implements Runnable {
//定义票数
private static int ticketNum = 100;
//拿到对象
Lock lo = new ReentrantLock();
//重写run方法
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
//开启锁
lo.lock();
if(ticketNum>0){
try {
Thread.sleep(10);
ticketNum--;
System.out.println(Thread.currentThread().getName()+"卖出"+ticketNum+"票");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
//释放锁
lo.unlock();
}
}
}
}
}
/**
*
- @author dch
*/
//测试类
public class TicketTest {
public static void main(String[] args) {
//创建线程对象
TicketImpl ti = new TicketImpl();
//借助Thread类创建
Thread t1 = new Thread(ti, "窗口1");
Thread t2 = new Thread(ti, "窗口2");
Thread t3 = new Thread(ti, "窗口3");
//运行
t1.start();
t2.start();
t3.start();
}
}