公平锁:先来后到
非公平锁:会根据运行时间、级别进行分配
package org.example;
/**
* @author sshdg
*/
public class SynchronizedDemo {
public static void main(String[] args) {
SaleTicket saleTicket = new SaleTicket();
new Thread(()->{
for (int i = 0; i < 50; i++) {
saleTicket.sale();
}
}, "A").start();
new Thread(()->{
for (int i = 0; i < 50; i++) {
saleTicket.sale();
}
}, "B").start();
}
}
class SaleTicket {
private int number = 100;
public synchronized void sale(){
System.out.println(Thread.currentThread().getName()+"卖出第"+(number--)+"张票,还剩"+number);
}
}
Lock是juc包下的锁,与synchronized同步锁有一些区别,功能更加强大
package org.example;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author: sshdg
* @Date: 2020/9/4 20:03
*/
public class LockDemo {
public static void main(String[] args) {
SaleTicket2 saleTicket = new SaleTicket2();
new Thread(()->{
for (int i = 0; i < 50; i++) {
saleTicket.sale();
}
}, "A").start();
new Thread(()->{
for (int i = 0; i < 50; i++) {
saleTicket.sale();
}
}, "B").start();
}
}
class SaleTicket2 {
private int number = 100;
Lock lock = new ReentrantLock();
public void sale() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"卖出第"+(number--)+"张票,还剩"+number);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
package org.example.pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author: sshdg
* @Date: 2020/9/4 21:22
*/
public class ProducerConsumer {
public static void main(String[] args) {
Date date = new Date();
new Thread(()->{
for (int i = 0; i < 10; i++) {
date.increment();
}
}, "A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
date.decrement();
}
}, "B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
date.increment();
}
}, "C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
date.decrement();
}
}, "D").start();
}
}
class Date {
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
/**
* 生产
*/
public void increment(){
lock.lock();
try {
while (number > 0){
//等待
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName()+":number->"+number);
//通知其他线程生产完毕
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
/**
* 消费
*/
public void decrement(){
lock.lock();
try {
while (number == 0){
//等待
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName()+":number->"+number);
//通知其他线程消费完毕
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
package org.example.pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author: sshdg
* @Date: 2020/9/5 11:12
*/
public class A {
public static void main(String[] args) {
Date2 date2 = new Date2();
new Thread(()->{
for (int i = 0; i < 10; i++) {
date2.printA();
}
}, "A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
date2.printB();
}
}, "B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
date2.printC();
}
}, "C").start();
}
}
class Date2 {
private int number = 1;
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
public void printA(){
lock.lock();
try {
while (number != 1){
//等待
condition1.await();
}
number = 2;
System.out.println(Thread.currentThread().getName()+"--> AA");
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
while (number != 2){
//等待
condition2.await();
}
number = 3;
System.out.println(Thread.currentThread().getName()+"--> BB");
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
while (number != 3){
//等待
condition3.await();
}
number = 1;
System.out.println(Thread.currentThread().getName()+"--> CC");
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
锁的是对象,方法的调用者。一个对象一把锁。
以synchronized
为例,锁只有一把,谁(线程)先抢到,谁先执行
package org.example.lock8;
import java.util.concurrent.TimeUnit;
/**
* @Author: sshdg
* @Date: 2020/9/5 15:24
*/
public class Test1 {
public static void main(String[] args) {
Phone phone = new Phone();
//以这个为例,大多数情况下是A线程先抢到锁,但是这里将其睡眠1s,所以B会先执行
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(1);
//TimeUnit.MICROSECONDS.sleep(1);睡 1微秒 也是一样,因为计算机的速度非常快
} catch (InterruptedException e) {
e.printStackTrace();
}
phone.sendSMS();
}, "A").start();
new Thread(()->{
phone.call();
}, "B").start();
}
}
class Phone{
public synchronized void sendSMS(){
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
而如果让方法睡1秒,则先抢到的要1s后才会执行完,在此期间占用此锁,其他线程无法执行带锁的方法。但是对无锁的方法没有影响
不建议通过对象实例访问静态方法,这样写仅为测试锁
静态方法只有一个,是在类加载的时候就有的
因此如下这种情况下,还是会先睡4s后打印发短信,然后执行打电话。
package org.example.lock8;
import java.util.concurrent.TimeUnit;
/**
* @Author: sshdg
* @Date: 2020/9/5 15:24
*/
public class Test1 {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(()->{
phone.sendSMS();
}, "A").start();
new Thread(()->{
phone2.call();
}, "B").start();
}
}
class Phone{
public static synchronized void sendSMS(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public static synchronized void call(){
System.out.println("打电话");
}
}
另外:静态方法的锁和普通方法的锁不是同一个。
package org.example.unsafe;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* @Author: sshdg
* @Date: 2020/9/5 19:52
*/
public class ListTest {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
}, Integer.toString(i)).start();
}
}
}
如上代码,很容易会出现java.util.ConcurrentModificationException
并发修改异常,ArrayList不是线程安全的。
解决方法:
Vector
是线程安全的,可以将ArrayList替换成VectorList list = Collections.synchronizedList(new ArrayList<>());
List list = new CopyOnWriteArrayList<>();
CopyOnWriteArrayList源码:
其实现原理是,复制一个数组向其中添加元素,然后将复制的新数组赋值给CopyOnWriteArrayList
,这样间接向CopyOnWriteArrayList
中插入了数据,且使用Lock锁保证了线程安全
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
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();
}
}
同理,set也会有线程不安全的问题,可以使用CopyOnWriteArraySet解决,也可以使用Collections工具类来解决
package org.example.unsafe;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* @Author: sshdg
* @Date: 2020/9/5 20:38
*/
public class SetTest {
public static void main(String[] args) {
// Set set = new HashSet<>();
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 1; i <= 10; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(Thread.currentThread().getName()+"->"+set);
}, Integer.toString(i)).start();
}
}
}
原理和上面两种不同
package org.example.unsafe;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* @Author: sshdg
* @Date: 2020/9/5 21:38
*/
public class MapTest {
public static void main(String[] args) {
// Map map = new HashMap<>();
Map<String, String> map = new ConcurrentHashMap<>();
for (int i = 1; i <= 10; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
System.out.println(Thread.currentThread().getName()+"->"+map);
}, Integer.toString(i)).start();
}
}
}
package org.example.callable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @Author: sshdg
* @Date: 2020/9/5 21:51
*/
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread myThread = new MyThread();
FutureTask<Integer> futureTask = new FutureTask<>(myThread);
new Thread(futureTask).start();
// get()会阻塞,如果call()方法有耗时的操作,会一直在这等着,因此常放在最后一行,或使用ajax的方式,让其在后台慢慢加载
Integer integer = futureTask.get();
System.out.println(integer);
}
}
class MyThread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("call()");
return 1024;
}
}
另:call方法执行的结果是会被缓存的,也就是说:如下代码只会输出一行call()
new Thread(futureTask,"A").start();
new Thread(futureTask,"B").start();