1.线程安全
多线程同时操作一个共享数据,往往会出现安全问题
例如这里:有三个线程,当ticket=1,t0线程抢到CPU资源,if判断结果为真,准备运行打印语句时,CPU资源突然被线程t1抢走了,此时ticket还没有进行(ticket–)操作,所以t1的if判断结果结尾也是真,此时准备运行打印语句时,CPU资源突然被又线程t3抢走了,此时ticket依旧没有进行(ticket–)操作,所以这里if判断结果依旧为真,这里t0和t1都陷入了临时阻塞的状态,当t0又抢到CPU的资源,(ticket–)结果打印出来就是(0),然后当t1又抢到CPU的资源时,(ticket–)结果打印出来就是(-1)。这样就出现了安全隐患。
例:一个售票系统,可以通过网上同时多人买票。
package cn.itcast.demo01;
//并发操作,同时针对一个数据源实行并发操作
public class Tickets01 implements Runnable{
private int tickets = 10;
public void run() {
while(true){
if(tickets > 0){
try {
//为了能更容易地看到这个隐患,让线程在这故意休眠10ms
Thread.sleep(10L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"...出售了第"+(tickets--)+"张");
}
}
}
}
package cn.itcast.demo01;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//并发操作
public class ThreadDemo01 {
public static void main(String[] args) {
Runnable r = new Tickets01();
// 创建线程池
ExecutorService es = Executors.newFixedThreadPool(2);
es.submit(r);
es.submit(r);
es.shutdown();
}
}
出现安全隐患的结果如图:
结果这里打印了不应该存在的0;
2.同步技术
java提供了一种技术:同步技术,用来解决线程的安全问题。
下面简单写一下同步代码块的格式:
sychronized(任意对象){
线程共享数据;
}
任意对象,这里的对象又称同步锁,对象监视器。
注意:这里不能写匿名对象,通常写 Object object= new Object(),用obj作为同步代码块的锁。
3.同步代码块的执行原理
线程遇到同步代码块,线程首先会判断有没有同步锁
有–>则获取锁并进入到同步代码块中,等代码块中的程序执行完毕,出代码块,并归还所。
无–>则无法进入同步代码块,被挡在代码块的外面。
好处:代码简洁,节约内存。
下面是加了同步代码块的代码,mian函数还是那样。
package cn.itcast.demo01;
//并发操作,同时针对一个数据源实行并发操作
public class Tickets01 implements Runnable{
private int tickets = 10;
//同步代码块的锁
Object obj = new Object();
public void run() {
while(true){
synchronized (obj) {
if(tickets > 0){
try {
//为了能更容易地看到这个隐患,让线程在这故意休眠10ms
Thread.sleep(10L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"...出售了第"+(tickets--)+"张");
}
}
}
}
}
这里还可以将共享数据抽取出来,写成一个方法,在方法的声明中加入sychronized
例:public sychronized void pay(){ }//这里的同步对象锁就是this了。
package cn.itcast.demo01;
//并发操作,同时针对一个数据源实行并发操作
public class Tickets01 implements Runnable {
private int tickets = 10;
public void run() {
while (true) {
pay();
}
}
public synchronized void pay() {
if (tickets > 0) {
try {
// 为了能更容易地看到这个隐患,让线程在这故意休眠10ms
Thread.sleep(10L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "...出售了第"
+ (tickets--) + "张");
}
}
}
问:
(1)同步方法有锁吗?
答:有,锁就是本类对象引用this
(2)如果同步方法是静态的,同步还有锁吗?
答:有,锁就是本类类名.class,不是this。