1.什么是线程安全问题?
从某个线程开始访问到访问结束的整个过程,如果有一个访问对象被其他线程修改,那么对于当前线程而言就发生了线程安全问题;
如果在整个访问过程中,无一对象被其他线程修改,就是线程安全的。
2.线程安全问题产生的根本原因
首先是多线程环境,即同时存在有多个操作者,单线程环境不存在线程安全问题。在单线程环境下,任何操作包括修改操作都是操作者自己发出的,
操作者发出操作时不仅有明确的目的,而且意识到操作的影响。
多个操作者(线程)必须操作同一个对象,只有多个操作者同时操作一个对象,行为的影响才能立即传递到其他操作者。
多个操作者(线程)对同一对象的操作必须包含修改操作,共同读取不存在线程安全问题,因为对象不被修改,未发生变化,不能产生影响。
综上可知,线程安全问题产生的根本原因是共享数据存在被并发修改的可能,即一个线程读取时,允许另一个线程修改
看实例:模拟火车站售票窗口,开启三个窗口售票,总票数为100张
实例一:
package com.yyx.test; //模拟火车站售票窗口,开启三个窗口售票,总票数为100张 //存在线程的安全问题 class Window extends Thread { static int ticket = 100; public void run() { while (true) { if (ticket > 0) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "售票,票号为:" + ticket--); } else { break; } } } } public class TestWindow { public static void main(String[] args) { Window w1 = new Window(); Window w2 = new Window(); Window w3 = new Window(); w1.setName("窗口1"); w2.setName("窗口2"); w3.setName("窗口3"); w1.start(); w2.start(); w3.start(); } }
实例二:
package com.yyx.test; //使用实现Runnable接口的方式,售票 /* * 此程序存在线程的安全问题:打印车票时,会出现重票、错票 */ class Window1 implements Runnable { int ticket = 100; public void run() { while (true) { if (ticket > 0) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "售票,票号为:" + ticket--); } else { break; } } } } public class TestWindow1 { 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(); } }
3.线程安全的解决
必须让一个线程操作共享数据完毕以后,其它线程才有机会参与共享数据的操作
1)线程的同步机制 synchronized
同步代码块
package com.yyx.test; /* 同步代码块 * synchronized(同步监视器){ * //需要被同步的代码块(即为操作共享数据的代码) * } * 1.共享数据:多个线程共同操作的同一个数据(变量) * 2.同步监视器:由一个类的对象来充当。哪个线程获取此监视器,谁就执行大括号里被同步的代码。俗称:锁 * 要求:所有的线程必须共用同一把锁! * 注:在实现的方式中,考虑同步的话,可以使用this来充当锁。但是在继承的方式中,慎用this! */ class Window implements Runnable { int ticket = 50;// 共享数据 public void run() { while (true) { // this表示当前对象,本题中即为w synchronized (this) { if (ticket > 0) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "售票,票号为:" + ticket--); } } } } } public class TestWindow { 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(); } }
同步方法
package com.yyx.test; /* * 同步方法 * 将操作共享数据的方法声明为synchronized。即此方法为同步方法,能够保证当其中一个线程执行 * 此方法时,其它线程在外等待直至此线程执行完此方法 */ class Window implements Runnable { int ticket = 50;// 共享数据 public void run() { while (true) { show(); } } public synchronized void show() { if (ticket > 0) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "售票,票号为:" + ticket--); } } } public class TestWindow { 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(); } }
原文地址:https://www.cnblogs.com/xianya/p/9193353.html
时间: 2024-10-30 07:35:15