Java多线程之脏读代码示例及处理

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

上篇文章说到了同步异步。那么在应用中同步异步的应用时候,设计程序时候,一定要注意业务的整体性。 也就是要注意上节学的资源的共享,则一定要同步。不然就会发生错误。比如经典的脏读(dirtyread)

脏读

概念: 对于对象的同步和异步的方法,我们在设计自己的程序时,一定要考虑问题的整体性,不然就会出现数据不一致的错误,很经典的就是脏读问题

package com.wkcto.intrinsiclock;

/**

* 脏读

* 出现读取属性值出现了一些意外, 读取的是中间值,而不是修改

之后 的值

* 出现脏读的原因是 对共享数据的修改 与对共享数据的读取不

同步

* 解决方法:

*

不仅对修改数据的代码块进行同步,还要对读取数据的代码

块同步

* Author: 老崔

*/

public class Test08 {

public static void main(String[] args) throws InterruptedException {

//开启子线程设置用户名和密码

PublicValue publicValue = new PublicValue();

SubThread t1 = new SubThread(publicValue);
t1.start();

//为了确定设置成功

Thread.sleep(100);

//在 main 线程中读取用户名,密码

publicValue.getValue();

}

//定义线程,设置用户名和密码

static class SubThread extends Thread{

private PublicValue publicValue;

public SubThread( PublicValue publicValue){

this.publicValue = publicValue;

}

@Override

public void run() {

publicValue.setValue("bjpowernode", "123");

}

}
static class PublicValue{

private String name = "wkcto";

private String pwd = "666";

public synchronized void getValue(){

System.out.println(Thread.currentThread().getName() + ",

getter -- name: " + name + ",--pwd: " + pwd);

}

public synchronized void setValue(String name, String pwd){

this.name = name;

try {

Thread.sleep(1000);

//模拟操作

name 属性需要一定时间

} catch (InterruptedException e) {

e.printStackTrace();

}

this.pwd = pwd;

System.out.println(Thread.currentThread().getName() + ",

setter --name:" + name + ", --pwd: " + pwd );

}
}

}
复制代码

线程出现异常会自动释放锁

package com.wkcto.intrinsiclock;

/**

* 同步过程中线程出现异常, 会自动释放锁对象

*

* Author: 老崔

*/

public class Test09 {

public static void main(String[] args) {

//先创建 Test01 对象,通过对象名调用 mm()方法

Test09 obj = new Test09();

//一个线程调用 m1()方法

new Thread(new Runnable() {

@Override
public void run() {

obj.m1();

//使用的锁对象是 Test06.class

}

}).start();

//另一个线程调用 sm2()方法

new Thread(new Runnable() {

@Override

public void run() {

Test09.sm2();

//使用的锁对象是 Test06.class

}

}).start();

}

//定义方法,打印 100 行字符串

public void m1(){

//使用当前类的运行时类对象作为锁对象,可以简单的理解

为把 Test06 类的字节码文件作为锁对象

synchronized ( Test09.class ) {

for (int i = 1; i <= 100; i++) {
System.out.println(Thread.currentThread().getName() + " --> " + i);

if ( i == 50){

Integer.parseInt("abc");

//把字符串转换为

int 类型时,如果字符串不符合 数字格式会产生异常

}

}

}

}

//使用 synchronized 修饰静态方法,同步静态方法, 默认运行时类

Test06.class 作为锁对象

public synchronized static void sm2(){

for (int i = 1; i <= 100; i++) {

System.out.println(Thread.currentThread().getName() + " --> " + i);

}

}

}
复制代码

死锁

package com.wkcto.intrinsiclock;

/**

* 死锁

* 在多线程程序中,同步时可能需要使用多个锁,如果获得锁的顺序

不一致,可能会导致死锁

* 如何避免死锁?

*

当需要获得多个锁时,所有线程获得锁的顺序保持一致即可

* Author: 老崔

*/

public class Test10 {

public static void main(String[] args) {

SubThread t1 = new SubThread();

t1.setName("a");

t1.start();

SubThread t2 = new SubThread();

t2.setName("b");

t2.start();
}

static class SubThread extends Thread{

private static final Object lock1 = new Object();

private static final Object lock2 = new Object();

@Override

public void run() {

if ("a".equals(Thread.currentThread().getName())){

synchronized (lock1){

System.out.println("a 线程获得了 lock1 锁,还需

要获得 lock2 锁");

synchronized (lock2){

System.out.println("a 线程获得 lock1 后又

获得了 lock2,可以想干任何想干的事");

}

}

}

if ("b".equals(Thread.currentThread().getName())){

synchronized (lock2){

System.out.println("b 线程获得了 lock2 锁,还需要获得 lock1 锁");

synchronized (lock1){

System.out.println("b 线程获得lock2后又

获得了 lock1,可以想干任何想干的事");

}

}

}

}

}

}
复制代码

猜你喜欢

转载自juejin.im/post/7018084984904908831