Java 高级编程 多线程

Java 多线程编程
一、理解线程、进程
线程:一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

进程:一个进程包括由操作系统分配的内存空间,**包含一个或多个线程。**一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。

二、一个线程的生命周期
在这里插入图片描述
阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:

等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。

同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。

其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。

三、创建一个线程的常用方法
1、通过实现 Runnable 接口:

class RunnableDemo implements Runnable {
private Thread t;
private String threadName;

RunnableDemo( String name) {
threadName = name;
System.out.println("Creating " + threadName );
}

public void run() {
System.out.println("Running " + threadName );
try {
for(int i = 4; i > 0; i–) {
System.out.println("Thread: " + threadName + ", " + i);
// 让线程睡眠一会
Thread.sleep(50);
}
}catch (InterruptedException e) {
System.out.println(“Thread " + threadName + " interrupted.”);
}
System.out.println(“Thread " + threadName + " exiting.”);
}

public void start () {
System.out.println("Starting " + threadName );
if (t == null) {
t = new Thread (this, threadName);
t.start ();
}
}
}

public class TestThread {

public static void main(String args[]) {
RunnableDemo R1 = new RunnableDemo( “Thread-1”);
R1.start();

  RunnableDemo R2 = new RunnableDemo( "Thread-2");
  R2.start();

}
}

运行结果如下图
在这里插入图片描述
2、通过继承 Thread 类本身

四、 线程安全问题

产生原因:多个线程竞争同一资源(访问同一数据),可参考经典的生产者消费者问题。

解决方案:
run 方法内:同步代码块 synchronized {}

Public synchronized 返回值类型 方法名(){} 自动释放对象锁

使用 Lock 锁
Lock 锁需要程序员(在 finally 代码块中)手动释放。
Lock lock=new ReentranttLock() // Reentrant(可重用的)

Lock接口中的方法
void lock() // 获取锁
void unlock() // 释放锁

Lock 接口的实现类:
java.util.concurrent.locks.ReentrantLock implements Lock

使用步骤:
1.在成员位置创建一个 ReentrantLock 对象。
2.在可能出现线程安全问题的代码前,调用 Lock 接口中的方法 lock 获取锁对象。
3.在可能出现线程安全问题的代码后,调用 Lock 接口中的方法 unlock 释放锁对象。

相关案例代码如下:
public class RunnableImpl implements Runnable{
//定义一个共享的票源
private int ticket = 100;
//1.在成员位置创建一个ReentrantLock对象
Lock l = new ReentrantLock();
//设置线程任务:卖票
@Override
public void run() {
//使用死循环,让卖票重复的执行
while(true){
//2.在可能出现线程安全问题的代码前,调用Lock接口中的方法lock获取锁对象
l.lock();
//判断票是否大于0
if(ticket>0){
//为了提高线程安全问题出现的几率,让程序睡眠10毫秒
try {
//可能会产生异常的代码
Thread.sleep(10);
//进行卖票 ticket–
System.out.println(Thread.currentThread().getName()+“正在卖第”+ticket+“张票!”);
ticket–;
} catch (InterruptedException e) {
//异常的处理逻辑
e.printStackTrace();
}finally {
//一定会执行的代码,一般用于资源释放(资源回收)
//3.在可能出现线程安全问题的代码后,调用Lock接口中的方法unlock释放锁对象
l.unlock();//无论程序是否有异常,都让锁对象释放掉,节约内存,提高程序的效率
}
}
}
}
}

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页