线程
1.1 、线程的定义
- 线程:线程是负责执行程序的控制单元,即应用程序的一个执行路径。
- 多线程:一个应用程序可以启动多个线程,有多个执行路径。
1.2、线程和进程的区别
- 进程:进程即正在运行的程序。进程是一个独立的应用程序,通常结束一个进程不会影响其他进程。每个进程都有自己独立的内存空间。
- 线程:线程是由进程启动的一个应用的执行路径,多个线程可以共享一个内存空间。当一个线程结束后可能会影响其他线程。
1.3、线程的几个状态
1、创建状态:new一个对象之后的状态。
2、就绪状态:当我们调用了线程的start方法之后的状态。
3、运行状态:当线程正在执行run()方法时的状态。
4、中断状态:当线程停止执行的状态。其中有blocked和waiting状态差别是waiting状态不能自动唤醒。
5、死亡状态:线程死亡了(线程执行完run)。
2.1、创建线程的两个方法
1、实现Runnable接口
public class MyThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20 ; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
在主函数中创建实现Runnable的线程
public static void main(String[] args) {
MyThread my = new MyThread();
Thread thread = new Thread(my);
//thread.setName();
thread.start();
for (int i = 0; i < 20 ; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
2、继承Thread类
public class MyThread2 extends Thread{
public void run(){
for (int i = 0; i < 30; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
在主函数中创建继承Thread的线程(main函数也是一个线程)
public static void main(String[] args) {
// MyThread my = new MyThread();
// Thread thread = new Thread(my);
// //thread.setName();
// thread.start();
for (int i = 0; i < 20 ; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
MyThread2 my2 = new MyThread2();
my2.start();
}
2.2、线程常用方法
1、getId():返回线程的标识符即编号。
2、getName():返回线程的名字。有set方法
3、getPriority():返回线程的优先级。有set方法。优先级越高极有可能先运行,但不一定
4、isDaemon():测试这个线程是否是守护线程(精灵线程)。
5、sleep(long millis):进入睡眠,醒来进入就绪状态。
6、yield():出让CPU。
7、wait():导致当前线程等待,知道调用notify()或notifyAll()方法,或者指定时间已过。
8、currentThread():获取当前线程对象。
3线程同步
3.1、线程同步的概念
多个线程同时访问某个资源就称为线程同步。这时就有可能出现问题,先来举个例子。
public class MyThread3 implements Runnable{
int n = 0 ;
@Override
public void run() {
n++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+"是第"+n+"个进入的线程");
}
}
MyThread3 my3 = new MyThread3();
Thread t = new Thread(my3);
Thread t1 = new Thread(my3);
t.start();
t1.start();
这里有两个线程如果要同时访问这个my3的对象那么结果会如何?
可以看到出了问题我们理想状态下,一个应该是第1个进入和第2个进入,可是现在同时是第2个进入,出现了资源抢占。那么这时候就要用到synchronized关键字。
public class MyThread3 implements Runnable{
int n = 0 ;
@Override
public void run() {
synchronized ("abc") {
n++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + "是第" + n + "个进入的线程");
}
System.out.println("我是"+Thread.currentThread().getName());
}
}
增加了synchronized关键字后运行结果如下:
3.2、synchronized关键字也可以修饰方法
public synchronized void sing() {
System.out.println("唱歌");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
3.3、死锁
我们先来建立一个类
public class Person implements Runnable{
Person p1;
Person p2;
public Person() {
}
public Person(Person p1 ,Person p2){
this.p1 = p1;
this.p2 = p2;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
if ("t1".equals(name)){
synchronized (p1){
System.out.println(name+"进入run方法");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (p2){
System.out.println("锁定p2");
}
}
}else {
synchronized (p2){
System.out.println(name+"进入run方法");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (p1){
System.out.println("锁定p1");
}
}
}
}
}
在这个类里面有两个对象p1,p2而不同的线程会在执行run方法时锁住其中一个对象,同时又不释放自己锁定的对象,还去锁定另一个线程锁定的对象,这时候就会造成死锁。
public static void main(String[] args) {
Person p1 = new Person();
Person p2 = new Person();
Person p = new Person(p1,p2);
Thread t1 = new Thread(p);
Thread t2 = new Thread(p);
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
运行结果如下:
可以看到虚拟机并不停止,而是一直等待。为了解决这个问题就需要一个设计模式了
4、生产者消费者设计模式
我们想去吃馒头,那么这里有个馒头类
public class ManTou {
private int n ;
public ManTou(int n) {
this.n = n;
}
public int getN() {
return n;
}
}
假设有一个食堂,它提供生产馒头和消费馒头的服务
public class ShiTang {
ManTou[] ms = new ManTou[5];
int i = -1 ;//记录馒头位置
//生产馒头
public synchronized void put(ManTou m){
if (ms[ms.length-1] != null){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
i++;
ms[i] = m;
System.out.println(Thread.currentThread().getName()+"生产了:"+m.getN()+"号馒头");
notify();//通知消费者消费
}
//消费馒头
public synchronized void pop(){
if (ms[0] == null){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
ManTou m ;
m = ms[i];
ms[i] = null;//消费者消费馒头
System.out.println(Thread.currentThread().getName()+"消费了:"+m.getN()+"号馒头");
i--;
notify();//通知生产者生产馒头
}
}
那要生产馒头就得有生产者:
public class Producer implements Runnable{
private ShiTang shiTang;
public Producer(ShiTang shiTang){
this.shiTang = shiTang;
}
@Override
public void run() {
int n = -1;
while (true){
n++;
ManTou m = new ManTou(n);
shiTang.put(m);
try {
Thread.sleep(3000);//生产一个就要休息一下
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
馒头生产出来就需要消费者消费馒头
public class Consumer implements Runnable{
private ShiTang shiTang;
public Consumer(ShiTang shiTang){
this.shiTang = shiTang;
}
@Override
public void run() {
while (true){
shiTang.pop();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
建立测试类看一下运行结果
public class Test {
public static void main(String[] args) {
ShiTang shiTang = new ShiTang();
Producer p = new Producer(shiTang);
Consumer c= new Consumer(shiTang);
Thread t = new Thread(p);
Thread t1 = new Thread(c);
t.setName("生产者");
t1.setName("消费者");
t.start();
t1.start();
}
}
运行结果如下:
这就是生产者消费者设计模式,合理运用了资源。