进程与线程

  • 进程:是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
  • 线程:是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
  • 每个程序都是一个进程,一个进程里面可以包含多个线程。(比如你在听歌的时候是一个线程,下载音乐又是另一个线程,它们不会互相干扰),线程之间可以共享进程的资源。

进程.png

Java多线程

  • Java虚拟机允许应用程序同时执行多个执行线程。

1、继承Thread

Thread常用的方法

Modifier and Type Method Description
void start() 导致此线程开始执行; Java虚拟机调用此线程的run方法。
static void sleep(long millis) 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。 会占用线程的睡眠
void join() 等待这个线程死亡。
static void yield() 对调度程序的一个暗示,即当前线程愿意产生当前使用的处理器。
void run() 如果这个线程使用单独的Runnable运行对象构造,则调用该Runnable对象的run方法; 否则,此方法不执行任何操作并返回。
更多方法请查阅官方API文档

继承Thread需要重写它的run 方法,调用时用start方法才能启动线程。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class ThreadTest {

public static void main(String[] args) {
count count1=new count();
count count2=new count();
count1.start();
count2.start();
}
}


class count extends Thread
{
@Override
public void run() {
for(int i=0;i<100;i++)
{
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}

部分-返回结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Thread-0:64
Thread-0:65
Thread-0:66
Thread-0:67
Thread-0:68
Thread-0:69
Thread-0:70
Thread-0:71
Thread-1:3
Thread-1:4
Thread-0:72
Thread-1:5
Thread-0:73
Thread-1:6
Thread-0:74
Thread-1:7
Thread-0:75

线程0和线程1互相交叉运行互不阻塞。

查看线程状态

程序路径:%JAVA_HOME%/bin/jvisualvm.exe

jvisualvm1.png
jvisualvm2.png

2、实现Runnable接口

其实Thread也是实现了Runnable接口,所以只需要把run方法重写就行了 让Thread去调用。只不过Thread需要继承而Runnable是接口。(好处自己回顾一下面向对象知识)

Thread.png

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class RunnableTest {
public static void main(String[] args) {
count count1=new count();
count count2=new count();
new Thread(count1).start();
new Thread(count2).start();
}
}


class count implements Runnable
{
@Override
public void run() {
for(int i=0;i<100;i++)
{
try {
Thread.sleep(100);//延时100毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}

部分-返回结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Thread-0:26
Thread-1:27
Thread-0:27
Thread-1:28
Thread-0:28
Thread-1:29
Thread-0:29
Thread-1:30
Thread-0:30
Thread-1:31
Thread-0:31
Thread-1:32
Thread-0:32
Thread-1:33
Thread-0:33
Thread-1:34
Thread-0:34

线程0和线程1互相交叉运行互不阻塞。

多线程的锁

给我一把锁,我能创造出一种规则。

线程异步会造成不安全,比如下方图 value两次++ 值应该改变为12。

原因:线程1 读取时 value还是10,因为计算机调度快 线程2也马上可以读取value等于10 。
线程异步.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class countVlaue {

public static void main(String[] args) {
Test Test=new Test();

new Thread(Test).start(); //线程1
new Thread(Test).start();//线程2
}
}
class Test implements Runnable{
int vlaue=10;

@Override
public void run() {
try {
Thread.sleep(3000); //为了线程同时读到值
} catch (InterruptedException e) {
e.printStackTrace();
}
vlaue++;
System.out.println(vlaue);
}
}

输出结果:

1
2
11
11

Synchronized 的解析

在Hotspot虚拟机中,对象在内存中的布局分为三块区域:对象头、实例数据和对齐填充;Java对象头是实现synchronized的锁对象的基础,一般而言,synchronized使用的锁对象是存储在Java对象头里。

实现原理: JVM 是通过进入、退出 对象监视器(Monitor) 来实现对方法、同步块的同步的,而对象监视器的本质依赖于底层操作系统的 互斥锁(Mutex Lock) 实现。

Synchronized 的使用

  • Synchronized 用于线程同步,避免线程共享资源的异常。

  • Synchronized 可用于方法锁、代码块锁、类锁。

  • Synchronized 默认为Synchronized (this) 参数为对象,静态类锁 参数为Synchronized(类.class)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public class countVlaue {

    public static void main(String[] args) {
    Test Test=new Test();

    new Thread(Test).start(); //线程1
    new Thread(Test).start();//线程2
    }
    }
    class Test implements Runnable{
    int vlaue=10;

    @Override
    public synchronized void run() { //添加synchronized关键字
    try {
    Thread.sleep(3000); //为了线程同时读到值
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    vlaue++;
    System.out.println(vlaue);
    }
    }

    输入结果:

    1
    2
    11
    12

ReentrantLock的使用

主要的方法

Modifier and Type Method Description
void lock() 获得锁。
void unlock() 尝试释放此锁。
更多方法请查阅官方API文档
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import java.util.concurrent.locks.ReentrantLock;

public class countVlaue {

public static void main(String[] args) {
Test Test=new Test();

new Thread(Test).start(); //线程1
new Thread(Test).start();//线程2
}
}
class Test implements Runnable{
int vlaue=10;
ReentrantLock lock=new ReentrantLock();
@Override
public void run() {

try {
lock.lock(); //建议放在try里面
vlaue++;
System.out.println(vlaue);
Thread.sleep(3000); //为了线程同时读到值
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
lock.unlock(); //解锁放在finally
}

}
}

输入结果:

1
2
11
12

死锁:两个线程互相占用对方需要的锁,会造成卡顿现象。

线程池

好处:提高响应速度、降低资源消耗、便于管理。

相关API:ExecutorServiceExecutors

消费者与生产者模式

业务需求

背景:消费者到肯德基店购买鸡,但是肯德基不能现杀鸡需要准备货物才能运营给消费者消费,所以消费者就必须等待并告诉肯德基需要准备货物。

肯德基是供应商,如果只生产鸡没有去宣传告诉消费者那永远运营不起来。

(鸡不可能 同时生产 同时消费)

业务图1.png

方法

Modifier and Type Method Description
void wait() 释放锁并阻塞但并不会持有锁
void notify() 唤醒某一条线程 未必唤醒成功
void notifyAll() 唤醒全部线程

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
public class PC{
public static void main(String[] args) {
Cache Cache=new Cache(); //同一个缓存区
new Producer(Cache).start();
new Consumer(Cache).start();
}
}

//生产者
class Producer extends Thread {

Cache cache;
public Producer(Cache cache) {
this.cache = cache;
}

@Override
public void run() {

for (int i = 0; i < 15; i++) {
cache.push(i);
System.out.println("生产第" + i + "只");
}
}
}


//消费者
class Consumer extends Thread {

Cache cache;

public Consumer(Cache cache) {

this.cache = cache;
}
@Override
public void run() {
for (int i = 0; i < 15; i++) {
System.out.println("消费第" + cache.pop().id + "只");
}
}
}

//共同消费的产品:鸡
class chicken {
int id;

public chicken(int id) {
this.id = id;
}
}

//缓冲区
class Cache {
private chicken[] chickens = new chicken[10];
public static int count = 0;

//生产
public synchronized void push(int id) {
if (count == chickens.length) { //生产仓库满了需要等待消费者消费
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//否则就生产鸡 并同时告诉消费者
chicken chicken = new chicken(id);
chickens[count++] = chicken;
this.notifyAll();
}

//消费
public synchronized chicken pop() {
chicken chicken = null;
if (count == 0) { //仓库没有鸡 消费者就必须等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

//消费者消费了 需要告诉生产者继续生产
count--;
chicken = chickens[count];
this.notifyAll();
return chicken;
}
}

输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
生产第0
生产第1
生产第2
生产第3
生产第4
生产第5
生产第6
生产第7
生产第8
生产第9
生产第10
消费第9
消费第10
消费第8
消费第11
消费第7
消费第6
消费第5
消费第4
消费第3
消费第2
消费第1
消费第0
生产第11
生产第12
生产第13
生产第14
消费第14
消费第13
消费第12