【我的Java笔记】多线程安全问题 & 同步机制

来源:互联网 发布:知乎的影响力 编辑:程序博客网 时间:2024/06/01 09:00

目前所学过安全的类:

(1)Vector (多用于合并流)

(2)StringBuffer

(3)Hashtable

(4)Collections类中的:public static <T> List<T> synchronizedList(List<T> list)返回指定列表支持的同步(线程安全的)列表






1.检验多线程是否安全的标准:

(1)当前是否为多线程环境

(2)多线程环境中是否有共享数据

(3)是否有多条语句对共享数据进行操作


2.解决多线程不安全的方法(同步代码块):

将多条语句对共享数据的操作进行更改:使用同步代码块将对共享数据操作的代码包起来


格式:

(1)在成员位置声明同步锁对象(任意对象都可以)

(2)Synchronized(同步锁对象){

  // 对共享数据进行操作的多条语句

     }


3.同步锁对象要求

(1)可以是任意类(Object类以及任意自定义对象)

(2)同步方法:将Synchronized关键字至于方法声明,变成同步方法

①非静态方法:可以将Synchronized关键字加入到方法声明上,变成同步方法(同步锁对象为:this

②静态方法:若同步方法为静态方法,需通过反射获取锁对象(锁对象为:当前类名.class



4.同步机制解决线程安全问题的弊端

(1)执行效率低(每一个线程抢到CPU的执行权时别的线程无法进入)

(2)容易出现死锁现象(两个或两个以上的线程出现互相等待的情况)









例:模拟电影院售票,一共有100张票,三个窗口同时开售

注:加入延迟操作和线程的随机性会导致线程的安全问题,出现一张票被卖多次和出现负票的情况

// 测试类public class SellTicketTest {public static void main(String[] args) {// 创建SellTicket对象SellTicket st = new SellTicket();// 创建线程Thread t1 = new Thread(st, "窗口1");Thread t2 = new Thread(st, "窗口2");Thread t3 = new Thread(st, "窗口3");// 启动线程t1.start();t2.start();t3.start();}}

// 自定义定义线程类public class SellTicket implements Runnable {// 定义共有数据100张票private static int tickets = 100;// 设置同步锁对象Object obj = new Object();@Overridepublic void run() {// 假设一直有票while(true) {// 同步代码块:设置锁对象,一条线程进来之后会“关门”,别的线程不会再进来synchronized (obj) {// 让线程睡眠0.1秒try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}if (tickets > 0) {System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");}}}}}






非同步方法:


// 自定义定义线程类public class SellTicket implements Runnable {// 定义共有数据100张票private static int tickets = 100;// 定义一个变量private int i;@Overridepublic void run() {// 假设一直有票while (true) {if (i % 2 == 0) {// 非静态方法this代表锁对象synchronized (this) {// 让线程睡眠0.1秒try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}if (tickets > 0) {System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");}}} else {sellTicket();}}}// 非静态同步方法private synchronized void sellTicket() {// 让线程睡眠0.1秒try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}if (tickets > 0) {System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");}}}







静态同步方法:


// 自定义定义线程类public class SellTicket implements Runnable {// 定义共有数据100张票private static int tickets = 100;// 定义一个变量private int i;@Overridepublic void run() {// 假设一直有票while (true) {if (i % 2 == 0) {// 静态方法SellTicket.class代表锁对象synchronized (SellTicket.class) {// 让线程睡眠0.1秒try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}if (tickets > 0) {System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");}}} else {sellTicket();}i++;}}// 静态同步方法private static synchronized void sellTicket() {// 让线程睡眠0.1秒try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}if (tickets > 0) {System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");}}}













同步机制可能会出现的死锁现象:

// 测试类public class DieLockTest {public static void main(String[] args) {// 创建线程类对象DieLock dl1 = new DieLock(true);DieLock dl2 = new DieLock(false);// 启动线程dl1.start();dl2.start();}}// 自定义锁class MyLock {//创建两把锁对象public static final Object objA = new Object() ;public static final Object objB = new Object() ;}// 自定义线程类class DieLock extends Thread {//定义一个成员变量private boolean flag ;public DieLock(boolean flag){this.flag = flag ;}@Overridepublic void run() {//dl1,dl2线程if(flag){synchronized(MyLock.objA){System.out.println("if objA");synchronized (MyLock.objB) {System.out.println("if objB");}}//代码执行完毕,objA锁相当于才能被释放掉}else {//dl2synchronized (MyLock.objB) {System.out.println("else objB");synchronized(MyLock.objA){System.out.println("else objA");}}}}}


运行后会出现这样的情况:


由于两个线程之间出现了互相等待的情况,所以导致了死锁。


而正常情况是这样:


由于线程的运行具有随机性,所以多运行几次后也会出现理想的情况











原创粉丝点击