synchronized关键字【重点】
多行代码的原子性问题
买票案例:
TicketTask.java
//卖票任务
public class TicketTask implements Runnable {
int count = 100;//票数
@Override
public void run() {
while (true) {
if (count > 0) {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("卖出第" + count + "张票");
count--;
} else {
break;
}
}
}
}
Test.java
public class Test {
public static void main(String[] args) {
TicketTask ticketTask = new TicketTask();
Thread thread1 = new Thread();
Thread thread2 = new Thread();
Thread thread3 = new Thread();
thread1.start();
thread2.start();
thread3.start();
}
}
此时会出现以下情况
数据重复:当一个线程执行完卖票后,还没来得及对票数-1,被其他线程抢走CPU,导致出现重复数据。
数据异常(出现-1这种非法数据):当剩下最后一张票时,多个线程都经过了if判断,进去卖票的代码块中,然后依次卖出第1张、第0张、第-1张。
synchronized介绍
作用是可以让多句代码具有原子性(当某个线程执行多句代码过程中不被其他线程打断)。
解决方案1——同步代码块
格式:
synchronized(锁对象){
需要同步的代码(需要保持原子性的代码)
}
代码实现:
TicketTask.java
//卖票任务
public class TicketTask implements Runnable {
int count = 100;//票数
Object obj = new Object();
@Override
public void run() {
while (true) {
//同步代码块
synchronized (obj) {
}
if (count > 0) {
System.out.println("卖出第" + count + "张票");
count--;
}
}
}
}
解决方案2——同步方法
格式:
publicsynchronized void 方法名{
可能会产生线程安全问题的代码}
代码实现:
Ticket.java
//卖票任务
public class TicketTask implements Runnable {
int count = 100;//票数
@Override
public void run() {
while (true) {
sellTicket();
}
}
public synchronized void sellTicket() {
if (count > 0) {
System.out.println("卖出第" + count + "张票");
count--;
}
}
}
注意:
1、同步代码块和同步方法原理差不多,同步代码块的同步锁需要我们自己指定,而同步方法的同步锁,默认使用当前对象this。
2、如果同步方法是static,默认使用当前类的字节码文件作为锁对象。
解决方案3——Lock锁
Lock是一个接口,我们需要使用其实现类ReentrantLock。
方法:
public ReentrantLock();
public void lock();
public void unlock();
格式:
ReentrantLock lock=new ReentrantLock();//创建一个锁对象
lock.lock();//加锁
lock.unlock();//解锁
代码实现:
Ticket.java
//卖票任务
public class TicketTask implements Runnable {
int count = 100;//票数
//创建一个lock锁
ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock();//加锁
if (count > 0) {
System.out.println("卖出第" + count + "张票");
count--;
}
lock.unlock();//解锁
}
}
}
并发包
什么是并发包?是指在JDK的并发包里提供了包含一个在公并发情况使用集合或工具类,使用这些集合或工具类时,能保证高并发情况下是安全的。
1、CopyOnWriteArrayList
ArrayList是不安的
代码实现:
MyThread.java
//这样最终结果会抛出异常
public class MyThread extends Thread {
ArrayList arrayList = new ArrayList();
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
arrayList.add(i);
}
System.out.println("添加完毕");
}
}
使用CopyOnWriteArrayList可以保证线程安全
代码实现:
MyThread.java
public class MyThread extends Thread {
//ArrayList arrayList = new ArrayList();
public static List<Integer> list = new CopyOnWriteArrayList<>();
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
list.add(i);
}
System.out.println("添加完毕");
}
}
2、CopyOnWriteArraySet
代码实现:
MyThread.java
public class MyThread extends Thread {
//ArrayList arrayList = new ArrayList();
//public static List<Integer> list = new CopyOnWriteArrayList<>();
public static Set<Integer> set = new CopyOnWriteArraySet<>();
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
set.add(i);
}
System.out.println("添加完毕");
}
}
3、ConcurrentHashMap
代码实现:
MyThread.java
public class MyThread extends Thread {
//ArrayList arrayList = new ArrayList();
//public static List<Integer> list = new CopyOnWriteArrayList<>();
//public static Set<Integer> set = new CopyOnWriteArraySet<>();
public static Map<Integer, Integer> map = new ConcurrentHashMap<>();
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
map.put(i, i);
}
System.out.println("添加完毕");
}
}
4、CountDownLatch
作用:允许一个线程,等待其他线程完成某种操作后,当前线程执行。
构造方法:
public CountDownLatch(int count);//需要传入计数器,需要等待的线程收
成员方法:
public void await throws InterruptedException;//让当前线程等待
public void countDown();//计数器-1
代码实现:
MyThread.java
5、CyclicBarrier
作用:让多个线程都到达了某种要求之后,新的任务才嫩执行。
构造方法:
public CyclicBarrier(int pattise,Runnable barrierAction);//需要多少个线程,所有线程都满足要求了,执行的任务
成员方法:
public int await ();
6、Semaphore
构造方法:
public Semaphore(int permits);//参数permits表示最多允许有多少个线程并发执行。
成员方法:
public void semaphore.acquire();//获取线程的许可证
public void semaphore.release();//释放线程的许可证
代码实现:
MyThread.java
public class MyThread extends Thread {
private Semaphore semaphore;
public MyThread(Semaphore semaphore) {
this.semaphore = semaphore;
}
@Override
public void run() {
//从Semaphore获取线程许可
try {
Thread.sleep(1000);
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "线程执行了" + System.currentTimeMillis());
//归还线程的许可
semaphore.release();
}
}
7、Exchanger
作用:交换者,用于线程间的数据交换。
构造方法:
public Exchanger<V>();
成员方法:
public V exchange(V x);//参数为发给别的线程的数据,返回值别的线程发过来的数据。
代码实现:
Thread1.java
public class Thread1 extends Thread {
private Exchanger<String> exchanger;
public Thread1(Exchanger<String> exchanger) {
this.exchanger = exchanger;
}
@Override
public void run() {
System.out.println("线程1将AAA发送给线程2");
String result = null;
try {
result = exchanger.exchange("AAA");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程1,获取到线程2送的礼物"+result);
}
}
Thread2.java
public class Thread2 extends Thread {
private Exchanger<String> exchanger;
public Thread2(Exchanger<String> exchanger) {
this.exchanger = exchanger;
}
@Override
public void run() {
System.out.println("线程2,要将BBB,送给线程1");
String result = null;
try {
result = exchanger.exchange("BBB");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程2,获取到线程1的礼物" + result);
}
}
Test.java
public class Test {
public static void main(String[] args) {
//创建线程间数据交换
Exchanger<String> exchanger = new Exchanger<>();
//创建线程1
Thread1 thread1 = new Thread1(exchanger);
thread1.start();
Thread2 thread2 = new Thread2(exchanger);
thread2.start();
}
}
小结
当我回头时,你就站在原地!!!