多线程共享数据
模拟抢票例子:
package cn.kgc;
/**
* @Author $(USER)
* @Date $(DATE)
* @Description 模拟用户网络购票
*/
public class TicketThread implements Runnable {
private int ticket=10;//记录车票总数
private int num=0;//记录用户抢到了第几张票
@Override
public void run() {
while(true){
//没有余票,跳出循环
if(ticket<=0){
break;
}
ticket--;
num++;
//模拟网络延迟
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到了第"+num+"张票,剩余"+ticket+"张票");
}
}
}
package cn.kgc;
/**
* @Author $(USER)
* @Date $(DATE)
* @Description
*/
public class TestThread {
public static void main(String[] args) {
Runnable runnable=new TicketThread();
Thread t1=new Thread(runnable,"张麻子");
Thread t2=new Thread(runnable,"黄四郎");
Thread t3=new Thread(runnable,"武举人");
System.out.println("开始抢票!");
t1.start();
t2.start();
t3.start();
}
}
发现问题:
不是从第一张票开始抢的;存在多人抢到一张票的情况。。。。。。
由此可发现多线程共享数据可能带来的问题:数据不安全,响应慢
原因:
多线程共同操作数据时,引发的冲突(如延迟时,操作未全部完成)
解决方法:
同步方法,同步代码块!
线程同步概念:
当两个或多个线程需要访问同一资源时,需要以某种顺序来确保该资源在某一时刻只能被一个线程使用的方式称为线程同步
采用同步来控制线程得执行有两种方式:同步方法和同步代码块,且都需要使用synchronized方法实现。
1.同步方法
语法格式:
访问修饰符 synchronized 返回类型 方法名{}
或 synchronized 访问修饰符 返回类型 方法名{}
修改代码如下:
扫描二维码关注公众号,回复:
13261203 查看本文章
package cn.kgc;
/**
* @Author $(USER)
* @Date $(DATE)
* @Description 模拟用户购票-----使用同步方法解决线程带来的数据不安全问题
*/
public class TicketThread1 implements Runnable{
private int ticket=10;//记录车票总数
private int num=0;//记录用户抢到了第几张票
private boolean flag=false;//票是否卖完,false代表没卖完
@Override
public void run() {
while(!flag){//这里得flag只是一个标志位!!!!!
sale();//调用用户抢票方法
}
}
//设置用户抢票方法
public synchronized void sale(){
//没有余票则跳出循环
if(ticket<=0){
flag=true;//票是否卖完,true代表卖完
return;
}
//有余票则抢票,第一步修改车票数:总票数-1;用户+1
ticket--;
num++;
//网络延迟
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//第二部:显示出票反馈给用户
System.out.println(Thread.currentThread().getName()+"抢到了第"+num+"张票,剩余"+ticket+"张");
}
}
package cn.kgc;
/**
* @Author $(USER)
* @Date $(DATE)
* @Description 模拟用户抢票
*/
public class TestThreadticket1 {
public static void main(String[] args) {
Runnable runnable=new TicketThread1();
Thread t1=new Thread(runnable,"携程");
Thread t2=new Thread(runnable,"美团");
Thread t3=new Thread(runnable,"12306");
System.out.println("+************开始抢票************");
t2.start();
t3.start();
t1.start();
}
}
2.同步代码块
语法格式
synchronized(synObject){
//需要同步访问控制的代码
}
synchronized中得代码块必须获得对象syncObject的锁才能执行,具体实现机制和同步方法一样。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。
修改代码如下:
package cn.kgc;
/**
* @Author $(USER)
* @Date $(DATE)
* @Description 模拟用户购票-----使用同步代码块的方法解决线程带来的数据不安全问题
*/
public class TicketThread3 implements Runnable{
private int ticket=10;//记录车票总数
private int num=0;//记录用户抢到了第几张票
@Override
public void run() {
while(true){
synchronized (this){
//没有余票则跳出循环
if(ticket<=0){
break;
}
//有余票则抢票,第一步修改车票数:总票数-1;用户+1
ticket--;
num++;
//网络延迟
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//第二部:显示出票反馈给用户
System.out.println(Thread.currentThread().getName()+"抢到了第"+num+"张票,剩余"+ticket+"张");
}
}
}
}
package cn.kgc;
/**
* @Author $(USER)
* @Date $(DATE)
* @Description 模拟用户抢票
*/
public class TestThreadticket3 {
public static void main(String[] args) {
Runnable runnable=new TicketThread3();
Thread t1=new Thread(runnable,"携程");
Thread t2=new Thread(runnable,"美团");
Thread t3=new Thread(runnable,"12306");
System.out.println("+************开始抢票************");
t1.start();
t2.start();
t3.start();
}
}