JUC,即java.util.concurrent这个处理线程的工具包,始于JDK1.5,其中下有三个包,为:
进程与线程的关系:
把进程看作是现实生活中的公司,如京东。线程则可看作是其下的某一个职能部门,负责完成某任务,如开发部门。
举个例子:
DOS窗口运行java HelloWorld,先启动JVM,JVM是一个进程,JVM启动一个主线程调用main方法,同时再启动一个垃圾回收线程来负责看护、回收垃圾。(也就是说Java程序至少两线程并发,main方法对应的主线程+GC)
进程A和进程B的内存关系:
Java中,线程A和线程B,堆内存和方法区内存共享,但栈内存独立,一个线程一个栈
。如启动了10个线程,就会有10个栈空间,每个栈和每个栈之间互不干扰,各自执行各自的,这就是多线程并发。
举个例子:
Java中的多线程机制,目的就是为了提高程序的处理效率, 如火车站看成是一个进程,则每个售票小窗口就是一个个线程,甲在窗口1买票,乙在窗口2买票,谁也不用等谁 一个个售票窗口就像一个个栈,有自己独立的空间。售票大厅这个共用空间就像堆和方法区
先说下串行和并行:
而并行和并发
,则是:
并发:同一时刻,多个线程在访问同一个资源
,多个线程对一个点,对应的例子: 春运抢票 电商秒杀
并行:多项工作一起执行,之后再汇总例子,举例就是泡方便面,电水壶烧水,一边撕调料倒入桶中
Java中的线程分两类:
写个测试代码:
public static void main(String[] args){
Thread aa = new Thread( () -> {
System.out.println(Thread.currentThread().getName() + "::" + Thread.currentThread().isDaemon());
while (true) {
}
}, "aa");
aa.setDaemon(true);
aa.start();
System.out.println(Thread.currentThread().getName() + " over");
}
可以看到,aa.setDaemon(true)被注释时,aa线程属于用户自定义线程,此时main线程结束以后,自定义的aa线程不受影响,JVM继续存活:
aa线程被set为守护线程后,则当用户线程都结束时,守护线程自动结束:
锁是
控制多个线程对共享资源进行访问的工具
。通常,锁提供了对共享资源的独占访问。一次只能有一个线程获得锁,对共享资源的所有访问都需要首先获得锁。不过,某些锁可能允许对共享资源并发访问,如ReadwriteLock的读取锁。
synchronized是Java关键字,表示一种同步锁,这个关键字:
synchronized(线程共享对象){
…
//线程同步代码块,即要排队执行的代码块
…
}
public synchronized void doSome(){
//...
}
多线程编程的步骤
模拟三个售票员同时对外出售30张票,三个售票员,即三个线程,资源类就是票类,资源类的属性是票的数量:
//资源类
class Ticket{
private Integer number = 30;
public synchronized void sale(){
if(number > 0 ){
System.out.println(Thread.currentThread().getName() + ": 卖出票,剩余" + number--);
}
}
}
开三个线程,调用资源类中的方法:
public class SaleTicket {
public static void main(String[] args) {
//多个线程共用这个资源对象
Ticket ticket = new Ticket();
new Thread(() -> {
for(int i = 0 ; i < 40; i++ ){
ticket.sale();
}
},"AA").start();
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0 ; i < 40; i++ ){
ticket.sale();
}
}
},"BB").start();
Thread cc = new Thread(() -> {
for (int i = 0; i < 40; i++) {
ticket.sale();
}
}, "CC");
cc.setPriority(Thread.MAX_PRIORITY);
cc.start();
}
}
API文档:
https://tool.oschina.net/apidocs/apidoc?api=jdk-zh
Lock接口比synchronized更灵活,其实现类有:
ReentrantLock即可重入锁,类比排队上厕所,进门后锁门,用完后解锁,下个人继续上锁,用完解锁。
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}
Lock接口比synchronized的对比:
用Lock实现上篇synchronized实现的卖票例子:
//资源类
class LTicket{
private Integer number = 30;
private final ReentrantLock lock = new ReentrantLock();
public void sale(){
//上锁
lock.lock();
try {
if( number > 0 ){
System.out.println(Thread.currentThread().getName() + ": 卖出票,剩余" + number--);
}
} finally {
//释放锁写finally语句中,防止上面发生异常导致锁未释放
lock.unlock();
}
}
}
创建共享资源类对象,开多个线程调用资源类的方法:
public class LSaleTicket {
public static void main(String[] args) {
LTicket ticket = new LTicket();
new Thread(() -> {
for(int i = 0 ; i < 40; i++ ){
ticket.sale();
}
},"AA").start();
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0 ; i < 40; i++ ){
ticket.sale();
}
}
},"BB").start();
new Thread(() -> {
for (int i = 0; i < 40; i++) {
ticket.sale();
}
}, "CC").start();
}
}