多线程线程安全的三种解决方案
前言
线程安全是多线程编程时的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。
1.使用同步代码块解决 extends Thread线程安全问题
代码如下(示例):
/**
* 创建三个窗口卖票,总票数为100张
*
* 存在线程不安全问题
*
* 实现多线程的方拾贰:实现Runnable接口
* 1.创建一个实现了Runnable接口的类
* 2.实现类趋势线Runnable中的抽象方法:run()
* 3.创建实现类的对象
* 4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
* 5.通过Thread类的对象调用start()
*
* 两种方式比较
* 优先选择Runnable接口的方式
* 1.实现的方式没有类的但继承的限制
* 实现的方式更适合来处理多个线程有共享数据
* 【相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run方法中
*
* 问题:买票过程中,出现了重票,错票--》出现了线程的安全问题
* 原因:当某个线程操作车票的过程中,尚未完成操作完成时,其他线程参与进来
* 解决:
* 方式一:同步代码块
* synchronized(同步监视器){
*
* }
* 说明:1.操作共享数据的代码,极为需要被同步的代码
* 2.共享数据:多个线程共同操作的变量。比如:ticket就是共享数据
* 3.同步监视器,俗称:锁。任何一个类的独享,都可以充当锁
*
* 同步的方式解决了线程的安全问题
* 操作同步代码是,只能有一个现成参与,其他线程等待相当于是一个单线程,效率低
*
*
* 在继承Thread类创建多线程的方式中,慎用this充当同步监视器,考虑用当前类来充当同步监视器
*
*/
//使用同步代码块
class Window extends Thread{
private static int ticket = 1000;
static Object obj=new Object();
@Override
public void run() {
while (true){
//正确的
synchronized (obj) {//正确的
// synchronized (this){错误的
// synchronized (Window.class){//正确的,window2只会加载一次
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + ":卖票,票号为:" + ticket);
ticket--;
} else {
break;
}
}
}
}
}
public class 线程安全 {
public static void main(String[] args) {
Window w1 = new Window();
Window w2=new Window();
Window w3 = new Window();
w1.setName("窗口一");
w2.setName("窗口二");
w3.setName("窗口三");
w1.start();
w2.start();
w3.start();
}
}
2.使用同步方法解决 extends Thread线程安全问题
代码如下(示例):
//使用同步方法
/*
關於同步方法的總結:
1.同步方法任然涉及到同步監視器
2.非静态的同步方法,同步监视器是:this
静态的同步方法,同步监视器是:当前类本身
*/
class Window2s extends Thread{
private static int ticket = 100;
@Override
public void run() {
while (true){
show();
}
}
public static synchronized void show(){//同步監視器:Window2s.class
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
}
}
}
public class Window2 {
public static void main(String[] args) {
Window2s w1 = new Window2s();
Window2s w2 = new Window2s();
Window2s w3 = new Window2s();
w1.setName("窗口一");
w2.setName("窗口二");
w3.setName("窗口三");
w1.start();
w2.start();
w3.start();
}
}
3.使用同步代码块解决 implements Runnable线程安全问题
代码如下(示例):
class MThread implements Runnable {
private int ticket = 100;
// Object obj = new Object();
@Override
public void run() {
while (true) {
// 方式一 synchronized (obj) {
synchronized (this){//方式二
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
} else {
break;
}
}
}
}
}
public class ThreadTest2 {
public static void main(String[] args) {
MThread m =new MThread();
Thread t1=new Thread(m);
Thread t2 = new Thread(m);
Thread t3 = new Thread(m);
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();
}
}
4.使用同步方法解决 Runnable接口线程安全问题
代码如下(示例):
/**
* 创建三个窗口卖票,总票数为100张使用Runnable接口的方式
*
*使用同步方法解决Runnable接口的线程安全问题
*/
class window1 implements Runnable{
private int ticket = 100;
@Override
public void run() {
while (true){
show();
}
}
public synchronized void show(){//同步監視器:this
if (ticket>0) {
System.out.println(Thread.currentThread().getName() + ":" + ticket);
ticket--;
}
}
}
public class WindowTest1 {
public static void main(String[] args) {
window1 w1=new window1();
Thread t1 = new Thread(w1);
Thread t2 = new Thread(w1);
Thread t3 = new Thread(w1);
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();
}
}
5.使用lock解决线程安全问题
代码如下(示例):
/**
* 解决线程安全问题的方式三:Lock锁----JDK5.0新增
*
* 1.面试题:synchronized与lock的异同
* 相同:都可以解决线程安全的问题
* 不同:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
* Lock需要手动的启动同步(lock),同时结束同步也需要手动的实现(unlock)
*
*
* 3.面试题:如何解决线程安全问题?有几种方式
*/
class Window1 implements Runnable{
private int ticket=1000;
//1.实例化lock
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true){
try {
//1.调用锁定方法lock()
lock.lock();
if (ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + ticket);
ticket--;
}else{
break;
}
} finally {
//调用解锁方法
lock.unlock();
}
}
}
}
public class LockTest {
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();
}
}
5.使用lock解决线程安全问题
代码如下(示例):
## 附:使用同步机制将单例模式中的懒汉式改为线程安全的
<font color=#999AAA >代码如下(示例):
```c
/**
*
* 使用同步机制将单例模式中的懒汉式改为线程安全的
*/
public class BankTest {
}
class Bank{
private Bank(){}
private static Bank instance = null;
public static Bank getInstance(){
//方式一:效率稍差
// synchronized (Bank.class) {
// if (instance == null) {
// instance = new Bank();
// }
// return instance;
// }
//方拾贰:效率比较高
if (instance==null) {
synchronized (Bank.class) {
if (instance == null) {
instance = new Bank();
}
}
}
return instance;
}
}
博主刚开始学习,如有写的不正确的地方请指出