目录
java(十二)线程
1. 什么是线程、什么是进程、什么是程序
概念:
- 程序是一组指令的集合,一般的操作系统都支持多个任务同时运行,而一个任务就是一个 程序。
- 进程是正在执行的程序,是系统执行的基本单元。
- 线程是一中轻量型的进程,是进程中的执行流,也是进程中的执行单元。
程序与进程、进程与线程的区别
程序与进程
- 程序是静态的,而进程与之相反是动态的。
- 程序与进程是一对多的关系,一个进程对应这一程序,但是一个程序可以有多个线程。
进程与线程
- 进程在内存中是拥有独立的内存空间的,而线程本身是没有任何系统资源的,他只拥有一些在运行过程中比不可少的一些资源,但那是它和同属同一进程下的所有线程共享这个进程的全部资源。
2. 生命周期
3. 实现线程方法
多线程的优点:
- 多线程最大的好处在于可以同时并发执行多个任务,当程序的某个功能部分正在等待某些资源的时候,此时又不愿意因为等待而造成程序暂停,那么就可以创建另外的线程进行其它的工作
- 多线程可以最大限度地减低CPU的闲置时间,从而提高CPU的利用率
实现方法
1.继承Thread类
public class MyThread extends Thread{
//重写run方法
public void run(){
//要做的事情
}
}
2.实现Runnable接口
public class MyThread implements Runnable{
//实现run方法
public void run(){
//要做的事情
}
}
线程的状态
- 新建状态(new):使用new关键字创建线程对象,仅仅只是分配了内存
- 就绪状态(ready):线程对象被创建之后,等待start()方法被调用,获得cpu的使用权
- 运行状态(running):执行run方法,此时的线程对象正在占用cpu
- 睡眠状态(sleeping):调用sleep()方法,线程被暂停,睡眠时间结束后,线程回到就绪状态,睡眠状态的线程不占CPU
- 死亡状态(dead):run方法执行完毕后,线程进入死亡状态
- 阻塞状态(blocked):线程由于某些事件(等待输入)放弃cpu,暂时停止运行,直到线程重新进入就绪状态,就有机会转到运行转到运行状态。
4.synchronized同步锁
同步方法
通过在方法声明中加入 synchronized关键字来声明 synchronized 方法。
synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率
public synchronized void test1(){
}
同步代码块
synchronized 块:通过 synchronized关键字来声明synchronized 块。语法如下:
synchronized(syncObject) {
//允许访问控制的代码
}
synchronized 块是这样一个代码块,其中的代码必须获得对象 syncObject (如前所述,可以是类实例或类)的锁方能执行,具体机制同前所述。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。
对synchronized(this)的一些理解:
- 当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
- 然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
- 一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分(包括同步代码块以及同步方法)的访问都被暂时阻塞。
5.生产者和消费者
- 生产者消费者模型是并发中的经典问题,具体场景是有一块缓冲区作为仓库,生产者可以向其添加产品,消费者可以从中取出产品。
- 解决生产者消费者问题可以采用两种方式:wait()/notify方式和BlockingQueue方式,在此主要讨论第一种,关于第二种方法可以参考Ranger的Audit模型。
wait()/notify()是Object的两个方法,也是并发中常见的两个方法:wait会使线程阻塞,直到一个notify通知其继续执行。下面通过一段代码来说明这两个方法。
public class MyThread implements Runnable {
//成员变量
private String name;
//被当做仓库的集合
private List<String> list = new ArrayList<String>();
//仓库上限
private final int size = 10;
//生产者
public void produce(int num) throws Exception {
while (true) {
//同步代码块
synchronized (list) {
//当仓库中的数据数目加上此次需要保存的数据数目大于仓库上限时进入循环
while (list.size() + num > size) {
System.out.println(Thread.currentThread().getName()+"生产过剩,等待消费");
//阻塞线程
list.wait();
}
System.out.println(Thread.currentThread().getName()+"正在生产");
//往线程中添加num个产品
for (int i = 0; i < num; i++) {
list.add("hello, world");
}
//启动其他阻塞的线程执行
list.notify();
}
Thread.sleep(1000);
}
}
public void consume() throws Exception {
while (true) {
//同步代码块
synchronized (list) {
//当仓库中没有产品时,进入循环
while (list.size() == 0) {
System.out.println(Thread.currentThread().getName()+"已无产品可消费,等待生产");
//阻塞线程
list.wait();
}
System.out.println(Thread.currentThread().getName()+"正在消费");
//移除集合首个产品
list.remove(0);
//启动其他阻塞的线程执行
list.notify();
}
Thread.sleep(1000);
}
}
//设置成员变量
public void setName(String name) {
this.name = name;
}
public void run() {
try {
//调用生产者或者消费者
if ("producer".equals(name)) {
produce(1);
}else {
consume();
}
}catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
MyThread myThread = new MyThread();
myThread.setName("producer");
Thread t1 = new Thread(myThread);
t1.start();
Thread.sleep(1);
myThread.setName("consumer");
Thread t2 = new Thread(myThread);
t2.start();
}
catch (Exception e) {
}
}
}
学习笔记分享