JUC线程创建,volatile内存可见性CAS算法模式和ConcurrentHashMap基础原理

1.线程的四种创建方式:

1.直接继承Thread的方式去实现线程的创建:

/**
 * 创建线程方式一:
 */
public class test01 {
    public static void main(String[] args) {
        ThreadDemo01 threadDemo01 = new ThreadDemo01();
        threadDemo01.start();
    }
}

class ThreadDemo01 extends Thread {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

1.1通过内部类直接创建,效果和上面是一样的。

//通过内部类直接创建:
public class test02 {
    public static void main(String[] args) {
      Thread thread = new Thread(){
          @Override
          public void run() {
              System.out.println(Thread.currentThread().getName());
          }
      };
      thread.start();
    }
}

底层代码分析:

/* What will be run. */
private Runnable target;
-----------------------------------------

@Override
public void run() {
    if (target != null) {
        target.run();//在这里,底层给我们写好了,可以直接调用run方法。
    }
}
----------------------------------------------
//直接去实现Runnable接口
@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

2.通过实现Runable

//实现Runnable接口
public class testRunable {
    public static void main(String[] args) {
        new Thread(new TestRunnableDemo(),"Runnable").start();
    }
}
class TestRunnableDemo implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

从底层来看,一个类去继承Thread和去实现Runnable的本质是一样的,最终的结果都是调用Runnable接口的run方法。

3.实现Callable接口:

public class TestCallable {
    public static void main(String[] args) {
        //1.创建对应的对象
        TestCallableDemo testCallableDemo = new TestCallableDemo();
        //2.需要通过FutureTask的实现类的支持,并用于接收运行结果
        FutureTask<String> futureTask = new FutureTask<>(testCallableDemo);
        //3.开启线程
        new Thread(futureTask).start();
        //4.接收运行的结果:
        try {
            String s = futureTask.get();
            System.out.println("这个是获取到的值为:"+s);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
class TestCallableDemo implements Callable<String>{

    @Override
    public String call() throws Exception {
        System.out.println(Thread.currentThread().getName());
        return "Callable";
    }
}
  • Java 5.0 在java.util.concurrent提供了一个新的创建执行线程的方式:Callable接口。
  • Callable接口类似于Runnable,两者都是为那些实例可能被另一个线程执行的类设计的,但是Runnable不会返回结果,并且无法抛出经过检查的异常,但是Callable是可以有返回结果,并且可以抛出检查的异常的。
  • Callable需要依赖FutureTask,FutureTask也可以用作闭锁。

1) FutureTask(实现类)实现了RunnableFuture(接口)。

2)RunnableFuture继承了Runnable(接口)和Future(接口)。

4.利用线程池实现:

public class TestThreadPool {
    public static void main(String[] args) {
        TestThreadPoolDemo testThreadPoolDemo = new TestThreadPoolDemo();
        //1.通过Executors.newFixedThreadPool
        ExecutorService pool = Executors.newFixedThreadPool(5);
        //2.为线程中的线程分配任务
        pool.submit(testThreadPoolDemo);
        //3.关闭线程池:
        pool.shutdown();
    }
}
class TestThreadPoolDemo implements Runnable{
    private int i=0;
    @Override
    public void run() {
        while(i<=100){
            System.out.println(Thread.currentThread().getName()+" : "+ i++);
        }
    }
}

4.1通过内部类直接实现:

public class TestThreadPool {
    public static void main(String[] args) {
        //1.创建线程池
        ExecutorService pool = Executors.newFixedThreadPool(5);
        //2.通过内部类完成对值的传递
        Future future = pool.submit(new Callable<Integer>() {
            int sum = 0;
            @Override
            public Integer call() throws Exception {
                for (int i = 0; i <=100 ; i++) {
                    sum+=i;
                }
                return sum;
            }
        });

        //3.取值
        try {
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        //4.关闭线程池
        pool.shutdown();
    }
}

一、线程池:提供了一个线程队列,队列中保存这所有等待状态的线程。避免了创建与销毁额外开销,提高了响应的速度。

二、线程池的体系结构:

java.util.concurrent.Exector:负责线程的使用与调度的根接口

java.util.concurrent.Executor : 负责线程的使用与调度的根接口

|–**ExecutorService 子接口: 线程池的主要接口

|–ThreadPoolExecutor 线程池的实现类

|–ScheduledExecutorService 子接口:负责线程的调度

|–ScheduledThreadPoolExecutor :继承 ThreadPoolExecutor, 实现 ScheduledExecutorService

三、工具类:Executors

ExecutorService ==》 newFixedThreadPool() : 创建固定大小的线程池

ExecutorService ==》 newCachedThreadPool() : 缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量。

ExecutorService ==》 newSingleThreadExecutor() : 创建单个线程池。线程池中只有一个线程

ScheduledExecutorService newScheduledThreadPool() : 创建固定大小的线程,可以延迟或定时的执行任务。

2. JUC简介:

在Java 5.0 提供了java.util.concurrent(简称JUC)包,在此包中添加了再并发编程中很常用的使用工具,用于定义类似于线程的自定义子系统,包括线程池,异步IO和轻量级任务框架。提供可调的、灵活的线程池。还提供了设计用于多线程上下文的Collection实现等。

3. volatile关键字 内存可见性

  • 内存可见性(Memory Visibility)是值当前某个线程正在使用对象状态而另一个线程正在修改该状态,需要确保当一个线程修改了对象状态后,其他线程能够看到发生的状态变化。
  • 可见性错误是指当前读操作与写操作在不用的线程中执行时,我们无法确保执行读操作的线程能实时地其他线程写入的值,有是甚至是根本不可能做的事情。
  • 我们可以通过同步来保证对象被安全的发布。除此之外,我们也可以使用一种更加轻量级的volatile变量。
  • Java提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程,可以将volatile看做一个轻量级的锁,但是又与锁有些不同:
    • 对于多线程,不是一种互斥关系
    • 不能保证变量状态的“原子性操作”
    • volatile相对于synchronized是一种较为轻量级的同步策略。
//volatile不具备互斥性。
//volatile不能保证变量的原子性
public class TestVolatile {
    public static void main(String[] args) {
        TestVolatileDemo testVolatileDemo = new TestVolatileDemo();
        new Thread(testVolatileDemo).start();
        while(true){
            if (testVolatileDemo.isFlag()){
                System.out.println("----------------");
                break;
            }
        }
    }
}
class TestVolatileDemo implements Runnable{
    private volatile boolean flag = false;
    @Override
    public void run() {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        flag = true;
        System.out.println("flag="+isFlag());
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

4. CAS算法

模拟CAS算法:

public class TestCompareAndSwap {
	public static void main(String[] args) {
		final CompareAndSwap cas=new CompareAndSwap();
		for (int i = 0; i < 10; i++) {
			new Thread(new Runnable() {
				
				@Override
				public void run() {
					int expectedValue = cas.get();
					boolean b = cas.compareAndSet(expectedValue,(int)(Math.random()+101));
					System.out.println(b);
				}
			}).start();
		}
	}
}
class CompareAndSwap{
	private int value;
	//获取内存值
	public synchronized int get() {
		return value;
	}
	
	//比较
	public synchronized int compareAndSwap(int ecpectedValue,int newValue) {
		int oldValue=value;
		if(oldValue == ecpectedValue) {
			this.value = newValue;
		}
		return oldValue;
	}
	
	public synchronized boolean compareAndSet(int expectedValue, int newValue) {
		return expectedValue == compareAndSwap(expectedValue, newValue);
	}
}
  • CAS(Compare-And-Swap)是一种硬件对并发的支持,针对多处理器操作而设计的处理器中的一种特殊指令,用于管理对共享数据的并发访问。
  • CAS是一种无所的非阻塞算法的实现。
  • CAS包含了三个操作数:
    • 需要读写的内存值V
    • 进行比较的值A
    • 拟写入的新值B
  • 当且仅当V的值等于A时,CAS通过原子方式用新值B来更新V的值,否则不会执行任何操作。

原子性:

/*
 * 一、i++的原子性问题:实际上是分为三个步骤,读-该-写
 * 二、解决方案:原子变量:jdk1.5后java.util.concurrent.atomic包下提供了常用的原子变量
 * 	1.volatile 保证内存可见性。
 * 	2.CAS(Compare-and-swap)算法保证数据的原子性。
 * 		CSA算法是硬件对于并发操作共享数据的支持。
 * 		CSA包含了三个操作:内存值 V 预估值A更新值B;当且仅当V==A时,V=B,否则不做任何操作
 */
public class TestAtomicDemo {
	public static void main(String[] args) {
		AtomicDemo ad = new AtomicDemo();
		for (int i = 0; i < 10; i++) {
			new Thread(ad).start();
		}
	}
}
class AtomicDemo implements Runnable{
	//private volatile int serialNumber=0;
	private AtomicInteger serialNumber=new AtomicInteger();

	@Override
	public void run() {
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
		}
		System.out.println(Thread.currentThread().getName()+"="+getSerialNumber());
	}

	public int getSerialNumber() {
		return serialNumber.getAndIncrement();
	}
}

5. ConcurrentHashMap锁分段机制:

Java 5.0 在java.util.concurrent包中提供了多种并发容器类来改进同步容器的性能。

ConcurrentHashMap同步容器类是Java 5增加的一个线程安全的哈希表。对于多线程的操作,介于HashMap与HashTable之间,内部采用“锁分段”机制代替HashTable的独占锁。进而提高性能。

此包还提供了设计用于多线程上下文中的Collection实现:

​ ConcurrentHashMap、COncurrentSkipListMap、COncurrentSkipListSet、CopyOnWriteArrayList和CopyOnWriteArraySet。当期望许多线程访问一个给定collection时,ConcurrentHashMap通常由于同步线程的HashMap,ConcurrentSkipListMap通常优于同步的TreeMap。当企鹅昂的读书和遍历远远大于列表的更新数时,CopyOnWriteArrayList由于同步的ArrayList。

如果同一组数据,即读取,有写入的话,这样是会报错的。

/**
 * 直接使用会报错:private static List<String> list = Collections.synchronizedList(new ArrayList());
 * ConcurrentModificationException
 * 因为操作的是同一组数据。
 * 
 * CopyOnWriteArrayList/CopyOnWriteArraySet:"写入并复制"
 * 注意:
 * 添加操作多时,效率低,因为每次添加时都会进行复制,开销非常大。并发迭代操作多时,可以选择。
 */
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class TestCopyOnWriteArrayList {
	public static void main(String[] args) {
		HelloThread hThread = new HelloThread();
		for (int i = 0; i < 10; i++) {
			new Thread(hThread).start();
		}
	}
}
class HelloThread implements Runnable{
	//private static List<String> list = Collections.synchronizedList(new ArrayList());
	private static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList();
	static {
		list.add("AA");
		list.add("BB");
		list.add("CC");
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		Iterator<String> it = list.iterator();
		while (it.hasNext()) {
			System.out.println(it.next());
			list.add("123");
		}
	}
	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值