Java 并发编程(一)-JUC

目录

一、并发编程

1、Lock接口

面试:Lock与Synchronized的区别?用新的Lock有什么好处?举例说说?

2、线程间的通信

2.1、synchronized通信示例

解决虚假唤醒问题

2.2、Lock通信示例:

2.3、线程间定制化通信

3、集合的线程安全

3.1、List集合线程不安全示例

1、Vector示例:

2、Collections示例:

3、CopyOnWriteArrayList示例:

4、CopyOnWriteArrayList源码原理:

3.2、HashSet线程不安全演示

1、CopyOnWriteArraySet示例:

3.3、HashMap线程不安全演示

1、ConcurrentHashMap示例:


一、并发编程

    JUC就是java.util.concurrent工具包的简称,这是一个处理线程的工具包,JDK1.5开始出现的。

    管程又称Monitor监视器,又是是一种同步的机制,保证同一时间,只有一个线程访问被保护的数据或代码。

    用户线程:自定义线程基本都是用户线程。通过Thread.currentThread().isDaemon()来判断是否是用户线程,等于 false是用户线程。等于true 是守护线程。

    守护线程:特殊线程,运行在后台,比如:垃圾回收

    设置守护线程:  线程.setDaemon(true); 在执行start()之前设置。

1、主线程结束了,用户线程还在运行,jvm存活。

2、没有用户线程了,都是守护线程,jvm结束。

1、Lock接口

Lock中常用方法: 
1.void lock():获取锁。 
2.void lockInterruptibly():如果当前线程未被中断,则获取锁。 
3.Condition newCondition():返回绑定到此 Lock 实例的新 Condition 实例。 
4.boolean tryLock():仅在调用时锁为空闲状态才获取该锁。 
5.boolean tryLock(long time, TimeUnit unit):如果锁在给定的等待时间内空闲,
并且当前线程未被中断,则获取锁。 
6.void unlock():释放锁。
Condition接口
//@since 1.5
public interface Condition {

Condition接口中常用方法
1.void await():造成当前线程在接到信号或被中断之前一直处于等待状态。 
2.boolean await(long time, TimeUnit unit):造成当前线程在接到信号、被中断或到达指定
等待时间之前一直处于等待状态。 
3.long awaitNanos(long nanosTimeout):造成当前线程在接到信号、被中断或到达指定等待时
间之前一直处于等待状态。 
4.void awaitUninterruptibly():造成当前线程在接到信号之前一直处于等待状态。 
5.boolean awaitUntil(Date deadline):造成当前线程在接到信号、被中断或到达指定最后期
限之前一直处于等待状态。 
6.void signal():唤醒一个等待线程。 
7.void signalAll():唤醒所有等待线程。

面试:Lock与Synchronized的区别?用新的Lock有什么好处?举例说说?

    Lock不是Java语言内置的,Synchronized是Java语言的关键字,因此是内置特性。

    Synchronized不需要用户手动释放锁,当Synchronized方法或者Synchronized代码块执行完之后,系统会自动让线程释放对锁的占用,而Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁的现象。

1、原始构成

    synchronized是关键字属于JVM层面,monitorenter(底层是通过monitor对象来完成,其实wait/notify等方法也依赖于monitor对象只有在同步块或方法中才能调wait/notify等方法)

monitorexit

    Lock是具体类(java.util.concurrent.Locks.Lock)是api层面的锁。

2、使用方法

synchronized不需要用户去手动释放锁,当synchronized代码执行完成后系统会自动让线程释放对锁的占用

ReentrantLock则需要用户去手动释放锁,若没有主动释放锁,就有可能导致出现死锁现象

3、等待是否可中断

synchronized不可中断,除非抛出异常或者正常运行完成

ReentrantLock可中断

        1.设置超时方法tryLock(long timeout,TimeUnit unit)

        2.lockInterruptibly()放代码块中,调用interrupt()方法可中断

4、加锁是否公平

synchronized非公平锁

ReentrantLock两者都可以,默认非公平锁

5、锁绑定多个条件Condition

synchronized没有

ReentrantLock用来实现分组唤醒需要唤醒的线程们,可以精确唤醒,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程

示例:

import java.util.concurrent.locks.ReentrantLock;

public class LockSaleTicket {

	public static void main(String[] args) {
		LockTicket lockTicket = new LockTicket();
		new Thread(()-> {
			for (int i = 0; i < 40; i++) {
				lockTicket.sale();
			}
		},"aa").start();
		new Thread(()-> {
			for (int i = 0; i < 40; i++) {
				lockTicket.sale();
			}
		},"bb").start();
		new Thread(()-> {
			for (int i = 0; i < 40; i++) {
				lockTicket.sale();
			}
		},"cc").start();
	}
	
}

class LockTicket{
	
	private int number = 30;
	
	private final ReentrantLock lock = new ReentrantLock();
	
	//卖票方法
	public void sale() {
		lock.lock();
		try {
			if (number >0) {
				System.out.println(Thread.currentThread().getName() + ":卖出" + (number--)+" 剩余:"+number);
			}
		} finally {
			lock.unlock();
		}
	}
	
}

2、线程间的通信

一个线程+1,一个线程-1交替运行。即线程之间通信。

2.1、synchronized通信示例

/**
 * 线程通信的例子:有2个线程实现对一个初始值是0的变量
 * 		一个线程对值+1、一个线程对值-1
 * 		A线程  1 			B线程  0 
 * 		A线程  1 			B线程  0 
 * 		A线程  1 			B线程  0 
 * 		....
 */
class Share{
	//初始值
	private int number = 0;
	//+1方法
	public synchronized void add() throws InterruptedException {
		if (number != 0) {//number不是0,等待
			wait();
		}
		number++;//number值是0就+1
		System.out.println(Thread.currentThread().getName() + " :: " + number);
		notifyAll();
	}
	//-1方法
	public synchronized void subtract() throws InterruptedException {
		if (number != 1) {//number不是1,等待
			wait();
		}
		number--;//number值是1就-1
		System.out.println(Thread.currentThread().getName() + " :: " + number);
		notifyAll();
	}
	
}

public class ThreadDemo1 {

	public static void main(String[] args) {
		Share share = new Share();
		new Thread(()-> {
			for (int i = 0; i <= 10; i++) {
				try {
					share.add();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"A").start();
		
		new Thread(()-> {
			for (int i = 0; i <= 10; i++) {
				try {
					share.subtract();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"B").start();
	}
	
}

结果:

A :: 1
B :: 0
A :: 1
B :: 0
A :: 1
B :: 0
A :: 1
B :: 0
A :: 1
B :: 0
A :: 1
B :: 0
A :: 1
B :: 0
A :: 1
B :: 0
A :: 1
B :: 0
A :: 1
B :: 0
A :: 1
B :: 0

上面示例如果再增加2个线程一个+1,一个-1.结果还是正确的吗?

/**
 * 线程通信的例子:有2个线程实现对一个初始值是0的变量
 * 		一个线程对值+1、一个线程对值-1
 * 		A线程  1 			B线程  0 
 * 		A线程  1 			B线程  0 
 * 		A线程  1 			B线程  0 
 * 		....
 */
class Share{
	//初始值
	private int number = 0;
	//+1方法
	public synchronized void add() throws InterruptedException {
		if (number != 0) {//number不是0,等待
			wait();
		}
		number++;//number值是0就+1
		System.out.println(Thread.currentThread().getName() + " :: " + number);
		notifyAll();
	}
	//-1方法
	public synchronized void subtract() throws InterruptedException {
		if (number != 1) {//number不是1,等待
			wait();
		}
		number--;//number值是1就-1
		System.out.println(Thread.currentThread().getName() + " :: " + number);
		notifyAll();
	}
	
}

public class ThreadDemo1 {

	public static void main(String[] args) {
		Share share = new Share();
		new Thread(()-> {
			for (int i = 0; i <= 10; i++) {
				try {
					share.add();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"A").start();
		
		new Thread(()-> {
			for (int i = 0; i <= 10; i++) {
				try {
					share.subtract();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"B").start();
		new Thread(()-> {
			for (int i = 0; i <= 10; i++) {
				try {
					share.add();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"C").start();
		
		new Thread(()-> {
			for (int i = 0; i <= 10; i++) {
				try {
					share.subtract();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"D").start();
	}
	
}

结果:

A :: 1
B :: 0
A :: 1
B :: 0
A :: 1
B :: 0
A :: 1
B :: 0
A :: 1
B :: 0
A :: 1
B :: 0
A :: 1
B :: 0
C :: 1
A :: 2
C :: 3
B :: 2
C :: 3
A :: 4
C :: 5
B :: 4
C :: 5
A :: 6
C :: 7
D :: 6
B :: 5
D :: 4
C :: 5
A :: 6
C :: 7
D :: 6
B :: 5
D :: 4
C :: 5
D :: 4
C :: 5
D :: 4
C :: 5
D :: 4

结果和预期结果不符。原因是虚假唤醒问题。

解决虚假唤醒问题

wait()在哪里睡,就在哪里醒

解决方式:把if判断换成while判断,等线程醒来之后会继续进行判断,不满足条件继续睡,直到满足条件继续往下执行。

/**
 * 线程通信的例子:有2个线程实现对一个初始值是0的变量
 * 		一个线程对值+1、一个线程对值-1
 * 		A线程  1 			B线程  0 
 * 		A线程  1 			B线程  0 
 * 		A线程  1 			B线程  0 
 * 		....
 */
class Share{
	//初始值
	private int number = 0;
	//+1方法
	public synchronized void add() throws InterruptedException {
		while (number != 0) {//number不是0,等待
			wait();
		}
		number++;//number值是0就+1
		System.out.println(Thread.currentThread().getName() + " :: " + number);
		notifyAll();
	}
	//-1方法
	public synchronized void subtract() throws InterruptedException {
		while (number != 1) {//number不是1,等待
			wait();
		}
		number--;//number值是1就-1
		System.out.println(Thread.currentThread().getName() + " :: " + number);
		notifyAll();
	}
	
}

public class ThreadDemo1 {

	public static void main(String[] args) {
		Share share = new Share();
		new Thread(()-> {
			for (int i = 0; i <= 10; i++) {
				try {
					share.add();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"A").start();
		
		new Thread(()-> {
			for (int i = 0; i <= 10; i++) {
				try {
					share.subtract();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"B").start();
		new Thread(()-> {
			for (int i = 0; i <= 10; i++) {
				try {
					share.add();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"C").start();
		
		new Thread(()-> {
			for (int i = 0; i <= 10; i++) {
				try {
					share.subtract();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"D").start();
	}
	
}

结果:

A :: 1
B :: 0
A :: 1
B :: 0
A :: 1
B :: 0
A :: 1
B :: 0
A :: 1
B :: 0
C :: 1
B :: 0
A :: 1
B :: 0
C :: 1
B :: 0
A :: 1
B :: 0
C :: 1
D :: 0
A :: 1
B :: 0
C :: 1
D :: 0
A :: 1
B :: 0
C :: 1
D :: 0
A :: 1
D :: 0
C :: 1
D :: 0
A :: 1
D :: 0
C :: 1
D :: 0
C :: 1
D :: 0
C :: 1
D :: 0
C :: 1
D :: 0
C :: 1
D :: 0

2.2、Lock通信示例:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 线程通信的例子:有2个线程实现对一个初始值是0的变量
 * 		一个线程对值+1、一个线程对值-1
 * 		A线程  1 			B线程  0 
 * 		A线程  1 			B线程  0 
 * 		A线程  1 			B线程  0 
 * 		....
 */
class Share{
	
	private final Lock lock = new ReentrantLock();
	private final Condition condition  = lock.newCondition(); 

	
	//初始值
	private int number = 0;
	//+1方法
	public void add() throws InterruptedException {
		lock.lock();
		try {
			while (number != 0) {//number不是0,等待
				condition.await();
			}
			number++;//number值是0就+1
			System.out.println(Thread.currentThread().getName() + " :: " + number);
			condition.signalAll();
		} finally {
			lock.unlock();
		}
	}
	//-1方法
	public void subtract() throws InterruptedException {
		lock.lock();
		try {
			while (number != 1) {//number不是1,等待
				condition.await();
			}
			number--;//number值是1就-1
			System.out.println(Thread.currentThread().getName() + " :: " + number);
			condition.signalAll();
		} finally {
			lock.unlock();
		}
	}
	
}

public class ThreadDemo2 {

	public static void main(String[] args) {
		Share share = new Share();
		new Thread(()-> {
			for (int i = 0; i <= 10; i++) {
				try {
					share.add();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"A").start();
		
		new Thread(()-> {
			for (int i = 0; i <= 10; i++) {
				try {
					share.subtract();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"B").start();
		new Thread(()-> {
			for (int i = 0; i <= 10; i++) {
				try {
					share.add();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"C").start();
		
		new Thread(()-> {
			for (int i = 0; i <= 10; i++) {
				try {
					share.subtract();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"D").start();
	}
	
}

结果:

A :: 1
B :: 0
A :: 1
B :: 0
A :: 1
B :: 0
A :: 1
B :: 0
A :: 1
B :: 0
A :: 1
B :: 0
A :: 1
B :: 0
A :: 1
B :: 0
A :: 1
B :: 0
A :: 1
B :: 0
A :: 1
B :: 0
C :: 1
D :: 0
C :: 1
D :: 0
C :: 1
D :: 0
C :: 1
D :: 0
C :: 1
D :: 0
C :: 1
D :: 0
C :: 1
D :: 0
C :: 1
D :: 0
C :: 1
D :: 0
C :: 1
D :: 0
C :: 1
D :: 0

2.3、线程间定制化通信

线程A输出2次,线程B输出3次,线程C输出5次这种例子,相当于定制版通信

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 线程间定制化通信的例子:
 * 有3个线程,按照如下进行打印
 * A线程  打印2次 	B线程  打印3次  C线程  打印5次 
 * A线程  打印2次 	B线程  打印3次  C线程  打印5次 
 * A线程  打印2次 	B线程  打印3次  C线程  打印5次 
 * 		....
 * 打印5轮
 */
class ShareResource{
	
	private int flag = 1;//1:A、2:B、3:C
	
	private final Lock lock = new ReentrantLock();
	private final Condition c1  = lock.newCondition(); 
	private final Condition c2  = lock.newCondition(); 
	private final Condition c3  = lock.newCondition(); 
	
	//打印2次
	public void print2(int loop) throws InterruptedException {
		lock.lock();
		try {
			while (flag != 1) {//flag不是1,等待
				c1.await();
			}
			for (int i = 1; i <=2; i++) {
				System.out.println(Thread.currentThread().getName() + " :: "+ i +" 轮数:"+ loop);
			}
			flag=2;
			c2.signal();
		} finally {
			lock.unlock();
		}
	}
	//打印3次
	public void print3(int loop) throws InterruptedException {
		lock.lock();
		try {
			while (flag != 2) {//flag不是2,等待
				c2.await();
			}
			for (int i = 1; i <=3; i++) {
				System.out.println(Thread.currentThread().getName() + " :: "+ i +" 轮数:"+ loop);
			}
			flag=3;
			c3.signal();
		} finally {
			lock.unlock();
		}
	}
	//打印5次
	public void print5(int loop) throws InterruptedException {
		lock.lock();
		try {
			while (flag != 3) {//flag不是3,等待
				c3.await();
			}
			for (int i = 1; i <=5; i++) {
				System.out.println(Thread.currentThread().getName() + " :: "+ i +" 轮数:"+ loop);
			}
			flag=1;
			c1.signal();
		} finally {
			lock.unlock();
		}
	}
}
public class ThreadDemo3 {

	public static void main(String[] args) {
		ShareResource share = new ShareResource();
		new Thread(()-> {
			for (int i = 1; i <= 5; i++) {
				try {
					share.print2(i);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"A").start();
		new Thread(()-> {
			for (int i = 1; i <= 5; i++) {
				try {
					share.print3(i);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"B").start();
		new Thread(()-> {
			for (int i = 1; i <= 5; i++) {
				try {
					share.print5(i);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"C").start();
	}
}

结果:

A :: 1 轮数:1
A :: 2 轮数:1
B :: 1 轮数:1
B :: 2 轮数:1
B :: 3 轮数:1
C :: 1 轮数:1
C :: 2 轮数:1
C :: 3 轮数:1
C :: 4 轮数:1
C :: 5 轮数:1
A :: 1 轮数:2
A :: 2 轮数:2
B :: 1 轮数:2
B :: 2 轮数:2
B :: 3 轮数:2
C :: 1 轮数:2
C :: 2 轮数:2
C :: 3 轮数:2
C :: 4 轮数:2
C :: 5 轮数:2
A :: 1 轮数:3
A :: 2 轮数:3
B :: 1 轮数:3
B :: 2 轮数:3
B :: 3 轮数:3
C :: 1 轮数:3
C :: 2 轮数:3
C :: 3 轮数:3
C :: 4 轮数:3
C :: 5 轮数:3
A :: 1 轮数:4
A :: 2 轮数:4
B :: 1 轮数:4
B :: 2 轮数:4
B :: 3 轮数:4
C :: 1 轮数:4
C :: 2 轮数:4
C :: 3 轮数:4
C :: 4 轮数:4
C :: 5 轮数:4
A :: 1 轮数:5
A :: 2 轮数:5
B :: 1 轮数:5
B :: 2 轮数:5
B :: 3 轮数:5
C :: 1 轮数:5
C :: 2 轮数:5
C :: 3 轮数:5
C :: 4 轮数:5
C :: 5 轮数:5

3、集合的线程安全

3.1、List集合线程不安全示例

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class ThreadDemo4 {

	public static void main(String[] args) {
		List<String> list = new ArrayList<>();
		for (int i = 0; i < 20; i++) {
			new Thread(()->{
				list.add(UUID.randomUUID().toString().substring(0, 4));
				System.out.println(list);
			},String.valueOf(i)).start();
		}
	}
}

执行会发生异常:java.util.ConcurrentModificationException

导致原因:

    并发争抢修改导致,一个人正在写入,另外一个人过来抢夺,导致数据不一致异常,并发修改异常。

解决方式:

    1、Vector

    2、Collections

    3、CopyOnWriteArrayList

1、Vector示例:
import java.util.*;

public class ThreadDemo4 {

	public static void main(String[] args) {
		List<String> list = new Vector<>();
		for (int i = 0; i < 20; i++) {
			new Thread(()->{
				list.add(UUID.randomUUID().toString().substring(0, 4));
				System.out.println(list);
			},String.valueOf(i)).start();
		}
	}
}
2、Collections示例:
import java.util.*;

public class ThreadDemo4 {

	public static void main(String[] args) {
		List<String> list = Collections.synchronizedList(new ArrayList<>());
		for (int i = 0; i < 20; i++) {
			new Thread(()->{
				list.add(UUID.randomUUID().toString().substring(0, 4));
				System.out.println(list);
			},String.valueOf(i)).start();
		}
	}
}
3、CopyOnWriteArrayList示例:
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

public class ThreadDemo4 {

	public static void main(String[] args) {
		List<String> list = new CopyOnWriteArrayList<>();
		for (int i = 0; i < 20; i++) {
			new Thread(()->{
				list.add(UUID.randomUUID().toString().substring(0, 4));
				System.out.println(list);
			},String.valueOf(i)).start();
		}
	}
}
4、CopyOnWriteArrayList源码原理:

    用到了写时复制技术。add()先加锁,并把原集合进行复制,不影响读操作。写完之后重新覆盖原集合,解锁。

写时复制:

        CopyOnWrite容器即写时复制的容器。往一个容器添加元素的时候,不直接往当前容器Object[]添加,而是先将当前容器Object[]进行Copy,复制出一个新的容器Object[] newElements,然后往新的容器Object[] newElements 里添加元素,添加完元素之后,再将原容器的引用指向新的容器setArray(newElements);这样做的好处是可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
...
    final transient ReentrantLock lock = new ReentrantLock();
    private transient volatile Object[] array;
    public CopyOnWriteArrayList() {
        setArray(new Object[0]);
    }
    final void setArray(Object[] a) {
        array = a;
    }
    public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

3.2、HashSet线程不安全演示

import java.util.*;

public class ThreadDemo4 {

	public static void main(String[] args) {
		Set<String> set = new HashSet<>();
		for (int i = 0; i < 20; i++) {
			new Thread(()->{
				set.add(UUID.randomUUID().toString().substring(0, 4));
				System.out.println(set);
			},String.valueOf(i)).start();
		}
	}
}

执行会发生异常:java.util.ConcurrentModificationException

解决方式:

    1、CopyOnWriteArraySet

    2、Collections.synchronizedSet(new HashSet<>());

1、CopyOnWriteArraySet示例:
import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;

public class ThreadDemo4 {

	public static void main(String[] args) {
		Set<String> set = new CopyOnWriteArraySet<>();
		for (int i = 0; i < 20; i++) {
			new Thread(()->{
				set.add(UUID.randomUUID().toString().substring(0, 4));
				System.out.println(set);
			},String.valueOf(i)).start();
		}
	}
}

CopyOnWriteArraySet底层还是CopyOnWriteArrayList

public class CopyOnWriteArraySet<E> extends AbstractSet<E>
        implements java.io.Serializable {
...
    private final CopyOnWriteArrayList<E> al;

    /**
     * Creates an empty set.
     */
    public CopyOnWriteArraySet() {
        al = new CopyOnWriteArrayList<E>();
    }
...
}

3.3、HashMap线程不安全演示

import java.util.*;

public class ThreadDemo4 {

	public static void main(String[] args) {
		Map<String,String> map = new HashMap<>();
		for (int i = 0; i < 20; i++) {
			String key = String.valueOf(i);
			new Thread(()->{
				map.put(key,UUID.randomUUID().toString().substring(0, 4));
				System.out.println(map);
			},String.valueOf(i)).start();
		}
	}
}

执行会发生异常:java.util.ConcurrentModificationException

解决方式:

    1、ConcurrentHashMap

    2、Collections.synchronizedMap(new HashMap<>());

1、ConcurrentHashMap示例:
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class ThreadDemo4 {

	public static void main(String[] args) {
		Map<String,String> map = new ConcurrentHashMap<>();
		for (int i = 0; i < 20; i++) {
			String key = String.valueOf(i);
			new Thread(()->{
				map.put(key,UUID.randomUUID().toString().substring(0, 4));
				System.out.println(map);
			},String.valueOf(i)).start();
		}
	}
}


Java 并发编程(二)-多线程锁分类

Java 多线程学习(一)

一个程序员最重要的能力是:写出高质量的代码!!
有道无术,术尚可求也,有术无道,止于术。
无论你是年轻还是年长,所有程序员都需要记住:时刻努力学习新技术,否则就会被时代抛弃!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杀神lwz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值