Java多线程-如何解决线程安全问题
方式一:同步代码块
* 关键字 * synchronized(同步监视器) * { * //需要被同步的代码 * } * 1.说明:操作共享数据的代码即为需被同步的代码 * 2.共享数据:多个线程共同操作的变量 * 3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。//Object obj=new Object(); * 要求:多个线程必须要共用同一把锁
方式二:同步方法
* 如果操作共享数据的代码完整声明在一个方法中,我们不妨将此方法声明为同步的。
* synchronized关键字
* public synchronized void show()
* 同步方法依然涉及同步监视器,只是不需要显示声明
* 非静态的同步方法,同步监视器是this
* 静态的同步方法,同步监视器是当前类本身
方式三:Lock锁
* 1.实例化ReentrantLock
* private ReentrantLock lock=new ReentrantLock();
* 2.调用锁定方法
* lock.lock()
* 3.调用解锁方法
* lock.unlock()
/** * 例子:创建三个窗口卖票,总票数100张,使用实现Runable接口的方式 *存在线程安全问题,待解决 * * 1.问题:卖票过程中,出现了重票、错票->线程安全问题 * 2.原因:当某个线程操作车票的过程中,尚未完成操作时,其他线程也参与进来操作车票 * 共享数据(共享信号量):车票数ticket * 3.解决:锁; * 4.在java中,通过同步机制来解决线程的安全问题 * * 方式一:同步代码块 * 关键字 * synchronized(同步监视器) * { * //需要被同步的代码 * } * 1.说明:操作共享数据的代码即为需被同步的代码 * 2.共享数据:多个线程共同操作的变量 * 3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。//Object obj=new Object(); * 要求:多个线程必须要共用同一把锁 * * * * 方法二:同步方法 * 如果操作共享数据的代码完整声明在一个方法中,我们不妨将此方法声明为同步的。 * synchronized关键字 * public synchronized void show() * 同步方法依然涉及同步监视器,只是不需要显示声明 * 非静态的同步方法,同步监视器是this * 静态的同步方法,同步监视器是当前类本身 * * * 5.同步的方式,解决了线程的安全问题。---好处 * 操作同步代码时只能有一个线程参与,其他线程等待,相当于是一个单线程的过程,效率低----局限性 * 死锁问题 * 6.同步监视器是否唯一 * * @author orz */ 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(Exception e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+":卖票,票号为:"+ticket); ticket--; } else { break; } } } } } public class WindowTest { 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("窗口一"); t2.setName("窗口二"); t3.setName("窗口三"); t1.start(); t2.start(); t3.start(); } }
/** * 线程同步 * *多线程共享变量 * * 例子:创建三个窗口卖票,总票数100张,使用继承Thread类的方式 *存在线程安全问题,待解决 * 使用同步代码块方法解决线程安全问题 * * * @author orz */ class Window2 extends Thread { //类变量,共用同一个变量 private static int ticket=100; //锁 private static Object obj=new Object(); @Override public void run() { while (true) { synchronized (obj) { if(ticket>0) { System.out.println(getName()+":卖票,票号为:"+ticket); ticket--; } else { break; } } } } } public class WindowTest2 { public static void main(String[] args) { Window2 w1=new Window2(); Window2 w2=new Window2(); Window2 w3=new Window2(); w1.setName("窗口一"); w2.setName("窗口二"); w3.setName("窗口三"); w1.start(); w2.start(); w3.start(); } }
/** * 使用同步方法来解决实现Runable接口的线程安全问题 * * @author orz */ class Window3 implements Runnable { private int ticket=100; public synchronized void show() { if(ticket>0) { try { Thread.sleep(10); } catch(Exception e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+":卖票,票号为:"+ticket); ticket--; } } @Override public void run() { while (true) { show(); } } } public class WindowTest3 { public static void main(String[] args) { Window3 w=new Window3(); Thread t1=new Thread(w); Thread t2=new Thread(w); Thread t3=new Thread(w); t1.setName("窗口一"); t2.setName("窗口二"); t3.setName("窗口三"); t1.start(); t2.start(); t3.start(); } }
/** * 使用同步方法来解决继承Thread类的线程安全问题 * @author orz */ class Window4 extends Thread { //类变量,共用同一个变量 private static int ticket=100; //同步监视器:WindowTest4.class //必须声明为静态方法 public static synchronized void show() { if(ticket>0) { System.out.println(Thread.currentThread().getName()+":卖票,票号为:"+ticket); ticket--; } } @Override public void run() { while (true) { show(); } } } public class WindowTest4 { public static void main(String[] args) { Window4 w1=new Window4(); Window4 w2=new Window4(); Window4 w3=new Window4(); w1.setName("窗口一"); w2.setName("窗口二"); w3.setName("窗口三"); w1.start(); w2.start(); w3.start(); } }
import java.util.concurrent.locks.ReentrantLock; /** * 解决线程安全问题的方式三:Lock锁 * * 1.实例化ReentrantLock * private ReentrantLock lock=new ReentrantLock(); * 2.调用锁定方法 * lock.lock() * 3.调用解锁方法 * lock.unlock(); * * * * 面试题:synchronized与lock异同 * 相同: * 不同: * synchronized机制执行完相应的同步代码之后,自动释放同步监视器 * Lock需要手动的启动同步(lock()),结束同步也需要手动实现(unlock()) * * * Lock、同步代码块、同步方法 * @author orz */ class Window5 implements Runnable{ private int ticket=100; /** * 1.实例化ReentrantLock */ private ReentrantLock lock=new ReentrantLock(); @Override public void run() { while (true) { //2.调用锁定方法 try{lock.lock(); if (ticket>0) { try { Thread.sleep(100); } catch (Exception e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+":售票,票号为:"+ticket); ticket--; } else { break; } } //3.调用解锁方法 finally { lock.unlock(); } } } } public class LockTest { public static void main(String[] args) { Window5 w=new Window5(); Thread t1=new Thread(w); Thread t2=new Thread(w); Thread t3=new Thread(w); t1.setName("窗口一"); t2.setName("窗口二"); t3.setName("窗口三"); t1.start(); t2.start(); t3.start(); } }