Java基础之多线程
前言
本文是对java基础复习时候的文章,拿来做总结。
一、概述
1、进程和线程
大家都是自诩为程序猿,那什么是程序呢?程序就是指为了完成特定任务,用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。了解了程序,接下来就是进程和线程。
进程:是程序的一次执行过程,或者是正在运行的一个程序,是一个动态的过程,。
线程:进程进一步的细分就是线程,是程序内的一条执行路径。
2、并行和并发
并行:多个CPU同时执行多个任务,比如:多个人同时做不同的事
并发:一个CPU同时执行多个任务。比如:多个人同时做一件事
二、线程的创建和使用
1、线程的创建
线程的创建有多种方式,本文主要介绍两种方式:继承Thread类和实现Runnable接口。
-
继承Thread类
- 实现方法:
1、创建一个类继承Thread类
2、实现Thread类的run()方法
3、创建子类的对象
4、通过子类对象调用start()方法启动线程
- 代码:
public class ImplThread extends Thread { @Override public void run() { for (int i = 0; i < 200; i++) { System.out.println("子线程开始发车,准备上车。。。。"+i); } } public static void main(String[] args) { ImplThread implThread = new ImplThread(); implThread.start(); for (int i = 0; i < 200; i++) { System.out.println("主线程开始上车,准备发车。。。。"+i); } } }
-
实现Runnable接口
- 实现方法:
1、创建一个类实现Runnable接口
2、实现run()方法
3、创建实现类对象
4、将此对象传入到Thread类的构造方法中,创建线程对象
5、通过此对象调用start()方法启动线程
- 代码:
public class ImplRunnable implements Runnable{ @Override public void run() { for (int i = 0; i < 200; i++) { System.out.println(Thread.currentThread().getName()+"线程正在执行。。。"+i); } } public static void main(String[] args) { ImplRunnable im = new ImplRunnable(); new Thread(im).start(); new Thread(im).start(); new Thread(im).start(); } }
2、线程的使用
- 常用方法:
●void start():启动线程,并执行对象的run()方法
●run():线程在被调度时执行的操作
●String getName():返回线程的名称
●void setName(String name):设置该线程名称
●static Thread currentThread():返回当前线程。在Thread 子类中就是this,通常用于主线程和Runnable实现类
●yield():释放当前CPU的执行权
●join():在线程a中调用线程b的join方法,此时线程a进入阻塞状态,直到线程b完全执行完之后,线程a才结束阻塞状态。
●stop():强制结束线程(已经过时)
●sleep(long milltime):让当前线程睡眠指定的milltime毫秒时间内,当前线程阻塞
●isAlive():判断当前线程是否还存活
●线程的优先级
●MAX PRIORITY: 10 MIN_ PRIORITY: 1 NORM PRIORITY: 5
●设置优先级:setpriority()
●获取优先级:getpriority()
三、线程的生命周期
在Thread.State
中定义了线程的五种状态:NEW
、RUNNABLE
、BLOCKED
、WAITING
、TERMINATED
,即一个线程完整的生命周期通常主要经历一下五种状态:
- 新建:当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态
- 就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是没分配到CPU资源
- 运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run()方法定义了线程的操作和功能
- 阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时中止自己的执行,进入阻塞状态
- 死亡:线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束
四、线程的同步与通信
1、线程的同步
当多个线程同时操作同个方法的时候,容易揣想那线程安全问题,比如在买票的过程中,如果多个线程同时访问当前的一张票,这样就会导致票的数量出现问题,导致线程安全问题出现,所以需要引入线程同步来解决线程安全问题,线程的同步主要有以下三种方法:
- 通过同步代码块
Synchronized(同步监视器){
需要被同步的代码
}
- 通过同步方法
public synchronized void ceshi(){
需要被同步的代码
}
- 通过Lock锁
private ReentrantLock lock = new ReentrantLock();
public void ceshi(){
try{
lock.lock();
需要被同步的代码
}finally{
lock.unlock();
}
}
2、线程的通信
线程的通信主要涉及三个方法:wait()
、notify()
、notifyAll()
;此三个方法的作用为:
-
wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
-
notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。
-
notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。
五、多线程的例子:生产者消费者问题
生产者生产,消费者消费,期间容易产生线程安全问题,话不多说,直接上代码。
- synchronized实现
public class TestProductorAndConsumer {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Productor pro = new Productor(clerk);
Consumer cus = new Consumer(clerk);
new Thread(pro, "生产者 A").start();
new Thread(cus, "消费者 B").start();
}
}
//店员
class Clerk{
private int product = 0;
//进货
public synchronized void get(){
while(product >= 10){//为了避免虚假唤醒问题,应该总是使用在循环中
System.out.println("产品已满!");
try {
this.wait();
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName() + " : " + ++product);
this.notifyAll();
}
//卖货
public synchronized void sale(){
while(product <= 0){
System.out.println("缺货!");
try {
this.wait();
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName() + " : " + --product);
this.notifyAll();
}
}
//生产者
class Productor implements Runnable{
private Clerk clerk;
public Productor(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
clerk.get();
}
}
}
//消费者
class Consumer implements Runnable{
private Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
clerk.sale();
}
}
}
- lock实现
public class ConsumerProductorTest {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Productor pro = new Productor(clerk);
Customer cus = new Customer(clerk);
new Thread(pro, "生產者").start();
new Thread(cus, "消費者").start();
}
}
class Clerk {
private int product = 0;
private Lock lock = new ReentrantLock();
Condition productorlock = lock.newCondition();
Condition customerlock = lock.newCondition();
// 生产:
public void getProduct() {
lock.lock();
try {
while (product >= 10) {
System.out.println("产品已满");
try {
productorlock.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ":" + ++product);
customerlock.signalAll();
} finally {
lock.unlock();
}
}
// 消费
public void setProduct() {
lock.lock();
try {
while (product <= 0) {
System.out.println("缺货");
try {
customerlock.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ":" + product--);
productorlock.signalAll();
} finally {
lock.unlock();
}
}
}
class Productor implements Runnable {
private Clerk clerk;
public Productor(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
clerk.getProduct();
}
}
}
class Customer implements Runnable {
private Clerk clerk;
public Customer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
clerk.setProduct();
}
}
}