Java基础 -- 线程
来源:互联网 发布:青岛java招聘 编辑:程序博客网 时间:2024/05/20 07:36
前言
线程是Java基础中的重要一章,必须要熟练掌握基本概念和使用方法。
目录
1. 基本概念
1.1 线程
操作系统中对线程的定义是程序执行的最小单元;实际上我们可以理解为一个进程里面的不同的执行路径。
1.2 进程
进程是一个静态的概念,可以理解为一个可执行的java文件。
2. 创建和启动
线程的创建和启动有两种方式:
(1)继承Thread类
public class ThreadStart { public static void main(String[] args) { Thread thread = new Thread1(); thread.start(); }}class Thread1 extends Thread { @Override public void run() { }}
(2)实现Runnable接口
public class TestThread1 { public static void main(String[] args) { Thread thread1 = new Thread(new Runnable1()); thread1.start(); for (int i = 0; i < 100; i ++ ) { System.out.println("main: " + i); } }}class Runnable1 implements Runnable { @Override public void run() { for (int i = 0; i < 100; i ++ ) { System.out.println("thread: " + i); } }}
(3)两种方法的对比
实现Runnable的接口要比继承Thread类要好,因为继承只能有一个父类,比较单一且不好控制。
3. 调度和优先级
线程的优先级基础的分为1-10级,默认值为5,优先级越高,获得的CPU的执行时间片越长:
Thread thread1 = new Thread(new Runnable1());thread1.setPriority(Thread.MAX_PRIORITY);thread1.start();
4. 状态控制
4.1 线程状态
4.2 执行方法
(1)sleep()
该方法是Thread类的静态方法,使线程处于睡眠等待的状态,让出cpu的执行权。
(2)interrupt()
需要捕获InterruptException的异常,当调用线程的该方法时,停止线程的执行,但是会先执行完InterruptException的异常处理之后,再结束线程。
(3)stop()
停止线程,该方法已经废弃,一旦调用会立即结束线程。
(4)join()
合并两个线程的执行。当在线程A中执行调用线程B的join方法时,线程B合并到A中执行,B结束后再接着执行线程A。
(5)yield()
当调用线程的该方法时,该线程立刻释放CPU的占用,重新进入就绪等待的状态。
(6)isAlive()
判断当前线程是否还存活:
Thread.currentThread().isAlive();
4.3 结束线程的方法
对比:
a. stop方法比interrupt粗暴,直接结束线程;
b. 结束一个线程的最好方法是,结束run()方法的执行。run()方法结束,那么线程也就结束了。
5. 线程同步
线程同步可以直接理解为资源独占,也就是同一时期的某个资源只能被一个线程独占。
5.1 互斥锁
资源独占可以用synchronized关键字进行修饰。
synchronized:
a. 修饰方法:当synchronized修饰一个方法时,该线程拥有该对象的锁(Object本身就是一个对象锁),则其他线程会阻塞 无法访问该方法,直到独占的线程释放该对象锁。
这种情况下,其他线程可以访问该对象的其他方法。
public synchronized void printDataA() { try { System.out.println("Current: " + new Date() + " === " + Thread.currentThread().getName()); Thread.sleep(5000); flag++; System.out.println("Current: " + new Date() + " === " + Thread.currentThread().getName()); } catch (InterruptedException e) { } System.out.println("Thread : " + Thread.currentThread().getName() + " , flag = " + flag); }
b. 修饰代码块:使用关键字时需要指明具体的锁
public void printDataB() { flag++; System.out.println("Current: " + new Date() + " === " + Thread.currentThread().getName()); synchronized (this) { try { flag++; System.out.println("Thread : " + Thread.currentThread().getName() + " , flag = " + flag); //System.out.println("Current: " + new Date() + " === " + Thread.currentThread().getName()); Thread.sleep(1000); System.out.println("Current: " + new Date() + " === " + Thread.currentThread().getName()); } catch (InterruptedException e) { } } }
5.2 死锁
死锁是指两个线程或多个线程之间相互等待需要的资源对象的现象,由于线程之间的相互等待,导致线程永远处于阻塞状态无法继续执行。经典的问题如哲学家吃饭。
针对死锁的问题,可以通过添加同步锁的方式(资源独占)进行解决。但是如果要保护好被访问的对象,需要仔细考虑所有操作该对象的方法是否被加了同步锁。
5.3 生产者/消费者 问题
生产者/消费者模型,是典型的容易发生死锁现象的问题。
5.3.1 wait和notify
该模型的具体代码见下面,这里使用到了线程控制的另一种方法wait(),该方法是Object对象的方法,与其他线程控制的方法不同。
wait():当调用某个对象的wait()方法时,说明锁定在该对象上的线程需要等待,让出cpu执行权。
当调用wait方法时,该线程处于睡眠状态,那么如何唤醒该线程呢?
notify()/notifyAll():叫醒wait在当前对象上的线程。如果某个线程处于wait状态时,Object不发通知也会产生死锁的现象。
5.3.2 wait和sleep的区别
wait属于Object的方法,sleep是Thread类的方法;
wait时会释放线程所占用的锁,sleep不会释放线程占用的锁;
5.3.3 生产者/消费者模型:
public class ProducerCunsumer { public static void main(String[] args) { Basket basket = new Basket(); new Thread(new Producer(basket)).start(); new Thread(new Consumer(basket)).start(); }}class Wotou { public Wotou(){}}class Basket{ int index = 0; int length = 5; Wotou[] array = new Wotou[length]; public synchronized void push(Wotou m) { while (index == length) { try{ wait(); } catch (InterruptedException e) { e.printStackTrace(); } } notify(); System.out.println("set wotou: " + index); array[index] = m; index++; } public Wotou pop() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized(this) { while (index == 0) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } notify(); index--; Wotou result = array[index]; System.out.println("get wotou: " + index); return result; } }}class Producer implements Runnable { Basket basket; Producer(Basket basket) { this.basket = basket; } @Override public void run() { for (int i = 0; i < 20; i++) { basket.push(new Wotou()); } }}class Consumer implements Runnable { Basket basket; Consumer(Basket basket) { this.basket = basket; } @Override public void run() { for (int i = 0; i < 20; i++) { basket.pop(); } }}
6. 总结
线程的基本概念和使用方法基本上已经介绍清楚,相关的测试代码会上传到github上。
- 线程之一:JAVA线程基础
- 线程之一:JAVA线程基础
- java线程学习基础
- Java基础 -- 线程
- Java基础 -- 线程2
- JAVA线程基础
- Java 线程基础
- Java线程基础1
- java线程基础
- java基础 线程
- Java 线程基础
- java 线程基础
- 十三、Java线程基础
- java线程基础
- java基础之线程
- JAVA线程基础(一)
- Java基础之线程
- java基础--线程总结
- 程序设计中的堆和栈
- Android移动开发-使用摄像头和麦克风录制视频的实现
- Zookeeper的功能以及工作原理
- apache服务器开启Gzip压缩服务
- Scratch2.0编程--第十一节 运算符
- Java基础 -- 线程
- 2017/9/30 学习笔记
- python基础(一)--join和os.path.join函数的用法以及字符串格式化
- 51nod1013 3的幂的和 (矩阵快速幂 或 逆元+快速幂)
- AHB总线协议
- Java运算符
- c++的数组与指针
- 第三章 Java的基本程序设计结构 (2)
- Angular2学习笔记——快速上手(1)