线程安全问题存在的原因?由于一个线程在操作共享数据过程中,未执行完毕的情况下,另外的线程参与进来,导致共享数据存在了安全问题
package com.atguigu.java; public class TestWindow1 { public static void main(String[] args) { Window w = new Window(); Thread t1 = new Thread(w); Thread t2 = new Thread(w); t1.setName("窗口1"); t2.setName("窗口2"); t1.start(); t2.start(); } } class Window implements Runnable { int ticket = 50; public void run() { while (true) { if (ticket > 0) { try { Thread.currentThread().sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "售票,票号为:" + ticket--); } else { break; } } } }
运行结果中的一种:
窗口1售票,票号为:50
窗口2售票,票号为:49
窗口1售票,票号为:48
窗口2售票,票号为:47
窗口2售票,票号为:46
窗口1售票,票号为:45
窗口2售票,票号为:43
窗口1售票,票号为:44
窗口1售票,票号为:42
窗口2售票,票号为:41
窗口2售票,票号为:40
窗口1售票,票号为:39
窗口2售票,票号为:38
窗口1售票,票号为:38
窗口2售票,票号为:37
窗口1售票,票号为:36
窗口1售票,票号为:34
窗口2售票,票号为:35
窗口2售票,票号为:33
窗口1售票,票号为:32
窗口2售票,票号为:31
窗口1售票,票号为:31
窗口2售票,票号为:30
窗口1售票,票号为:29
窗口2售票,票号为:28
窗口1售票,票号为:27
窗口2售票,票号为:26
窗口1售票,票号为:26
窗口1售票,票号为:25
窗口2售票,票号为:24
窗口1售票,票号为:23
窗口2售票,票号为:22
窗口1售票,票号为:21
窗口2售票,票号为:20
窗口1售票,票号为:19
窗口2售票,票号为:19
窗口1售票,票号为:18
窗口2售票,票号为:17
窗口2售票,票号为:15
窗口1售票,票号为:16
窗口1售票,票号为:14
窗口2售票,票号为:13
窗口1售票,票号为:12
窗口2售票,票号为:11
窗口1售票,票号为:10
窗口2售票,票号为:9
窗口2售票,票号为:8
窗口1售票,票号为:8
窗口2售票,票号为:7
窗口1售票,票号为:6
窗口1售票,票号为:4
窗口2售票,票号为:5
窗口2售票,票号为:3
窗口1售票,票号为:2
窗口2售票,票号为:0
窗口1售票,票号为:1
可以发现运行结果中存在重票及票数为0的情况
如何来解决线程的安全问题?必须让一个线程操作共享数据完毕以后,其它线程才有机会参与共享数据的操作
Java实现线程的安全方式一:synchronized关键字,线程的同步
1)同步代码块
package com.atguigu.java;
//实现Runnable接口 class Window implements Runnable { int ticket = 50;// 共享数据 public void run() { while (true) {
//注:在实现的方式中,考虑同步的话,可以使用this来充当锁。但是在继承的方式中,慎用this! synchronized (this) {//this表示当前对象,本题中即为w if (ticket > 0) { try { Thread.currentThread().sleep(10); } 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); t1.setName("窗口1"); t2.setName("窗口2"); t1.start(); t2.start(); } }
package com.atguigu.java; //继承Thread类 class Window extends Thread { static int ticket = 100; static Object obj = new Object(); public void run() { while (true) { synchronized (obj) { if (ticket > 0) { try { Thread.currentThread().sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "售票,票号为:" + ticket--); } } } } } public class TestWindow { public static void main(String[] args) { Window w1 = new Window(); Window w2 = new Window(); w1.setName("窗口1"); w2.setName("窗口2"); w1.start(); w2.start(); } }
2)同步方法
package com.atguigu.java;
class Window implements Runnable { int ticket = 100;// 共享数据 public void run() { while (true) { show(); } } public synchronized void show() { if (ticket > 0) { try { Thread.currentThread().sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block 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); t1.setName("窗口1"); t2.setName("窗口2"); t1.start(); t2.start(); } }
Java实现线程的安全方式二:Lock
package com.yyx.thread; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class TestLock { public static void main(String[] args) { Window w = new Window(); Thread t1 = new Thread(w); Thread t2 = new Thread(w); t1.setName("窗口1"); t2.setName("窗口2"); t1.start(); t2.start(); } } //实现Runnable接口 class Window implements Runnable { int ticket = 100;// 共享数据 Lock lock=new ReentrantLock(); public void run() { while (true) { try { lock.lock(); if (ticket > 0) { try { Thread.currentThread().sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "售票,票号为:" + ticket--); } } catch (Exception ex) { }finally{ lock.unlock(); } } } }