Java多线程Thread的安全及解决机制

Posted guhun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java多线程Thread的安全及解决机制相关的知识,希望对你有一定的参考价值。

        线程安全问题存在的原因?由于一个线程在操作共享数据过程中,未执行完毕的情况下,另外的线程参与进来,导致共享数据存在了安全问题

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();
            }
              
      }
  }
}

 




























































以上是关于Java多线程Thread的安全及解决机制的主要内容,如果未能解决你的问题,请参考以下文章

多线程Thread线程创建

Java多线程原理及Thread类的使用

java中线程安全问题及解决方法线程状态线程间通信(线程等待唤醒机制)

java中线程安全问题及解决方法线程状态线程间通信(线程等待唤醒机制)

什么是线程安全?

阶段1 语言基础+高级_1-3-Java语言高级_05-异常与多线程_第3节 线程同步机制_4_解决线程安全问题_同步代码块