这里使用窗口售票的例子,每个窗口相当于不同的线程,当不考虑线程安全问题时,此时会出现下面的情况
窗口2 当前剩余票:3 窗口3 当前剩余票:1 窗口2 当前剩余票:0 窗口1 当前剩余票:-1
显然,-1张票是不符合现实意义的,多个线程出现安全问题的原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票。
也就是说如果多个线程没有共享变量(数据)时,也就不会存在安全问题!!线程并行的
如何解决:添加锁,当一个线程在操作时,其他线程不能参与进来。
使用同步机制解决安全问题方法如下。
方式一:同步代码块
1.使用接口的方式
格式:synchronized(同步监视器:obj){
//需要被同步的代码
}
说明:操作共享数据的代码,即为需要被同步的代码--->不能保护代码多了,也不能少了。
共享数据:多个线程共同操作的变量,比如本问题中的ticket。
同步监视器,俗称:锁,任何一个类的对象,都可以充当锁。
要求:多个线程必须要共用同一把锁。
补充:在实现runnable接口创建多线程的方式中,我们可以考虑this当同步监视器,在继承Thread类创建多线程的方式中,慎用this充当同步监视器,可以考虑当前类作为同步监视器。(不管用哪个,保证对象是唯一的)
下面例子是使用接口的方式解决线程安全问题
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;
}
}
}
}
}
public class windowTs {
public static void main(String[] args) {
window1 w1 = new window1();
Thread t1 = new Thread(w1);
Thread t2 = new Thread(w1);
Thread t3 = new Thread(w1);
t1.start();
t2.start();
t3.start();
}
}
2.使用继承Thread类的方式
使用同步代码块解决继承Thread类的方式的线程安全问题
class window2 extends Thread{
private static int ticket = 100;
private static Object obj = new Object();
@Override
public void run() {
while (true) {
//this 是windows2类创建的对象(w1,w2,w3)此时有多个对象
//所以此时是不安全的
// synchronized (this) 是错误的,
synchronized(window1.class){
//类也是对象
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ' ' + ticket);
ticket--;
} else {
break;
}
}
}
}
}
public class windowTs2 {
public static void main(String[] args) {
window2 w2 = new window2();
window2 w1 = new window2();
window2 w3 = new window2();
w1.start();
w2.start();
w3.start();
}
}
注意:不能保护代码多了,也不能少了
假如synchronized把while包进去了,那么就变成了单线程了!!!下例
synchronized(window1.class){
while (true){
//类也是对象
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ' ' + ticket);
ticket--;
} else {
break;
}
窗口2 当前票数:100 窗口2 当前票数:99 窗口2 当前票数:98 窗口2 当前票数:97 窗口2 当前票数:96 窗口2 当前票数:95可以看到变成了单线程!所以不能把包含多的代码块!!! 方法二 使用同步方法解决
1.使用接口的方式
private synchronized void show(){
需要同步的代码块
}
同步方法中不用手动声明锁,用的默认的:this
class window2 implements Runnable {
private static int ticket = 100;
@Override
public void run() {
while (true) {
show();
}
}
private synchronized void show() {
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ' ' + ticket);
ticket--;
}
}
}
public class windowTs2 {
public static void main(String[] args) {
window2 w2 = new window2();
window2 w1 = new window2();
window2 w3 = new window2();
Thread t1 = new Thread(w1);
Thread t2 = new Thread(w2);
Thread t3 = new Thread(w3);
t1.start();
t2.start();
t3.start();
}
}
2.使用继承Thread类的方式
class window1 extends Thread {
private static int ticket = 100;
// Object obj = new Object();
@Override
public void run() {
while (true){
show();
}
}
//同步监视器是:windows1.class这个类
private static synchronized void show() {
if (ticket >0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ' ' + ticket);
ticket--;
}
}
}
public class windowTs {
public static void main(String[] args) {
window1 w1 = new window1();
window1 w2 = new window1();
window1 w3 = new window1();
// Thread t1 = new Thread(w1);
// Thread t2 = new Thread(w1);
// Thread t3 = new Thread(w1);
w1.start();
w2.start();
w3.start();
}
}
使用同步方法仍涉及到同步监视器,只是不需要我们显式的声明。
非静态的同步方法,同步监视器是:this
静态的同步方法同步监视器是:当前类本身。



