线程安全问题:java
问题:当某个线程操做共享数据中,还没有操做完成时,其余线程参与进来,也操做共享数据。安全
解决方法:当一个线程在操做共享数据时,其余线程不能参与进来。即便操做中的线程出现阻塞,也不能改变。多线程
例子:建立三个窗口卖票,总票数为100张,使用实现Runable接口的方式。ide
JAVA 中经过同步机制 来解决线程的安全问题。this
方式1、同步代码块
synchronized(同步监视器){spa
//须要被同步的代码 线程
} code
说明:对象
1.操做共享数据的代码,为须要被同步的代码继承
2.共享数据,多个线程共同操做的变量,
3.同步监视器,俗称:锁。任何一个类的对象,均可以充当锁。
要求:多个线程必需要共用同一把锁。
补充:在实现Runable接口建立多线程的方式中,咱们能够考虑使用this 充当同步监视器
使用实现 Runable方式:
public class TheardWindow { public static void main(String[] args) { TicketWindow w1 = new TicketWindow(); Thread t1=new Thread(w1); Thread t2= new Thread(w1); Thread t3 =new Thread(w1); t1.setName("窗口1"); t2.setName("窗口2"); t3.setName("窗口3"); t1.start();; t2.start(); t3.start(); } } class TicketWindow implements Runnable{ private int ticket=100; final Object obj=new Object(); @Override public void run() { while (true){ //synchronized (obj){ synchronized (this){ if (ticket>0){ try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+":卖票 票号:"+ticket); ticket--; }else { break; } } } } }
使用继承方式:
package org.zhanghl; public class WindowB { public static void main(String[] args) { TicketRun t1= new TicketRun(); TicketRun t2= new TicketRun(); TicketRun t3 =new TicketRun(); t1.setName("窗口1"); t2.setName("窗口2"); t3.setName("窗口3"); t1.start(); t2.start(); t3.start(); } } class TicketRun extends Thread{ private static int ticket=100; private static Object obj=new Object(); @Override public void run() { while (true){ //synchronized (obj){ //能够用当前类作锁 synchronized (WindowB.class){ if (ticket>0){ try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+":卖票 票号:"+ticket); ticket--; }else { break; } } } } }
使用同步代码块 解决继承Thread类的方式的线程安全问题
注意:
1. 共享数据与锁对象都应是惟一的,这里为static.
2. 用继承Thread类建立多线程的方式时,慎用this充当同步监视器,考虑使用当前类对象作锁。Class只会加载一次。
3. 同步的方式,优势是解决了线程的安全问题,
缺点是操做同步代码时,只能由一个线程参与,其余线程等待。先当与是一个线程的过程,效率低。
方式2、同步方法
若是操做共享数据的代码完整的声明在一个方法中。咱们不妨将此方法声明同步的
package org.zhanghl; /* * 使用同步方法解决实现Runable接口的线程安全问题 * */ class WindowC1 implements Runnable { private int ticket = 100; final Object obj = new Object(); @Override public void run() { while (true) { if (ticket > 0) { show(); } else { break; } } } /*同步监视器:this */ private synchronized void show() { if (ticket > 0) { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":卖票 票号:" + ticket); ticket--; } } } /* *使用同步方法处理继承Thread类的方式中的线程安全问题 * */ class WindowC2 extends Thread{ private static int ticket = 100; final Object obj = new Object(); @Override public void run() { while (true) { if (ticket > 0) { show(); } else { break; } } } /*同步监视器:this */ private static synchronized void show() { /*此时同步监视器是 WindowC2.class*/ //private synchronized void show() 此种方式是错误的 if (ticket > 0) { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":卖票 票号:" + ticket); ticket--; } } } public class WindowC { public static void impl_show(){ WindowC1 w1 = new WindowC1(); Thread t1 = new Thread(w1); Thread t2 = new Thread(w1); Thread t3 = new Thread(w1); t1.setName("窗口1"); t2.setName("窗口2"); t3.setName("窗口3"); t1.start(); t2.start(); t3.start(); } public static void extd_show(){ WindowC2 t1=new WindowC2(); WindowC2 t2=new WindowC2(); WindowC2 t3=new WindowC2(); t1.setName("窗口1"); t2.setName("窗口2"); t3.setName("窗口3"); t1.start(); t2.start(); t3.start(); } public static void main(String[] args) { /*实现方式,同步方法*/ //impl_show(); /*继承方式,同步方法*/ extd_show(); } }
总结:
1.同步方法仍然涉及到同步监视器,只是不须要咱们显示的声明。
2.非静态的同步方法,同步监视器是this.
静态的同步方法,同步监视器是 当前类自己。