第五章 .Java多线程和并发编程

本文详细介绍了Java中的多线程和并发编程概念,包括多进程与多线程的区别,Java如何创建和启动线程,以及线程信息共享和管理。重点讨论了线程同步和死锁问题,并提供了相关示例代码。此外,还提到了守护线程的特性及其在资源管理中的注意事项。
摘要由CSDN通过智能技术生成

1. 多进程和多线程简介

1.1 多进程

:每个独立执行的任务就是一个进程
– 程序因IO堵塞时,可以释放CPU,让CPU为其他程序服务

并发:每个任务分配一个时间片,从外部看,所有任务是同时在执行。但是在CPU上,任务是按照串行依次运行(单核CPU)
并行:如果是多核,多个进程任务可以并行。但是单个核上,多进程只能串行执行。

1.2 多线程

:一个程序可以包括多个子任务(一个进程可以有多个线程)
– 线程由进程创建
– 如果一个子任务阻塞,程序可以将CPU调度另外一个子任务进行工作。这样CPU还是保留在本程序中,而不是被调度到别的程序(进程)去。这样,提高本程序所获得CPU时间和利用率。

1.3 对比

多进程 vs 多线程

  1. 线程共享数据
  2. 线程通讯更高效, 更轻量级,更容易切换,更容易管理

多进程执行:启动两个java.exe
多线程执行:只启动一个java.exe


2. Java多线程的实现

2.1Java 多线程创建

  1. java.lang.Thread
    – 线程继承Thread类,实现run方法
  2. java.lang.Runnable接口
    – 线程实现Runnable接口,实现run方法

2.2 Java多线程启动

– start方法,会自动以新线程调用run方法
– Runnable启动时需要Thread类的支持

  • 直接调用run方法,将变成串行执行
  • 同一个线程,多次start会报错,只执行第一次start方法
  • 多个线程启动,其启动的先后顺序是随机的
  • main函数(线程)可能早于新线程结束,整个程序并不终止,线程无需关闭,只要其run方法执行结束后,自动关闭,整个程序终止是等所有的线程都终止(包括main函数线程
public class Thread1 extends Thread{
	public void run()
	{
		System.out.println("hello");
	}
	public static void main(String[] a)
	{
		new Thread1().start();
	}
}

public class Thread2 implements Runnable{
	public void run()
	{
		System.out.println("hello");
	}
	public static void main(String[] a)
	{
		new Thread(new Thread2()).start();
	}
}

2.3 Java 多线程实现对比

Thread vs Runnable:

  1. Thread占据了父类的名额,不如Runnable方便
  2. Runnable启动时需要Thread类的支持
  3. Runnable 更容易实现多线程中资源共享

3. Java多线程信息共享

  1. 通过共享变量达到信息共享
    – static变量,同一个Runnable类的成员变量
    – 同一个类型线程之间信息的传递
  2. JDK原生库暂不支持发送消息 (类似MPI并行库直接发送消息)

public class ThreadDemo0
{
	public static void main(String [] args)
	{
		new TestThread0().start();
		new TestThread0().start();
		new TestThread0().start();
		new TestThread0().start();
	}
}
class TestThread0 extends Thread  
{
	//private int tickets=100;           //每个线程卖100张,没有共享
	private static int tickets=100;  //static变量是共享的,所有的线程卖100张
	public void run()
	{
		while(true)
		{
			if(tickets>0)
			{
				System.out.println(Thread.currentThread().getName() +
				" is selling ticket " + tickets);
				tickets = tickets - 1;
			}
			else
			{
				break;
			}
		}
	}
}
private int tickets=100;       卖了400张   
private static int tickets=100;  卖了103张(出现脏读,幻读)

在这里插入图片描述

public class ThreadDemo1
{
	public static void main(String [] args)
	{
		TestThread1 t=new TestThread1();//只实例化了一个TestThread1,共享private int tickets=100;
		new Thread(t).start();
		new Thread(t).start(); //创建新的线程执行run(),但tickets仍只实例了一个
		new Thread(t).start();
		new Thread(t).start();
	}
}
class TestThread1 implements Runnable
{
	private int tickets=100;
	public void run()
	{
		while(true)
		{
			if(tickets>0)
			{
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				tickets--;
				System.out.println(Thread.currentThread().getName() +" is selling ticket " + tickets);
			}
			else
			{
				break;
			}
				
		}
	}
}

3.1 多线程信息共享问题

  1. 工作缓存副本,操作不可见
  2. 关键步骤缺乏加锁限制
  3. 操作不具有原子性

在这里插入图片描述

3.2 多线程信息共享问题的解决方式

  1. 变量副本问题的解决方法
    – 采用volatile 关键字修饰变量,保证不同线程对共享变量操作时的可见性
  2. 关键步骤加锁限制
    –互斥的关键字是synchronized
    synchronized代码块/函数,只能一个线程进入
    synchronized加大性能负担,但是使用简便

volatile,synchronized:


public class ThreadDemo3 {
	public static void main(String[] args) {
		TestThread3 t = new TestThread3();
		new Thread(t, "Thread-0").start();
		new Thread(t, "Thread-1").start();
		new Thread(t, "Thread-2").start();
		new Thread(t, "Thread-3").start();
	}
}

class TestThread3 implements Runnable {
	private volatile int tickets = 100; // 多个 线程在共享的
	String str = new String("");

	public void run() {
		while (true) {
			sale();
			try {
				Thread.sleep(100);
			} catch (Exception e) {
				System.out.println(e.getMessage());
			}
			if (tickets <= 0) {
				break;
			}
		}

	}

	public synchronized void sale() { // 同步函数,一次只能有一个线程调用
		if (tickets > 0) {
			System.out.println(Thread.currentThread().getName() + " is saling ticket " + tickets--);
		}
	}
}

在这里插入图片描述


4. Java多线程管理

线程状态
– NEW 刚创建(new)
– RUNNABLE 就绪态(start)
– RUNNING 运行中(run)
– BLOCK 阻塞(sleep)
– TERMINATED 结束

在这里插入图片描述

4.1多线程管理方法:

• Thread的部分API已经废弃
– 暂停和恢复 suspend/resume
– 消亡 stop/destroy
• 线程阻塞/和唤醒
– sleep,时间一到,自己会醒来
– wait/notify/notifyAll,阻塞/唤醒,被动等待别人唤醒
– join,等待另外一个线程结束
– interrupt,向另外一个线程发送中断信号,该线程收到信号,会
触发InterruptedException(可解除阻塞),并进行下一步处理

生产消费者问题:

class Storage {
	// 仓库容量为10
	private Product[] products = new Product[10];
	private int top = 0;

	// 生产者往仓库中放入产品
	public synchronized void push(Product product) {
		while (top == products.length) {
			try {
				System.out.println("producer wait");
				wait();//仓库已满,等待
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
        //把产品放入仓库
		products[top++] = product;
		System.out.println(Thread.currentThread().getName() + " 生产了产品"
				+ product);
		System.out.println("producer notifyAll");
		notifyAll();//唤醒等待线程
		

	}

	// 消费者从仓库中取出产品
	public synchronized Product pop() {
		while (top == 0) {
			try {
				System.out.println("consumer wait");
				wait();//仓库空,等待
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}

		//从仓库中取产品
		--top;
		Product p = new Product(products[top].getId(), products[top].getName());
		products[top] = null;
		System.out.println(Thread.currentThread().getName() + " 消费了产品" + p);
		System.out.println("comsumer notifyAll");
		notifyAll();//唤醒等待线程
		return p;
	}
}

4.2 死锁

线程管理方式:

  1. 线程被动地暂停和终止
    – 依靠别的线程来拯救自己 (wait,notify…interrupt)
    – 没有及时释放资源
  2. 线程主动暂停和终止
    – 定期监测共享变量
    – 如果需要暂停或者终止,先释放资源,再主动动作
  • 最好线程主动暂停和终止
public class InterruptTest {

	public static void main(String[] args) throws InterruptedException {
		TestThread1 t1 = new TestThread1();
		TestThread2 t2 = new TestThread2();

		t1.start();
		t2.start();

		// 让线程运行一会儿后中断
		Thread.sleep(2000);
		t1.interrupt(); //被动
		t2.flag = false;//主动监控变量
		System.out.println("main thread is exiting");
	}

}

死锁
– 进程推进不合理,竞争资源导致
– 每个线程互相持有别人需要的锁(哲学家吃面问题)
– 预防死锁,对资源进行等级排序

synchronized(n):对对象加锁

package deadlock;

import java.util.concurrent.TimeUnit;

public class ThreadDemo5
{
	public static Integer r1 = 1; //资源数量
	public static Integer r2 = 1;
	public static void main(String args[]) throws InterruptedException
	{
		TestThread51 t1 = new TestThread51();
		t1.start();
		TestThread52 t2 = new TestThread52();
		t2.start();
	}
}

class TestThread51 extends Thread
{
	public void run() 
	{
		//先要r1,再要r2
		synchronized(ThreadDemo5.r1)
		{
			try {
				TimeUnit.SECONDS.sleep(3);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			synchronized(ThreadDemo5.r2)
			{
				System.out.println("TestThread51 is running");
			}
		}
	}
} 
class TestThread52 extends Thread
{
	public void run() 
	{
		//先要r2,再要r1
		synchronized(ThreadDemo5.r2)
		{
			try {
				TimeUnit.SECONDS.sleep(3);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			synchronized(ThreadDemo5.r1)
			{
				System.out.println("TestThread52 is running");
			}
		}
	}
} 

线程查看工具 :jvisualvm
在这里插入图片描述

4.3守护(后台)线程

– 普通线程的结束,是run方法运行结束
– 守护线程的结束,是run方法运行结束,或main函数结束

  • 守护线程永远不要访问资源,如文件或数据库等
    –来不及释放资源
		TestThread4 t = new TestThread4();
		t.setDaemon(true);//设置守护线程
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值