不正确地访问资源

boolean类型地数据是原子性操作,即赋值和返回值这样的操作不会有发生中断的可能。虽然虚拟机规范没有定义boolean类型数据所占的字节数,但通常一个bit位即可表示。

  1. boolean: The boolean data type has only two possible values: true and false.
  2. Use this data type for simple flags that track true/false conditions.
  3. This data type represents one bit of information, but its "size" isn't something that's precisely defined
  1. package com.thinking.in.java.course.chapter21;
  2. //外部条件控制代码的执行
  3. public abstract class IntGenerator {
  4. private volatile boolean canceled = false;//volatile+boolean即保证了可视性又保证了原子性
  5. public abstract int next();
  6. //具体的控制开关
  7. public void cancel(){
  8. canceled = true;
  9. }
  10. public boolean isCanceled(){
  11. return canceled;
  12. }
  13. }
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class EvenChecker implements Runnable{
    private IntGenerator generator;//外部条件
    private final int id;
    public EvenChecker(IntGenerator gp,int ident){
        generator = gp;
        id = ident;
    }
    @Override
    public void run() {
        while (!generator.isCanceled()){
            int num = generator.next();
            // 并发去访问受限资源的话,由于无法保证网络的稳定性,存在诸多问题
            System.out.println(Thread.currentThread().getName()+"->"+num);
            try {
                TimeUnit.MILLISECONDS.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //----------------------
            if(num % 2 !=0) {
                System.out.println(num + " is not even");
                generator.cancel();
            }
        }
    }
    public static void testEven(IntGenerator generator,int count){
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < count; i++) {
            executorService.execute(new EvenChecker(generator,i));
        }
        executorService.shutdown();
    }
    public static void test(IntGenerator generator){
        testEven(generator,10);
    }
}
public class EvenGenerator extends IntGenerator{
    private  int currentEvenValue = 0;

    //非同步的操作、非原子的操作
    @Override
    public  int next() {
        ++currentEvenValue;
        ++currentEvenValue;
        return currentEvenValue;
    }
    public static void main(String[] args) {
        EvenGenerator generator = new EvenGenerator();
        EvenChecker.test(generator);
    }
}

解决方案一

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

public class EvenGenerator extends IntGenerator{
    private  int currentEvenValue = 0;
    private Lock lock = new ReentrantLock();
    @Override
    public  int next() {
        lock.lock();
        ++currentEvenValue;
        ++currentEvenValue;
        lock.unlock();
        return currentEvenValue;
    }
    public static void main(String[] args) {
        EvenGenerator generator = new EvenGenerator();
        EvenChecker.test(generator);
    }
}

解决方案二

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

public class EvenGenerator extends IntGenerator{
    private  int currentEvenValue = 0;
    @Override
    public synchronized int next() {
        ++currentEvenValue;
        ++currentEvenValue;
        return currentEvenValue;
    }
    public static void main(String[] args) {
        EvenGenerator generator = new EvenGenerator();
        EvenChecker.test(generator);
    }
}

解决资源共享竞争

synchronized 修饰的方法,其实是实例(对象)锁

image.png

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class SynchronizedMethodDemo {
    synchronized void f(){
        try {
            TimeUnit.MILLISECONDS.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"->"+"f()");
    }
    synchronized void g(){
        try {
            TimeUnit.MILLISECONDS.sleep(20000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"->"+"g()");
    }
    public static void main(String[] args) {
        SynchronizedMethodDemo demo = new SynchronizedMethodDemo();
        demo.testSyncMethod();
    }
    public  void testSyncMethod(){
        new Thread(()->{
            g();
            try {
                TimeUnit.MILLISECONDS.sleep(30000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            f();
        },"AA").start();

        new Thread(()->{
            f();
            g();
        },"BB").start();
    }
}

image.png
image.png

synchronized +static 修饰的方法 其实是类锁

image.png

import java.util.concurrent.TimeUnit;

public class SynchronizedStaticDemo {
    private static  int ticketNum = 100;
    public synchronized static void getTicket()  {
        if (ticketNum>0) {
            try {
                TimeUnit.MILLISECONDS.sleep(100);
            }catch (InterruptedException e){
                System.out.println(e.getCause());
            }
            ticketNum--;
            System.out.println(Thread.currentThread().getName() + "->" + ticketNum);
        }else {
            System.out.println(Thread.currentThread().getName() + "->" +"票已售罄");
            System.exit(0);
        }
    }
    public synchronized static void getTicket2()  {
        if (ticketNum>0) {
            try {
                TimeUnit.MILLISECONDS.sleep(100);
            }catch (InterruptedException e){
                System.out.println(e.getCause());
            }
            ticketNum--;
            System.out.println(Thread.currentThread().getName() + "->" + ticketNum);
        }else {
            System.out.println(Thread.currentThread().getName() + "->" +"票已售罄");
            System.exit(0);
        }
    }
    public static void main(String[] args) {
        while (true) {
            new Thread(() -> {
                getTicket();
            }, "AAA").start();
            new Thread(() -> {
                getTicket();
            }, "BBB").start();
            new Thread(() -> {
                getTicket();
            }, "CCC").start();
            new Thread(() -> {
                getTicket();
            }, "DDD").start();
            new Thread(() -> {
                getTicket2();
            }, "A-A").start();
            new Thread(() -> {
                getTicket2();
            }, "B-B").start();
            new Thread(() -> {
                getTicket2();
            }, "C-C").start();
            new Thread(() -> {
                getTicket2();
            }, "D-D").start();
        }
    }
}

synchronized 与Lock的区别

image.png
①synchronized 尝试获取锁会一直处于阻塞状态 倔强派
image.png

import java.util.concurrent.TimeUnit;

public class SynchronizedMethodDemo2 {
    synchronized void f(){
        try {
            TimeUnit.MILLISECONDS.sleep(1000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"->"+"f()");
    }
    public static void main(String[] args) {
        SynchronizedMethodDemo2 demo = new SynchronizedMethodDemo2();
        demo.testSyncMethod2();
    }
    public  void testSyncMethod2(){
        new Thread(()->{
            f();
        },"AA").start();

        new Thread(()->{
            f();
        },"BB").start();
    }
}

②lock会尝试着去获取锁,当锁没法获取的时候,就放弃 活套派

import java.util.concurrent.*;
import java.util.concurrent.locks.*;

public class AttemptLocking {
    private ReentrantLock lock = new ReentrantLock();

    public void untimed() {
        boolean captured = lock.tryLock();
        try {
            System.out.println("tryLock(): " + captured);
            try {
              TimeUnit.SECONDS.sleep(30);
            }catch (InterruptedException e){
              e.printStackTrace();
            }
        } finally {
            if (captured)
                lock.unlock();
        }
    }
    public void timed() {
        boolean captured = false;
        try {
            captured = lock.tryLock(10, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        try {
            System.out.println("tryLock(2, TimeUnit.SECONDS): " +  captured);
        } finally {
            if (captured)
                lock.unlock();
        }
    }
    public static void main(String[] args) {
        final AttemptLocking al = new AttemptLocking();
        new Thread(()->{al.untimed();}).start();
        new Thread(()->{al.timed();}).start();
    }
}

原子性与易变性

image.png

原子操作的定义

对域中的值做赋值和返回操作通常都是原子性的。

基本类型非原子操作示例一

package com.thinking.in.java.course.chapter21;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class AtomcityTest implements Runnable{

    private  int i = 0;
    //返回是原子操作
    public int getValue(){
        return i;
    }
    //方法是同步的,但是对变量的修改 其他线程可以看到
    //错误的理解:synchronized同步的方法,内部对成员变量的处理,
    //在通过其他非同步方法访问的时候也是同步的
    public synchronized void evenIncrement(){
        i++;//非原子操作
        i++;

    }

    @Override
    public void run() {
        while (true) {
            //System.out.println("---"+i+"---");
            evenIncrement();

        }
    }

    public static void main(String[] args) {
        AtomcityTest atomcityTest = new AtomcityTest();
        ExecutorService service = Executors.newCachedThreadPool();
        service.execute(atomcityTest);
        while (true){
            int value = atomcityTest.getValue();
            System.out.println("==="+value+"===");
            if(value % 2 !=0){
                System.out.println(value);
                System.exit(0);
            }
        }
    }
}

基本类型原子操作示例二

package com.thinking.in.java.course.chapter21;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class AtomcityTest2 implements Runnable{

    private  boolean flag = false;
    public  boolean getValue(){
        return flag;
    }

    //此处不加synchronized也是可以的
    public synchronized boolean evenIncrement(){
      return  flag =  !flag;
    }

    @Override
    public void run() {
        while (true) {
            System.out.println("---"+flag+"---");
            evenIncrement();
        }
    }

    public static void main(String[] args) {
        AtomcityTest2 test2 = new AtomcityTest2();
        ExecutorService service = Executors.newCachedThreadPool();
        service.execute(test2);
        while (true){
           boolean flag = test2.getValue();
            System.out.println("==="+flag+"===");
            if(flag){
                System.out.println(flag);
                System.exit(0);
            }
        }

    }
}

临界区

synchronized 修饰的方法在一个线程获取锁的时候,并不影响其他线程对普通方法的调用
synchronized 修饰的this代码块方法在一个线程获取锁的时候,并不影响其他线程对普通方法的调用
synchronized void syncM(){}、void thisM(){ synchronized (this) {}}对同一对象来说 是同一把锁

package com.thinking.in.java.course.chapter21;


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

class Moo{
    /**
     * synchronized void syncM(){}
     * void thisM(){ synchronized (this) {}}
     * 对同一对象来说 是同一把锁
     */

    AtomicInteger atomicInteger = new AtomicInteger();

    //synchronized 修饰的方法在一个线程获取锁的时候,并不影响其他线程对普通方法的调用
    synchronized void syncM(){
        try {
            TimeUnit.SECONDS.sleep(3);
        }catch (InterruptedException e){
            System.out.println(e);
        }
        System.out.println("Moo sync method");
    }
    //synchronized 修饰的this方法在一个线程获取锁的时候,并不影响其他线程对普通方法的调用
     void thisM(){
        synchronized (this) {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                System.out.println(e);
            }
            System.out.println("Moo this method");
        }
    }


    void commonM(){
        System.out.println("Moo common method");
    }
}

class MooTask implements Runnable{

    Moo moo;

    MooTask(Moo moo){this.moo = moo;}

    @Override
    public void run() {
        while (true){
            moo.syncM();
        }
    }
}

class Moo2Task implements Runnable{
    Moo moo;

    Moo2Task(Moo moo){this.moo = moo;}

    @Override
    public void run() {
        while (true) {
            //moo.thisM();
            moo.atomicInteger.incrementAndGet();
        }
    }
}

public class CriticalSection2 {

    public static void main(String[] args) throws InterruptedException {
        final ExecutorService executorService = Executors.newCachedThreadPool();
        final Moo moo = new Moo();

        executorService.execute(new MooTask(moo));
        //TimeUnit.MILLISECONDS.sleep(100);
        executorService.execute(new Moo2Task(moo));
    }

}

在其他对象上同步

package com.thinking.in.java.course.chapter21;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
通过jps、jstack 查看lock的对象是不一样的
*/

class DualSynch {
    public Object syncObject = new Object();

    //同步方法 其实就是 实例锁
    public synchronized  void f() {

        for (int i = 0; i < 5; i++) {
            System.out.println("f() =>"+Thread.currentThread().getName());
            Thread.yield();
        }
    }

    public void g() throws InterruptedException {
        //对象锁
        synchronized (syncObject) {

            for (int i = 0; i < 5; i++) {
                System.out.println("g()");
                Thread.yield();
            }
        }
    }
}

public class SyncObject {
    public static void main(String[] args) throws InterruptedException {
        final DualSynch ds = new DualSynch();
        new Thread() {
            public void run() {
                ds.f();
            }
        }.start();
         ds.g();
    }
}

线程的本地存储

对于实例变量、静态字段、构成数组对象的元素,线程会共享,造成资源冲突,解决的方式使用ThreadLocal进行包裹。

实例变量示例一


import java.util.concurrent.TimeUnit;

public class ThreadLocalNoDemo {
    //实例变量
    private String str = "abc";

    public static void main(String[] args) {
        final ThreadLocalNoDemo threadLocalNoDemo = new ThreadLocalNoDemo();
        new Thread(()->{
            try {
                TimeUnit.MILLISECONDS.sleep(100);
            }catch (InterruptedException e){}
            threadLocalNoDemo.str +=1;
            System.out.println(threadLocalNoDemo.str);
        }).start();

        new Thread(()->{
            threadLocalNoDemo.str +="w";
            try {
                TimeUnit.MILLISECONDS.sleep(10);
            }catch (InterruptedException e){}
            System.out.println(threadLocalNoDemo.str);
        }).start();
    }
}

实例变量示例二


public class ThreadLocalWithDemo {

    //采用ThreadLocal包裹的实例变量
    ThreadLocal<String> value = new ThreadLocal<String>(){
        @Override
        protected String initialValue() {
            return "abc";
        }
    };

    public static void main(String[] args) {
        final ThreadLocalWithDemo threadLocalWithDemo = new ThreadLocalWithDemo();
        new Thread(()->{
            final String s = threadLocalWithDemo.value.get();

            threadLocalWithDemo.value.set(s+"1111");
            try {
                TimeUnit.MILLISECONDS.sleep(1000);
            }catch (InterruptedException e){}
            System.out.println(threadLocalWithDemo.value.get());
        }).start();

        new Thread(()->{
            final String s = threadLocalWithDemo.value.get();

            threadLocalWithDemo.value.set(s+"wwww");
            try {
                TimeUnit.MILLISECONDS.sleep(100);
            }catch (InterruptedException e){}
            System.out.println(threadLocalWithDemo.value.get());
        }).start();
    }
}

局部变量与方法参数属于线程私有,不会被共享

package com.thinking.in.java.course.chapter21;

import java.util.concurrent.TimeUnit;

public class ThreadLocalNoDemo2 {

    //方法中的参数,属于线程独有的 并不会共享
    public void testMethodParam(String str){
        String s = "hello";
        str+=s;
        System.out.println(str);
    }


    public static void main(String[] args) {
        ThreadLocalNoDemo2 demo2 = new ThreadLocalNoDemo2();
        new Thread(()->{
            try {
                TimeUnit.MILLISECONDS.sleep(100);
            }catch (InterruptedException e){}
            demo2.testMethodParam("1111");
        }).start();


        new Thread(()->{
            try {
                TimeUnit.MILLISECONDS.sleep(100);
            }catch (InterruptedException e){}
            demo2.testMethodParam("2222");
        }).start();
    }
}