创建线程的方式:
1.继承Thread类
Thread类中有一个start方法开启一个线程,在这个方法下面有一个start0的本地方法,意味着Java不能直接的创建线程。
start方法去调用重写的run方法
2.实现Runable接口
3.实现Callable接口
可以获取返回值。上面两种方法无法获取返回值。
public class UserCallable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<Integer> futureTask = new FutureTask<>(new Task());
//开启线程
new Thread(futureTask).start();
//获取返回值
Integer integer = futureTask.get();
System.out.println(integer);
}
static class Task implements Callable<Integer>{
public Integer call() throws Exception {
return 1;
}
}
}
守护线程:
要将线程设置为守护线程,我们要做的就是调用Thread.setDeamon
Thread deamonThread = new Thread();
//开启守护线程
deamonThread.setDeamon(true);
deamonThread.start();
守护线程会随着被守护线程的结束而结束。
线程的状态:
public enum State {
/**
* 尚未启动的线程的线程状态。
*/
NEW,
/**
* 可运行线程的线程状态。 处于可运行状态的线程正在
* Java 虚拟机中执行,但它可能正在等待来自操作系统
* 的其他资源,例如处理器。
*/
RUNNABLE,
/**
* 线程阻塞等待监视器锁的线程状态。 处于阻塞状态
* 的线程正在等待监视器锁进入同步块/方法或在调用
* Object.wait后重新进入同步块/方法。
*/
BLOCKED,
/**
* 等待线程的线程状态。 由于调用以下方法之一,线程处于等待状态:
* Object.wait没有超时
* Thread.join没有超时
* LockSupport.park
* 处于等待状态的线程正在等待另一个线程执行特定操作。 例如,在对
* 象上调用Object.wait()的线程正在等待另一个线程在该对象上
* Object.notify()或Object.notifyAll() 。 调用Thread.join()
* 的线程正在等待指定的线程终止。
*/
WAITING,
/**
* 具有指定等待时间的等待线程的线程状态。 由于使用指定的正等待
* 时间调用以下方法之一,线程处于定时等待状态:
* Thread.sleep
* Object.wait超时
* Thread.join超时
* LockSupport.parkNanos
* LockSupport.parkUntil
*/
TIMED_WAITING,
/**
* 终止线程的线程状态。 线程已完成执行。
*/
TERMINATED;
}
Synchronized锁谁:
方法
|–实例方法 —>锁实例对象
|–静态方法 —>锁类对象
代码块
|–this —>实例对象
|–类名.Class —>类对象
Lock锁
可以在创建锁的时候用构造方法控制。
公平锁:可以先来后到
非公平锁:可以插队(默认)
售票
出现问题的版本:出现票被重复卖等现象
public class SaleTickedDemo1 {
public static void main(String[] args) {
//公共资源
Ticked ticked = new Ticked();
new Thread(() -> { for (int i = 0; i < 60; i++) ticked.sale(); }, "A").start();
new Thread(() -> { for (int i = 0; i < 60; i++) ticked.sale(); }, "B").start();
}
}
class Ticked {
//票数
private int number = 50;
//售票的方法
public void sale() {
//有票就卖
if (number > 0) {
try {
//放大问题发生
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票,剩余:" + number);
}
}
}
使用Synchronized解决的版本:问题得到了解决
//方法上添加关键字synchronized
public synchronized void sale()
使用lock锁解决的版本:
这种写法不规范,可以使用try-finally上锁解锁
public void sale() {
//有票就卖
if (number > 0) {
try {
//放大问题发生
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
//加锁
lock.lock();
System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票,剩余:" + number);
//解锁
lock.unlock();
}
}
Synchronized和lock区别
1.Synchronized内置的Java关键字,lock是一个java类
2.Synchronized无法去判断获取锁的状态,lock可以判断
3.Synchronized会自动释放锁,lock必须手动放弃锁,如果不释放锁,会造成死锁
4.Synchronized线程1(获取锁,阻塞),线程(死等)lock可以尝试的获取锁,不会一直等待
5.Synchronized适合锁少量的代码块,lock适合锁大量的同步代码
消费者生产者问题
Synchronized版本:
public class ProviderConsumerDemo {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"生产者").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"消费者").start();
}
}
class Data{
private int number = 0;
//+1
public synchronized void increment() throws InterruptedException {
if (number!=0){
//等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程,加完了
this.notifyAll();
}
//-1
public synchronized void decrement() throws InterruptedException {
if (number==0){
//等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程,减完了
this.notifyAll();
}
}
问题:上述的代码如果是一个生产者一个消费者的话没有问题,但是如果出现多个就会出现虚假唤醒的问题。
可以把Data中的increment和decrement方法中判断的if改为while,因为if只判断了一次。
Lock版本:
通过lock可以找到Condition,condition中await方法和signalAll跟wait和notifyAll效果一样
class Data{
private int number = 0;
Lock lock = new ReentrantLock();
//获取condition
Condition condition = lock.newCondition();
//+1
public void increment() throws InterruptedException {
lock.lock();
try {
while (number!=0){
//等待
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程,加完了
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//-1
public void decrement() throws InterruptedException {
lock.lock();
try {
while (number==0){
//等待
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程,减完了
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
可以使用创建多个condition,使用await和signal配合,精准的控制线程唤醒。
集合类不安全
ArrayList
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(Thread.currentThread().getName()+"=="+list);
},String.valueOf(i)).start();
}
}
出现问题:
ConcurrentModificationException 并发修改异常
解决:
1.使用Vector ,加了Synchronized关键字
2.Collections.synchronizedList(new ArrayList<>());
3.new CopyOnWriteArrayList<>();使用这个类
原理:
先复制一份,写入元素,然后放回去
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
public static void main(String[] args) {
Set<String> set = new HashSet<>();
for (int i = 0; i < 30; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(Thread.currentThread().getName()+"=="+set);
},String.valueOf(i)).start();
}
}
同样,还是会出现ConcurrentModificationException问题:
解决方法;
1.Collections.synchronizedSet(new HashSet<>());
2.new CopyOnWriteArraySet<>();
map
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
for (int i = 0; i < 30; i++) {
new Thread(()->{
map.put(UUID.randomUUID().toString().substring(0,5),"0");
System.out.println(Thread.currentThread().getName()+"=="+map);
},String.valueOf(i)).start();
}
}
不出所料的继续出现ConcurrentModificationException
解决:
1.new Hashtable<>(); 加了synchronized修饰方法
2.new ConcurrentHashMap()