java线程安全问题
出现原因:多个线程之间存在“共享数据”(即共同操作的变量,“ticket”)
车站卖票案例:
定义窗口1类实现Runnable接口
class Window1 implements Runnable{
private int ticket = 100;
@override
public void run(){
while(true){
if(ticket > 0 ){
//为了让线程安全问题更明显(错票)
try{
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
/*
如果放在这里就是重票
try{
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
*/
ticket--;
} else {
break;
}
}
}
定义主方法
public class WindowTest1 {
public static void main(String[] args) {
Window1 w = new Window1();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
运行结果如下:
如何解决:
方式一:同步代码块
synchronized(同步监视器) { //需要的代码 }
说明:
1) 操作共享数据的代码,即为需要被同步的代码
2)同步监视器,俗称“锁”。任何一个类的对象,都可以充当锁
要求:多个线程之间必须共用一个锁
补充:
在实现Runnable接口创建多线程时,可以考虑使用this充当同步监视器
在继承Thread类创建多线程时,可以考虑使用当前类.class充当同步监视器
修改如下:
class Window1 implements Runnable {
private int ticket = 100;
Object obj = new Object();
@override
public void run() {
while(true) {
synchronized (obj) {
if (ticket > 0 ) {
try{
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
} else {
break;
}
}
}
}
方式二:同步方法
如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明为同步的。
总结:
1.同步方法仍然涉及到同步监视器,只是我们不需要显示的声明
2.非静态的同步方法,同步监视器是:this
静态的同步方法,同步监视器是:当前类本身
代码如下;
private static synchronized void show(){
}
方式三:Lock锁 —JDK5.0新增
> class Window implements Runnable {
> private int ticket = 100;
> //1.创建lock对象
> private ReentrantLock lock = new ReentrantLock(fair:true);
>
> @override
> public void run() {
> while(true) {
> try {
>
> //2.调用锁定方法lock()
> lock.lock();
>
> if(ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
> System.out.println(Thread.currentThread().getName() + ":售票,票号为:" + ticket);
> ticket--;
> } else {
> break;
> } finally {
> //3.调用解锁方法:unlock()
> lock.unlock();
> }
> }
> }
public class LockTest {
public static void main(String[] args) {
Window w = new Window();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
synchronized与lock的异同:
相同:二者可以解决线程安全问题
不同:synchronized机制在执行完相应的同步代码以后,自动释放同步监视器,而lock需要手动释放
采用同步方式:
好处:解决了线程的安全问题。
局限性:操作同步代码时,只能有一个线程参与,其他线程等待。相当于一个单线程的过程,效率低