Java多线程编程核心技术总结——对象及变量的并发访问(二)

方法内的变量为线程安全

   --如果是方法内部的私有变量,则不会存在“非线程安全”问题,所得结果也就是“线程安全”的了。

 示例:

public class HasSelfPrivateNum {
    public void addI(String username) {
        try {
            int num = 0;
            if (username.equals("a")) {
                num = 100;
                System.out.println("a set over");
                Thread.sleep(2000);
            } else {
                num = 200;
                System.out.println("b set over");
            }
            System.out.println(username + " num=" + num);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class ThreadA extends Thread {
    private HasSelfPrivateNum hasSelfPrivateNum;

    public ThreadA(HasSelfPrivateNum numRef) {
        super();
        this.hasSelfPrivateNum = numRef;
    }

    @Override
    public void run() {
        super.run();
        hasSelfPrivateNum.addI("a");
    }
}

class ThreadB extends Thread {
    private HasSelfPrivateNum hasSelfPrivateNum;

    public ThreadB(HasSelfPrivateNum numRef) {
        super();
        this.hasSelfPrivateNum = numRef;
    }

    @Override
    public void run() {
        super.run();
        hasSelfPrivateNum.addI("b");
    }
}

class Run {
    public static void main(String[] args) {
        HasSelfPrivateNum numRef = new HasSelfPrivateNum();
        ThreadA athread = new ThreadA(numRef);
        athread.start();
        ThreadB bthread = new ThreadB(numRef);
        bthread.start();
    }
}

output:线程A启动,num的值为100,设值线程沉睡1秒,B线程启动,则num的值变为200,但num的值是私有变量,并没有出现脏读,输出结果正常。

实例变量非线程安全

   --如果多个线程共同访问1个对象的实例变量,则可能出现“非线程安全”

示例:更改HasSelfPrivateNum类,其他类不变

public class HasSelfPrivateNum{
   private int num = 0;
   public void addI(String username){
   try{
     if(username.equals("a")){
     num = 100;
     System.out.println("a set over");
      }else {
        num =200;
        System.out.println("b set over");
        }
   System.out.println(username + "num=" + num);
     }catch(InterruptedException e ){
        e.printStackTrace();
    }
  }
}

output:出现“非线程安全”


示例:通过synchronized关键字解决

public class HasSelfPrivateNum{
   private int num = 0;
   synchronized public void addI(String username){
   try{
     if(username.equals("a")){
     num = 100;
     System.out.println("a set over");
      }else {
        num =200;
        System.out.println("b set over");
        }
   System.out.println(username + "num=" + num);
     }catch(InterruptedException e ){
        e.printStackTrace();
    }
  }
}

output:


多个对象多个锁

示例:更改Run类,创建两个对象

class Run {
    public static void main(String[] args) {
        HasSelfPrivateNum numRef1 = new HasSelfPrivateNum();
        HasSelfPrivateNum numRef2 = new HasSelfPrivateNum();
        ThreadA athread = new ThreadA(numRef1);
        athread.start();
        ThreadB bthread = new ThreadB(numRef2);
        bthread.start();
    }
}

output:显示的结果是交叉运行的,因为多个线程访问多个对象,则JVM会创建多个锁(运行发现,因为两个对象,所以此处不加synchronized也不会出现“非线程安全”,只有共享的资源读写访问才需要同步化,如果不是共享资源,那么根本不需要同步化)

synchronized方法与锁对象

     --A线程先持有对象的Lock锁,B线程可以以异步的方式调用Object对象中的非synchronized类型的方法

     --A线程先持有object对象的Lock锁,B线程如果在这是调用object对象中的synchronized类型的方法则需要等待,也就是同步。

示例:

public class MyObject {

    synchronized public void methodA() {
        try {
            System.out.println("begin methodA threadName=" + Thread.currentThread().getName());
            Thread.sleep(2000);
            System.out.println("end a");
        } catch (Exception e) {
        }
    }

    public void methodB() {
        try {
            System.out.println("begin methodB threadName=" + Thread.currentThread().getName());
            Thread.sleep(2000);
            System.out.println("end b");
        } catch (Exception e) {
        }
    }
}

class ThreadA extends Thread {
    private MyObject myObject;

    public ThreadA(MyObject myObject) {
        super();
        this.myObject = myObject;
    }

    @Override
    public void run() {
        super.run();
        myObject.methodA();
    }
}

class ThreadB extends Thread {
    private MyObject myObject;

    public ThreadB(MyObject myObject) {
        super();
        this.myObject = myObject;
    }

    @Override
    public void run() {
        super.run();
        myObject.methodB();
    }
}

class Run {

    public static void main(String[] args) {
        MyObject myObject = new MyObject();
        ThreadA threadA = new ThreadA(myObject);
        ThreadB threadB = new ThreadB(myObject);
        threadA.start();
        threadB.start();
    }
}

output:


更改MyObject类

public class MyObject {

    synchronized public void methodA() {
        try {
            System.out.println("begin methodA threadName=" + Thread.currentThread().getName());
            Thread.sleep(2000);
            System.out.println("end a");
        } catch (Exception e) {
        }
    }

    synchronized public void methodB() {
        try {
            System.out.println("begin methodB threadName=" + Thread.currentThread().getName());
            Thread.sleep(2000);
            System.out.println("end b");
        } catch (Exception e) {
        }
    }
}

output:


synchronized 锁重入

     --当一个线程得到一个对象锁后,再次请求对象锁时,是可以再次得到该对象的锁。在一个synchronized 方法/块 的内部调用本类的其他synchronized方法/块时,是永远可以得到锁的

public class Service {
    synchronized public void service1() {
        System.out.println("Service1");
        service2();

    }
    synchronized public void service2() {
        System.out.println("Service2");
        service3();
    }
    synchronized public void service3() {
        System.out.println("Service3");
    }
}

class ThreadA extends Thread {

    Service service;
    public ThreadA(Service service) {
        super();
        this.service = service;
    }
    @Override
    public void run() {
        service.service1();
    }
}

class Run {
    public static void main(String[] args) {
        Service service = new Service();
        ThreadA threadA = new ThreadA(service);
        threadA.start();
    }
}

output:


出现异常,锁自动释放

  示例:

public class Service {
    synchronized public void testMethod() {
        if (Thread.currentThread().getName().equals("a")) {
            System.out.println("ThreadName=" + Thread.currentThread().getName() + " run beginTime=" + System.currentTimeMillis());
            int i = 1;
            while (i == 1) {
                Integer.parseInt("a");
            }
        } else {
            System.out.println("Thread B run");
        }

    }
}

class ThreadA extends Thread {
    private Service service;

    public ThreadA(Service service) {
        super();
        this.service = service;
    }

    @Override
    public void run() {
        service.testMethod();
    }
}

class ThreadB extends Thread {
    private Service service;

    public ThreadB(Service service) {
        super();
        this.service = service;
    }

    @Override
    public void run() {
        service.testMethod();
    }
}

class Run {
    public static void main(String[] args) throws Exception {
        Service service = new Service();
        ThreadA threadA = new ThreadA(service);
        threadA.setName("a");
        threadA.start();
        Thread.sleep(3030);
        ThreadB threadB = new ThreadB(service);
        threadA.setName("b");
        threadB.start();
    }
}

output:线程a出现异常并释放锁,线程b进入方法正常打印,实验的结论就是出现异常的锁被自动释放了。


同步不具有继承性

示例:

public class Main {
    synchronized public void serviceMethod() {
        System.out.println("int main 下一步sleep begin threadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
        System.out.println("int main 下一步sleep sleep end threadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
    }
}

class Sub extends Main {

    @Override
    public void serviceMethod() {
        try {
            System.out.println("int sub 下一步sleep begin threadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
            Thread.sleep(4000);
            System.out.println("int sub 下一步sleep end threadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
            super.serviceMethod();
        } catch (Exception e) {
        }
    }
}

class MyThreadA extends Thread {
    private Sub sub;

    public MyThreadA(Sub sub) {
        super();
        this.sub = sub;
    }

    @Override
    public void run() {
        sub.serviceMethod();
    }
}

class MyThreadB extends Thread {
    private Sub sub;

    public MyThreadB(Sub sub) {
        super();
        this.sub = sub;
    }

    @Override
    public void run() {
        sub.serviceMethod();
    }
}

class Test {
    public static void main(String[] args) {
        Sub sub = new Sub();
        MyThreadA threadA = new MyThreadA(sub);
        MyThreadB threadB = new MyThreadB(sub);
        threadA.setName("A");
        threadB.setName("B");
        threadA.start();
        threadB.start();
    }
}

output:


synchronized同步语句块

   --用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个时间的任务,那么B线程则必须等待较长的时间,在这样的情况下可以使用synchronized同步语句块来解决。

   --synchronized方法是对当前对象加锁,而synchronized代码块是对某一个对象加锁。

public class Task {

    private String getDate1;

    private String getDate2;

    public synchronized void doLongTimeTask() {
        try {
            System.out.println("begin task");
            Thread.sleep(3000);
            getDate1 = "长时间处理任务后从远程返回的值1 threadName=" + Thread.currentThread().getName();
            getDate2 = "长时间处理任务后从远程返回的值2 threadName=" + Thread.currentThread().getName();
            System.out.println(getDate1);
            System.out.println(getDate2);
            System.out.println("end task");
        } catch (Exception e) {
        }
    }
}

class CommonUtils {
    public static long beginTime;
    public static long endTime;
    public static long beginTime2;
    public static long endTime2;
}

class MyThread1 extends Thread {
    private Task task;

    public MyThread1(Task task) {
        super();
        this.task = task;
    }

    @Override
    public void run() {
        super.run();
        CommonUtils.beginTime = System.currentTimeMillis();
        task.doLongTimeTask();
        CommonUtils.endTime = System.currentTimeMillis();
    }
}

class MyThread2 extends Thread {
    private Task task;

    public MyThread2(Task task) {
        super();
        this.task = task;
    }

    @Override
    public void run() {
        super.run();
        CommonUtils.beginTime2 = System.currentTimeMillis();
        task.doLongTimeTask();
        CommonUtils.endTime2 = System.currentTimeMillis();
    }
}

class Test2 {
    public static void main(String[] args) {
        Task task = new Task();
        MyThread1 thread1 = new MyThread1(task);
        thread1.start();
        MyThread2 thread2 = new MyThread2(task);
        thread2.start();
        try {
            Thread.sleep(10000);
        } catch (Exception e) {
        }
        long beginTime = CommonUtils.beginTime;
        if (CommonUtils.beginTime2 < CommonUtils.beginTime) {
            beginTime = CommonUtils.beginTime2;
        }
        long endTime = CommonUtils.endTime;
        if (CommonUtils.endTime2 > CommonUtils.endTime) {
            endTime = CommonUtils.endTime2;
        }
        System.out.println("耗时:" + (endTime - beginTime) / 1000);
    }
}

output:


用同步代码块解决同步方法的弊端

更改文件Task.java文件

public class Task {

    private String getDate1;

    private String getDate2;

    public void doLongTimeTask() {
        try {
            System.out.println("begin task");
            Thread.sleep(3000);
            String gd1 = "长时间处理任务后从远程返回的值1 threadName=" + Thread.currentThread().getName();
            String gd2 = "长时间处理任务后从远程返回的值2 threadName=" + Thread.currentThread().getName();
            synchronized (this) {
                getDate1 = gd1;
                getDate2 = gd2;
            }

            synchronized (this) {
                System.out.println(getDate1);
                System.out.println(getDate2);
                System.out.println("end task");
            }

        } catch (Exception e) {
        }
    }
}

output:


synchronized代码块间的同步性

    --当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对同一个object中所有其他synchronized(this)同步代码块的访问将被阻塞,这说明synchronized使用的“对象监视器”是一个。

public class ObjectService {

    public void serviceMethodA() {
        try {
            synchronized (this) {
                System.out.println("A begin time=" + System.currentTimeMillis());
                Thread.sleep(2000);
                System.out.println("A end end=" + System.currentTimeMillis());
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void serviceMethodB() {
        synchronized (this) {
            System.out.println("B begin time=" + System.currentTimeMillis());
            System.out.println("B end end=" + System.currentTimeMillis());
        }
    }

}

class ThreadA extends Thread {
    private ObjectService service;

    public ThreadA(ObjectService service) {
        super();
        this.service = service;
    }

    @Override
    public void run() {
        super.run();
        service.serviceMethodA();
    }
}

class ThreadB extends Thread {
    private ObjectService service;

    public ThreadB(ObjectService service) {
        super();
        this.service = service;
    }

    @Override
    public void run() {
        super.run();
        service.serviceMethodB();
    }
}

class Run {
    public static void main(String[] args) {
        ObjectService service = new ObjectService();
        ThreadA a = new ThreadA(service);
        a.setName("a");
        a.start();
        ThreadB b = new ThreadB(service);
        b.setName("b");
        b.start();
    }
}

output:


将任意对象作为对象监听器

    --使用“synchronized(非this对象x)同步代码块”格式进行同步操作时,对象监听器必须是同一个对象。如果不是同一个对象监视器,运行的结果是异步调用了,就会交叉运行。                                                                                                                                                                              

静态同步synchronized方法与synchronized(class)代码块

   --将synchronized关键字加到非static方法上使用的效果是一样的。synchronized关键字加到static静态方法上是给Class类上锁,而synchronized关键字加到非static静态方法上是给对象上锁。

数据类型String的常量池特性

示例:

public class Service2 {
    public void print(String str) {
        try {
            synchronized (str) {
                while (true) {
                    System.out.println(Thread.currentThread().getName());
                    Thread.sleep(1000);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class ThreadA extends Thread {
    private Service2 service2;

    public ThreadA(Service2 service2) {
        super();
        this.service2 = service2;
    }

    @Override
    public void run() {
        service2.print("AA");
    }
}

class ThreadB extends Thread {
    private Service2 service2;

    public ThreadB(Service2 service2) {
        super();
        this.service2 = service2;
    }

    @Override
    public void run() {
        service2.print("AA");
    }
}

class Run {
    public static void main(String[] args) {
        Service2 service2 = new Service2();
        ThreadA a = new ThreadA(service2);
        a.setName("A");
        a.start();
        ThreadB b = new ThreadB(service2);
        b.setName("B");
        b.start();
    }
}

output:出现这样的情况就是因为String的两个值都是AA,两个线程持有相同的锁,所以造成线程B不能执行。这就是String常量池所带来的问题。因此在绝大数情况下,同步synchronized代码块都不适用String作为锁对象,而改用其他,不如new Object()实例化一个Object对象,但它并不放入缓存中。


更改代码如下:

public class Service2 {
    public void print(Object object) {
        try {
            synchronized (object) {
                while (true) {
                    System.out.println(Thread.currentThread().getName());
                    Thread.sleep(1000);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class ThreadA extends Thread {
    private Service2 service2;

    public ThreadA(Service2 service2) {
        super();
        this.service2 = service2;
    }

    @Override
    public void run() {
        service2.print(new Object());
    }
}

class ThreadB extends Thread {
    private Service2 service2;

    public ThreadB(Service2 service2) {
        super();
        this.service2 = service2;
    }

    @Override
    public void run() {
        service2.print(new Object());
    }
}

class Run {
    public static void main(String[] args) {
        Service2 service2 = new Service2();
        ThreadA a = new ThreadA(service2);
        a.setName("A");
        a.start();
        ThreadB b = new ThreadB(service2);
        b.setName("B");
        b.start();
    }
}

output:


同步synchronized方法无限等待与解决

示例:同步方法容易造成死循环,b线程永远无法运行

public class Service {
    synchronized public void methodA() {
        System.out.println("methodA begin");
        boolean isContinueRun = true;
        while (isContinueRun) {
        }
        System.out.println("methodA end");
    }

    synchronized public void methodB() {
        System.out.println("methodB begin");
        System.out.println("methodB end");
    }
}

class ThreadA extends Thread {
    private Service service;

    public ThreadA(Service service) {
        super();
        this.service = service;
    }

    @Override
    public void run() {
        service.methodA();
    }
}

class ThreadB extends Thread {
    private Service service;

    public ThreadB(Service service) {
        super();
        this.service = service;
    }

    @Override
    public void run() {
        service.methodB();
    }
}

class Test {
    public static void main(String[] args) {
        Service service = new Service();
        ThreadA threadA = new ThreadA(service);
        threadA.setName("A");
        threadA.start();
        ThreadB threadB = new ThreadB(service);
        threadB.setName("B");
        threadB.start();
    }
}

output:


通过同步块来解决这样的问题

public class Service {
    public void methodA() {
        synchronized (new Object()) {
            System.out.println("methodA begin");
            boolean isContinueRun = true;
            while (isContinueRun) {
            }
            System.out.println("methodA end");
        }
    }

    synchronized public void methodB() {
        System.out.println("methodB begin");
        System.out.println("methodB end");
    }
}

class ThreadA extends Thread {
    private Service service;

    public ThreadA(Service service) {
        super();
        this.service = service;
    }

    @Override
    public void run() {
        service.methodA();
    }
}

class ThreadB extends Thread {
    private Service service;

    public ThreadB(Service service) {
        super();
        this.service = service;
    }

    @Override
    public void run() {
        service.methodB();
    }
}

class Test {
    public static void main(String[] args) {
        Service service = new Service();
        ThreadA threadA = new ThreadA(service);
        threadA.setName("A");
        threadA.start();
        ThreadB threadB = new ThreadB(service);
        threadB.setName("B");
        threadB.start();
    }
}

output:


volatile关键字

示例:

public class PrintString {
    private boolean isContinuePrint = true;

    public boolean isContinuePrint() {
        return isContinuePrint;
    }

    public void setContinuePrint(boolean isContinuePrint) {
        this.isContinuePrint = isContinuePrint;
    }

    public void printStringMethod() {
        try {
            while (isContinuePrint == true) {
                System.out.println("run printStringMethod threadName=" + Thread.currentThread().getName());
                Thread.sleep(1000);
            }
        } catch (Exception e) {
        }

    }
}

class Run {
    public static void main(String[] args) {
        PrintString printString = new PrintString();
        printString.printStringMethod();
        System.out.println("我要停止它!" + Thread.currentThread().getName());
        printString.setContinuePrint(false);
    }
}

output:程序运行后,根本停不下来,如图所示


 使用多线程技术:

public class PrintString implements Runnable {
    private boolean isContinuePrint = true;

    public boolean isContinuePrint() {
        return isContinuePrint;
    }

    public void setContinuePrint(boolean isContinuePrint) {
        this.isContinuePrint = isContinuePrint;
    }

    public void printStringMethod() {
        try {
            while (isContinuePrint == true) {
                System.out.println("run printStringMethod threadName=" + Thread.currentThread().getName());
                Thread.sleep(1000);
            }
        } catch (Exception e) {
        }
    }

    @Override
    public void run() {
        printStringMethod();
    }
}

class Run {
    public static void main(String[] args) {
        PrintString printString = new PrintString();
        new Thread(printString).start();
        System.out.println("我要停止它!" + Thread.currentThread().getName());
        printString.setContinuePrint(false);
    }
}

output:


当上面的示例代码运行在-server服务器模式中64bit的JVM上时,会出现死循环,解决的办法是使用volatile关键字。

关键字volatile的作用是强制从公共堆栈中取得变量的值,而不是从线程私有数据栈中取得变量的值。

synchronized和volatile比较

   --关键字volatile是线程同步的轻量级实现,所以volatile性能肯定比synchronized要好,并且volatile只能修饰变量,而synchronized可以修饰方法,以及代码块。

    --多线程访问volatile不会发生阻塞,而synchronized会出现阻塞

    --volatile能保证数据的可见性,但不能保证原子性;而synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公共内存的数据做同步。

synchronized代码块有volatile同步的功能

示例:

public class Service {
    private boolean isContinueRun = true;

    public void runMethod() {
        String anyString = new String();
        while (isContinueRun == true) {
            synchronized (anyString) {
            }
        }
        System.out.println("停下来了!");
    }
    public void stopMethod() {
        isContinueRun = false;
    }
}

class ThreadA extends Thread {
    private Service service;

    public ThreadA(Service service) {
        super();
        this.service = service;
    }

    @Override
    public void run() {
        service.runMethod();
    }
}

class ThreadB extends Thread {
    private Service service;

    public ThreadB(Service service) {
        super();
        this.service = service;
    }

    @Override
    public void run() {
        service.stopMethod();
    }
}

class Run {
    public static void main(String[] args) {
        try {
            Service service = new Service();
            ThreadA a = new ThreadA(service);
            a.start();
            Thread.sleep(1000);
            ThreadB b = new ThreadB(service);
            b.start();
            System.out.println("已经发起停止的命令了");
        } catch (Exception e) {
        }
    }
}

output:



参考:《Java多线程编程核心技术》--机械工业出版社 高洪岩 著










  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值