个人备案网站可以做淘宝客,it网站开发,网站开发与app开发原理,店铺设计软件线程的安全问题与线程的同步机制
以火车站买票的问题来举例。假设火车站有100张票#xff0c;分三个窗口售卖这一百张票。
分别用继承Thread类和实现Runnable接口的方式#xff1a;
实现Runnable接口#xff1a;
public class WindowTest
{public static void main(Stri…线程的安全问题与线程的同步机制
以火车站买票的问题来举例。假设火车站有100张票分三个窗口售卖这一百张票。
分别用继承Thread类和实现Runnable接口的方式
实现Runnable接口
public class WindowTest
{public static void main(String[] args){//线程的安全问题SaleTicket ss new SaleTicket();Thread t1 new Thread(ss,窗口一);Thread t2 new Thread(ss,窗口二);Thread t3 new Thread(ss,窗口三);t1.start();t2.start();t3.start();//这种情况下会出现重票错票的问题}
}
class SaleTicket implements Runnable
{private int ticket 100;public int getTicket(){return ticket;}Overridepublic void run(){while(true){if(ticket 0){try{Thread.sleep(10);} catch (InterruptedException e){e.getStackTrace();}System.out.println(Thread.currentThread().getName() 卖出一张票票号为 ticket);ticket--;}else{break;}}}
}
继承Thread类
public class WindowTest2
{public static void main(String[] args){//线程的安全问题Window w1 new Window(窗口一);Window w2 new Window(窗口二);Window w3 new Window(窗口三);w1.start();w2.start();w3.start();//同样会出现重票错票的问题}
}class Window extends Thread
{public Window(){}public Window(String name){super(name);}private static int ticket 100;Overridepublic void run(){while(true){if(ticket 0){try{Thread.sleep(10);} catch (InterruptedException e){e.getStackTrace();}System.out.println(Thread.currentThread().getName() 卖出一张票票号为 ticket);ticket--;}else{break;}}}
}
分别运行可以发现两中方式卖票时都会出现重票错票的问题。
出现这种现象的原因
例如线程一窗口一操作ticket的过程中尚未结束的情况下其他的线程也参与进来对ticket进行操作。
要解决这种问题必须保证一个线程假设是线程一在操作ticket的过程中其他线程必须等待直到线程一操作ticket结束以后其他线程才可以进来继续操作ticket。 要解决线程的安全问题就要使用线程的同步机制。
方式一同步代码块
格式
synchronized(同步监视器{
//需要被同步的代码
//……
}
说明
·需要被同步的代码即为操作共享数据的代码。
·共享数据即多个线程都需要操作的数据。
·需要被同步的代码在被synchronized包裹以后就使得一个线程在操作这些代码的过程中其他线程必须等待。
·同步监视器俗称锁哪个线程获取了锁哪个线程就能执行需要被同步的代码。
·同步监视器可以使用任何一个类的对象充当。多个线程这些线程要执行同一段需要被同步的代码必须共用同一个同步监视器。
同步监视器类似于交通信号灯多个线程共用同一个同步监视器即多个线程看同一个交通信号灯多个线程都是只有获取到这个锁之后才能执行相应需要被执行的代码
·实现Runnable的方式创建线程的方式同步监视器使用this最方便继承Thread类的方式创建线程的方式使用 当前类名.class 最方便。 使用同步代码块后的卖票的代码如下
实现Runnable接口
public class WindowTest
{public static void main(String[] args){//用同步代码块线程的安全问题SaleTicket ss new SaleTicket();Thread t1 new Thread(ss,窗口一);Thread t2 new Thread(ss,窗口二);Thread t3 new Thread(ss,窗口三);t1.start();t2.start();t3.start();//这种情况下会出现重票错票的问题}
}
class SaleTicket implements Runnable
{private int ticket 100;//Object o new Object();public int getTicket(){return ticket;}Overridepublic void run(){while(true){try{Thread.sleep(10);} catch (InterruptedException e){e.getStackTrace();}//synchronized(o){//如果多个线程要使用同一段同步的代码同步监视器中的对象要保证这些线程使用同一个锁。synchronized(this)//同步监视器中可以放任何一个唯一对象只要保证唯一。一般情况下放this最方便。{if(ticket 0){try{Thread.sleep(10);//调用sleep代码只是为了更好的显示同步代码块时轮流买票的情况。} catch (InterruptedException e){e.getStackTrace();}System.out.println(Thread.currentThread().getName() 卖出一张票票号为 ticket);ticket--;}else{break;}}}}
}
继承Thread类
public class WindowTest2
{public static void main(String[] args){//线程的安全问题Window w1 new Window(窗口一);Window w2 new Window(窗口二);Window w3 new Window(窗口三);w1.start();w2.start();w3.start();//同样会出现重票错票的问题}
}class Window extends Thread
{public Window(){}public Window(String name){super(name);}private static int ticket 100;//static Object oo new Object();Overridepublic void run(){while(true){//synchronized (this)//w1,w2,w3使用的并非同一个同步监视器相当于三个线程看的“交通信号灯”不是同一个。//synchronized (oo)synchronized (Window.class)//继承Thread类的方式同步监视器建议使用 当前类名.class{if(ticket 0){try{Thread.sleep(100);} catch (InterruptedException e){e.getStackTrace();}System.out.println(Thread.currentThread().getName() 卖出一张票票号为 ticket);ticket--;}else{break;}}}}
} 方式二同步方法
如果操作共享数据的代码完整地声明在了一个方法中那么我们就可以将此方法声明为同步方法。在方法声明中加上synchronized
·非静态的同步方法默认同步监视器为this
·静态的同步方法默认同步监视器是 当前类名.class 。
具体代码如下
实现Runnable接口
public class WindowTest
{public static void main(String[] args){//线程的安全问题//使用同步方法解决实现Runnable接口的方式的线程安全问题SaleTicket ss new SaleTicket();Thread t1 new Thread(ss,窗口一);Thread t2 new Thread(ss,窗口二);Thread t3 new Thread(ss,窗口三);t1.start();t2.start();t3.start();}
}
class SaleTicket implements Runnable
{private int ticket 100;public int getTicket(){return ticket;}Overridepublic void run(){boolean b true;//判断ticket是否大于0while(b){show(b);}}public synchronized void show(boolean b)//此时的同步监视器是this{if(ticket 0){try{Thread.sleep(10);} catch (InterruptedException e){e.getStackTrace();}System.out.println(Thread.currentThread().getName() 卖出一张票票号为 ticket);ticket--;}else{b false;}}
}
继承Thread类
public class WindowTest2
{public static void main(String[] args){//线程的安全问题Window w1 new Window(窗口一);Window w2 new Window(窗口二);Window w3 new Window(窗口三);w1.start();w2.start();w3.start();}
}class Window extends Thread
{public Window(){}public Window(String name){super(name);}private static int ticket 100;Overridepublic void run(){boolean b true;while(b){show(b);}}//public synchronized void show(boolean b)//同步监视器还是默认为this而this在这里三个不同的线程过来时分别指三个对象不能解决问题public static synchronized void show(boolean b)//此时同步监视器当前类名.class//注意不是使用静态的情况不要使用此方式应该继续使用同步代码块的方式{if(ticket 0){try{Thread.sleep(10);} catch (InterruptedException e){e.getStackTrace();}System.out.println(Thread.currentThread().getName() 卖出一张票票号为 ticket);ticket--;}else{b false;}}
}
synchronized关键字
好处解决了线程安全问题。
弊端在操作共享数据指多线程其实是串行执行的意味着性能低。