以下讲的是在同一进程下多线程操作
什么是JUC
JUC就是java.util.concurrent工具包
线程和进程
进程:一个程序
一个进程可以包含多个线程,至少一个!
java默认包含两个线程:main、GC
线程:开一个进程,执行某个事物(线程负责)
对应Java而言实现线程:Thread、Runnable、Callable
java真的可以开启线程吗?否 ,只能通过本地方法去调用底层的C++,java无法直接操控硬件,是运行在虚拟机上的
并发和并行
并发(多线程操作同一个资源):CPU单核,CPU在多线程中快速切换,造成多线程的假象。从微观上还是单线程
并行(多个人一起行走):CPU多核,多个线程同时执行
线程有几个状态
可以查看源码 Thread类中public enum State {}方法
new(新生)
runnable(运行)
blocked(阻塞)
waiting(等待、死死的等)
timed_waiting(超时等待)
terminated(终止)
wait和sleep区别
1. 来自不同的类
wait => Object
sleep => Thread
2. 使用的范围不同
wait:必须在同步代码块中使用
sleep:可以在任何地方使用
3. 是否需要捕获异常
wait 不需要捕获异常
sleep 必须要捕获异常
Synchronized
synchronized是java中的关键字,一种同步锁。保证它修饰的代码块在同一时刻只有一个线程执行
public void saleTest(){
//并发:多线程操作同一个资源类,三个线程同时卖票
Ticket ticket = new Ticket();
//lambda表达式 (参数)->{代码}
new Thread(()->{
for (int i = 0; i < 50; i++) {
ticket.sale();
}
}, "A").start();
new Thread(()->{
for (int i = 0; i < 50; i++) {
ticket.sale();
}
}, "B").start();
new Thread(()->{
for (int i = 0; i < 50; i++) {
ticket.sale();
}
}, "C").start();
}
public class Ticket {
//属性
private int num = 100;
//synchronized 本质:队列,锁
public synchronized void sale() {
if (num > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了第" + (num--) + "张票,剩余:" + num);
}
}
}
Lock
公平锁:十分公平,可以先来后到 。如果前面有个3h的线程,那么后面3s就要等3h
非公平锁:十分不公平,可以插队 (默认)
public void saleTest(){
//并发:多线程操作同一个资源类,三个线程同时卖票
Ticket ticket = new Ticket();
//lambda表达式 (参数)->{代码}
new Thread(()->{
for (int i = 0; i < 50; i++) {
ticket.sale();
}
}, "A").start();
new Thread(()->{
for (int i = 0; i < 50; i++) {
ticket.sale();
}
}, "B").start();
new Thread(()->{
for (int i = 0; i < 50; i++) {
ticket.sale();
}
}, "C").start();
}
public class Ticket {
//属性
private int num = 100;
//锁
Lock lock = new ReentrantLock();
public void sale() {
//加锁
lock.lock();
try {
if (num > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了第" + (num--) + "张票,剩余:" + num);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//解锁
lock.unlock();
}
}
}
Synchronized和Lock区别
Synchronized 内置的关键字,Lock是一个Java类
Synchronized 无法判断获取锁的状态,Lock可以判断是否获取到了锁
Synchronized 会自动释放锁,Lock 必须要手动释放锁!如果不释放锁,会出现死锁
Synchronized 线程1(获得锁,阻塞)、线程2(等待,傻傻的等);Lock锁就不一定会等待下去
Synchronized 可重入锁,不可以中断,非公平;Lock,可重入锁,可以判断锁,非公平(可以自己设置)
Synchronized 适合锁少量的代码同步问题,Lock 适合锁大量的同步代码
锁住的是谁?
锁住的对象是方法的调用者
如果方法改成静态同步方法,那么锁住的就是 class
以下用的是单元测试,在单元测试中,子线程中存在阻塞、死亡状态时,单元测试会立即停止所有子线程。所以在sendSms中睡2秒是无法看到后面的打印输出的,可以直接用main方法测试,就可以看到效果。
/**
* 中间睡1秒,很明显 先发短信
*/
@Test
public void phoneTest(){
Phone phone = new Phone();
new Thread(()->{
phone.sendSms();
}, "A").start();
try {
//睡1秒
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
}, "B").start();
}
public class Phone {
public synchronized void sendSms() {
System.out.println("发短信");
}
public synchronized void call() {
System.out.println("打电话");
}
}
/**
* synchronized 锁住的对象是<方法的调用者>,锁住了phone对象
* A线程先开始,先锁住了phone对象,先发短信
*/
@Test
public void phoneTest(){
Phone phone = new Phone();
new Thread(()->{
phone.sendSms();
}, "A").start();
try {
//睡1秒
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
}, "B").start();
}
public class Phone {
public synchronized void sendSms() {
try {
//睡两秒
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call() {
System.out.println("打电话");
}
}
未完待续。。。