多线程安全一实现多线程安全的3种方式

1、:为什么多线程并发是不安全的?
在操作系统中,线程是不拥有资源的,进程是拥有资源的。而线程是由进程创建的,一个进程可以创建多个线程,这些线程共享着进程中的资源。多线程并发之所以会不安全,就是因为线程不拥有资源,它们共享竞争着进程的资源,这样线程并发起来不安全,一般的解决方案便用锁,保证每时每刻一个资源最多只能被一个线程拥有。所以,当线程一起并发运行时,同时对一个数据进行修改,就可能会造成数据的不一致性,看下面的例子

假设一个简单的int字段被定义和初始化: 
  int counter = 0; 
  该counter字段在两个线程A和B之间共享。假设线程A、线程B同时对counter进行计算,递增运算: 
  counter ++; 
  那么计算的结果应该是 2 。但是真实的结果却是 1 ,这是因为:线程A得到的运算结果是1,线程B的运算结果也是1,
当线程A将结果写回到内存中的 count 后,线程B也将结果写回到内存中去,这就会把线程A的计算结果给覆盖了。

2、多线程并发不安全的原因已经知道,那么针对这个种情况,java中有两种解决思路:
给共享的资源加把锁,保证每个资源变量每时每刻至多被一个线程占用。
让线程也拥有资源,不用去共享进程中的资源。
3、如何实现多线程高并发安全?

  1. 多实例、或者是多副本(ThreadLocal):ThreadLocal可以为每个线程的维护一个私有的本地变量;
  2. 使用锁机制 synchronizelock方式:为资源加锁;
  3. 使用 java.util.concurrent 下面的类库:有JDK提供的线程安全的集合类
    下面介绍方式二,使用锁机制的示例
    继承Thread类,使用同步代码块
package com.exerse;
/*
*使用继承Thread类创建多线程
*使用同步代码块保证多线程安全
* synchronized(同步监视器){
*  //需要同步的代码
*  }
*  同步监视器,俗称锁,任何一个类的对象都可以充当锁,但要求必须要共同用一把锁
*  比如使用继承Thread类时就创建了三个对象,不是共用一把锁,不能采用this(当前对象),只能用
*   MyThread.class(当前类的class)或
*   private static Object obj=new Object();
*
 */
public class ThreadTest {
    public static void main(String[] args) {
        MyThread myThread1=new MyThread();//创建了三个对象
        MyThread myThread2=new MyThread();
        MyThread myThread3=new MyThread();
         myThread1.setName("窗口A");
         myThread2.setName("窗口B");
         myThread3.setName("窗口C");
         myThread1.start();
         myThread2.start();
         myThread3.start();

    }
}
class MyThread extends Thread{
    private static int ticket=100;//使用static作为3个对象共享一百张票
    @Override
    public void run() {
       while (true){
           synchronized (MyThread.class){
               if(ticket>0){
                   try {
                       sleep(100);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
                   System.out.println(Thread.currentThread().getName()+"卖票,票号"+ticket);
                   ticket--;
               }else{
                   break;
               }
           }
           }
    }
}

继承Thread类使用同步方法

package com.exerse;
/*
*使用继承Thread类创建多线程
 *使用同步方法保证多线程安全
 * 1 同步方法仍然涉及到同步监视器(锁),只是不需要我们显示的声明、
 * 2 非静态的同步方法,同步监视器为:this
 *    静态的同步方法,同步监视器为:当前类本身
 */
public class ThreadTest1 {
    public static void main(String[] args) {
        MyThread1 myThread11 = new MyThread1();
        MyThread1 myThread12 = new MyThread1();
        MyThread1 myThread13 = new MyThread1();
        myThread11.setName("窗口A");
        myThread12.setName("窗口B");
        myThread13.setName("窗口C");
        myThread11.start();
        myThread12.start();
        myThread13.start();
    }
}
class MyThread1 extends Thread {
    private static int ticket = 1000;//使用static作为3个对象共享一百张票
    @Override
    public void run() {
            while (true){
                show();
            }
        }
        private static synchronized void show(){//同步监视器为,(当前类本身MyThread1.class 注意建立三个对象,不唯一,要加static(静态方法)
              if (ticket>0){
                  try {
                      sleep(100);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  System.out.println(Thread.currentThread().getName() + "卖票,票号" + ticket);
                  ticket--;
              }
        }
    }

使用实现Runnable接口同步代码块

package com.exerse;
/*
*实现 Runnable 接口创建多线程
* 使用同步代码块保证多线程安全
 * synchronized(同步监视器){
 *  //需要同步的代码
 *  }
 *  同步监视器,俗称锁,任何一个类的对象都可以充当锁,但要求必须要共同用一把锁,此时
 *  借用this。
*
 */
public class RunnableTest {
    public static void main(String[] args) {
        Wind w = new Wind();//只创建一个对象
        Thread thread1 = new Thread(w);
        Thread thread2 = new Thread(w);
        Thread thread3 = new Thread(w);
        thread1.setName("窗口A");
        thread2.setName("窗口B");
        thread3.setName("窗口C");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}
class Wind implements Runnable {
    private int ticket = 1000;
    @Override
    public void run() {
        while (true) {
            synchronized (this){
                if (ticket > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "卖票,票号" + ticket);
                    ticket--;
                }else{
                    break;
                }
            }
        }
            }
}

使用实现Runnable接口,使用同步方法

package com.exerse;
/*
 *实现 Runnable 接口创建多线程
 *使用同步方法实现多线程安全
 */
public class RunnableTest1 {
    public static void main(String[] args) {
        Wind1 w = new Wind1();
        Thread thread1 = new Thread(w);
        Thread thread2 = new Thread(w);
        Thread thread3 = new Thread(w);
        thread1.setName("窗口A");
        thread2.setName("窗口B");
        thread3.setName("窗口C");
        thread1.start();
        thread3.start();
        thread2.start();
    }
}
class Wind1 implements Runnable {
    private int ticket = 1000;
    @Override
    public void run() {
        while (true) {
            show();
        }
    }
    private synchronized void show() {//此时非静态方法的同步监视器为this
        if (ticket > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "卖票,票号" + ticket);
            ticket--;
        }
    }
}

实现Runnable接口,通过lock锁保证多线程安全

package com.exerse;
import java.util.concurrent.locks.ReentrantLock;
/*
 *实现 Runnable 接口创建多线程
 * 使用 方式三手动Lock锁保证多线程安全(通过显示定义锁来实现同步)
 *解决线程安全的方式三:Lock锁
 * 面试:关于synchronized与lock锁区别
 * 相同:二者都可以解决线程安全的问题
 * 不同:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
 *             Lock需要手动的开启同步(lock),同时结束同步也需要手动的实现(unlock())
 */
public class RunnableTest2 {
    public static void main(String[] args) {
        Wind2 w = new Wind2();//只创建一个对象
        Thread thread1 = new Thread(w);
        Thread thread2 = new Thread(w);
        Thread thread3 = new Thread(w);
        thread1.setName("窗口A1");
        thread2.setName("窗口B1");
        thread3.setName("窗口C1");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}
class Wind2 implements Runnable {
    private int ticket = 100;
    private ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true) {
          // private ReentrantLock lock = new ReentrantLock();错误的每次执行run就创建一个Lock对象,必须使用同一个lock
            lock.lock();
            try {
                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();
            }
        }
    }
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值