JAVA线程安全问题

 卖票的过程中出现了线程安全问题,有重票和错票的问题。
 为什么会出现这个问题?
 当某个线程在操作卖票的过程中尚未完成卖票操作,其他线程也参与进来操作卖票,就会出现线程安全问题,(主要是共享数据(nums)的问题)

 怎么去解决这个问题?
 当某个线程在操作卖票的时候,其他线程不能参与进来,直到这个线程操作完成(即使这个线程阻塞了也得等这个线程执行完成),其他线程才能进来。

 java里面我们通过同步机制来解决线程安全问题
 方式一:同步代码块
 synchronized(同步监视器){

 }
 说明:
 (1)操作共享数据的代码即为需要被同步的代码,不能包含的代码太多会浪费资源,降低效率。
 (2)共享数据:多个线程共同操作的变量。比如卖票里面的nums
 (3)同步监视器俗称锁,任何一个类的对象都可以充当锁,但是前提是这个对象对于多个线程来说都是唯一的。
补充:在实现Runnable接口创建线程对象的方式中,我们可以考虑使用this充当同步监视器,如果是使用继承Thread类的方式
 新建的线程对象则慎用This(可能会有多个对象),可以使用类.class来当同步监视器。
package com.jinjian;

/**
 卖票的过程中出现了线程安全问题,有重票和错票的问题。
 为什么会出现这个问题?
 当某个线程在操作卖票的过程中尚未完成卖票操作,其他线程也参与进来操作卖票,就会出现线程安全问题,(主要是共享数据(nums)的问题)

 怎么去解决这个问题?
 当某个线程在操作卖票的时候,其他线程不能参与进来,直到这个线程操作完成(即使这个线程阻塞了也得等这个线程执行完成),其他线程才能进来。

 java里面我们通过同步机制来解决线程安全问题
 方式一:同步代码块
 synchronized(同步监视器){

 }
 说明:
 (1)操作共享数据的代码即为需要被同步的代码,不能包含的代码太多会浪费资源,降低效率。
 (2)共享数据:多个线程共同操作的变量。比如卖票里面的nums
 (3)同步监视器俗称锁,任何一个类的对象都可以充当锁,但是前提是这个对象对于多个线程来说都是唯一的。
补充:在实现Runnable接口创建线程对象的方式中,我们可以考虑使用this充当同步监视器,如果是使用继承Thread类的方式
 新建的线程对象则慎用This(可能会有多个对象),可以使用类.class来当同步监视器。


 方式二:同步方法


 */
class Window implements Runnable{
     private int nums = 100;
     Object object = new Object();
    @Override
    public void run() {
        while (true) {
//            synchronized (object) {
            synchronized (this) {
                if (nums > 0) {
                    System.out.println(Thread.currentThread().getName() + "卖票,票号为: " + nums);
                    nums--;
                } else {
                    break;
                }
            }
        }
    }
}

class Window1 extends Thread{
    private static int nums = 100;
    @Override
    public void run() {
        while (true) {
            synchronized (Window1.class) {
                if (nums > 0) {
                    System.out.println(Thread.currentThread().getName() + "卖票,票号为: " + nums);
                    nums--;
                } else {
                    break;
                }
            }
        }
    }
}
public class ThreadTest03 {
    public static void main(String[] args) {
        //这样相当于新建了三个Window1对象,要想共享nums变量就需要用static修饰nums。
        //而且不能用this作为对象监视器,三个对象有三个this不能保证对象监视器的唯一
        //可以使用Window1.class来作为对象监视器
        Window1 w1 = new Window1();
        Window1 w2 = new Window1();
        Window1 w3 = new Window1();

        w1.start();
        w2.start();
        w3.start();



        //使用实现Runnable接口来新建线程对象
//        Window window = new Window();
        //这样nums就是共用的变量,不需要加static,因为只创建了一个Window对象,下面三个线程对象共用一个Window对象

//        Thread t1 = new Thread(window);
//        Thread t2 = new Thread(window);
//        Thread t3 = new Thread(window);
//
//        t1.start();
//        t2.start();
//        t3.start();
    }
}
使用同步方法来解决线程安全问题,
 (1)同步方法依然涉及到同步监视器,只是不需要我们说明
 (2)非静态的同步方法的监视器默认是this,静态方法的同步监视器是当前类本身
package com.jinjian;

/**
使用同步方法来解决线程安全问题,
 (1)同步方法依然涉及到同步监视器,只是不需要我们说明
 (2)非静态的同步方法的监视器默认是this,静态方法的同步监视器是当前类本身
 */

class Window2 implements Runnable{
    private int nums = 100;

    @Override
    public void run() {
        while (true) {
            seal();
        }
    }

    private synchronized void seal(){//这样默认的同步监视器是this
        if (nums > 0) {
            System.out.println(Thread.currentThread().getName() + "卖票,票号为: " + nums);
            nums--;
        }
    }
}

class Window3 extends Thread{
    private static int nums = 100;
    @Override
    public void run() {
        while (true) {
//            synchronized (Window1.class) {
            seal();
            //}
        }
    }

//    private synchronized void seal(){//这样是不行的,This不唯一有三个对象
    private static synchronized void seal(){//这样默认的同步监视器是Window3.class
        if (nums > 0) {
            System.out.println(Thread.currentThread().getName() + "卖票,票号为: " + nums);
            nums--;
        }
    }
}

public class ThreadTest04 {
    public static void main(String[] args) {

//        Window2 window2 = new Window2();
//
//        Thread t1 = new Thread(window2);
//        Thread t2 = new Thread(window2);
//        Thread t3 = new Thread(window2);
//
//        t1.start();
//        t2.start();
//        t3.start();

        Window3 w1 = new Window3();
        Window3 w2 = new Window3();
        Window3 w3 = new Window3();

        w1.start();
        w2.start();
        w3.start();
    }




}

死锁的情况:

演示程序死锁的问题
 (1)不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的资源,就形成了线程的死锁
 (2)出现死锁后不会出现异常,不会出现提示,只是所有线程都处于阻塞状态,无法继续。所以要避免出现死锁
(3)a线程等待拿到s2的锁,执行完成后释放s1的锁。b线程等待拿到s1的锁,执行完成后释放s2的锁。这样就形成了死锁
package com.jinjian;

import javax.swing.plaf.synth.SynthOptionPaneUI;

/**
演示程序死锁的问题
 (1)不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的资源,就形成了线程的死锁
 (2)出现死锁后不会出现异常,不会出现提示,只是所有线程都处于阻塞状态,无法继续。所以要避免出现死锁
 (3)a线程等待拿到s2的锁,执行完成后释放s1的锁。b线程等待拿到s1的锁,执行完成后释放s2的锁。这样就形成了死锁

 */
public class TestThreadDeadLock {
    public static void main(String[] args) {
        StringBuffer s1 = new StringBuffer();
        StringBuffer s2 = new StringBuffer();

        new Thread(){
            @Override
            public void run(){
                synchronized (s1){
                    s1.append('a');
                    s2.append('1');
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    synchronized (s2){
                        s1.append('b');
                        s2.append('2');
                        System.out.println(s1);
                        System.out.println(s2);
                    }
                }
            }
        }.start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (s2){
                    s1.append('C');
                    s2.append('3');
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (s1){
                        s1.append('D');
                        s2.append('4');
                        System.out.println(s1);
                        System.out.println(s2);
                    }
                }
            }
        }).start();
    }
}
解决线程安全问题方式三:使用Lock-----------JDK5新增

 面试题:
 Lock和synchronized之间的异同?
 相同:二者都可以解决线程安全问题
 不同:synchronized机制在执行完相应代码后会自动释放同步监视器,
 Lock需要手动启动同步(lock),和结束同步也需要手动释放锁(unlock)

 几张同步方式的优先使用顺序
 Lock-----synchronized同步代码块(已经进入了方法体,分配了相应的资源)------synchronized方法(在方法体之外)
package com.jinjian;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
解决线程安全问题方式三:使用Lock-----------JDK5新增

 面试题:
 Lock和synchronized之间的异同?
 相同:二者都可以解决线程安全问题
 不同:synchronized机制在执行完相应代码后会自动释放同步监视器,
 Lock需要手动启动同步(lock),和结束同步也需要手动释放锁(unlock)

 几张同步方式的优先使用顺序
 Lock-----synchronized同步代码块(已经进入了方法体,分配了相应的资源)------synchronized方法(在方法体之外)
 */
class Window5 implements Runnable{
    private int nums = 100;
    //实例化ReentrantLock
    private ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true) {
            try {
                //上锁,在锁之间的代码块会被锁住,和synchronized包住的代码块一样,实现线程安全
                lock.lock();
                if (nums > 0) {
                    System.out.println(Thread.currentThread().getName() + "卖票,票号为: " + nums);
                    nums--;
                } else {
                    break;
                }
            }finally {
                //解锁
                lock.unlock();
            }
        }
    }
}
public class TestReentrantLock {
    public static void main(String[] args) {
        Window5 window5 = new Window5();

        Thread t1 = new Thread(window5);
        Thread t2 = new Thread(window5);
        Thread t3 = new Thread(window5);

        t1.start();
        t2.start();
        t3.start();
    }
}

你可能感兴趣的:(1024程序员节,java,开发语言)