Java并发编程之线程基础与生产者与消费者问题案例

什么是线程、进程、多线程?

  • 线程: 是Java中执行代码的一条执行路径。
    线程是一组指令的集合,或者是程序的特殊段,它可以在程序里独立执行
    线程基本上是轻量级的进程,它负责在单个程序里执行多任务。通常由操作系统负责多个线程的调度和执行。
  • 进程:每个正在系统上运行的程序都是一个进程,是操作系统调度的执行单元。
  • 多线程:在一个进程中,有多条不同的执行路径,并行执行,目的为了提高程序的效率
    在一个进程中有一个主线程作为程序的入口

线程分类

用户线程:用户进程可以通过setDeamon设置为守护进程,执行业务代码。
守护线程:低级别线程,主要是为用户线程进行服务的。

线程的实现方式

https://blog.csdn.net/qq_34898847/article/details/84858048
主要就是集成Thread 类 , 实现Runnable借口,匿名内部类, 线程池等方式。

同步、异步概念

同步: 代码从上往下执行 (HTTP借口:请求-》响应)
异步: 不同线程之间的执行不会互相影响

线程的运行状态

新建: new Thread()
就绪: 调用start()方法,等待CPU调度分配执行权限
运行: 执行run()方法
阻塞: 当调用sleep 、 wait 、join 方法等的时候
死亡:run方法执行完毕,销毁
join方法: 释放CPU执行权, 若 A、B 线程, A.join(), 则A线程先执行
yield()方法: A.yield() : 则B先执行

线程安全

  • 当多个线程同时共享,同一个全局变量或静态变量,做写的操作时,可能会发生数据冲突问题,也就是线程安全问题。但是做读操作是不会发生数据冲突问题。
  • 将可能会发生数据冲突问题(线程不安全问题),只能让当前一个线程进行执行。代码执行完成后释放锁,让后才能让其他线程进行执行。这样的话就可以解决线程不安全问题。

内置的锁

Java提供了一种内置的锁机制来支持原子性
内置锁使用synchronized关键字实现,synchronized关键字有两种用法
(1)修饰需要进行同步的方法(在方法上修饰synchronized 称为同步方法)
(2)同步代码块

synchronized(同一个数据){
 可能会发生线程冲突问题
}

同步的前提:
1,必须要有两个或者两个以上的线程
2,必须是多个线程使用同一个锁
必须保证同步中只能有一个线程在运行
好处:解决了多线程的安全问题
弊端:多个线程需要判断锁,较为消耗资源、抢锁的资源。

他是可重入锁的:获得了锁, 可以进行锁传递
总结:
synchronized 修饰方法使用锁是当前this锁。
synchronized 修饰静态方法使用锁是当前类的字节码文件

多线程死锁

有A/B线程
A需要先获取obj锁,然后获取this锁
B需要先获取this锁,再获取obj锁
当A拥有obj锁,没有释放,等待获取this锁;B获取了this锁,没有释放,等待获取obj锁;出现死锁
解决方法:
注意枷锁的顺序;
设置加锁的时限(放弃请求锁和释放自己的锁)

线程安全解决办法?

使用多线程之间同步synchronized或使用锁(lock)

多线程应用场景?

答:主要能体现到多线程提高程序效率。
举例: 迅雷多线程下载、数据库连接池、分批发送短信等。

什么是多线程之间通讯?

多线程之间通讯,其实就是多个线程在操作同一个资源,但是操作的动作不同。

wait、notify方法

1.因为涉及到对象锁,他们必须都放在synchronized中来使用. Wait、Notify一定要在synchronized里面进行使用。
2.Wait必须暂定当前正在执行的线程,并释放资源锁,让其他线程可以有机会运行
3. notify/notifyall: 唤醒因锁池中的线程,使之运行

注意:一定要在线程同步中使用,并且是同一个锁的资源

wait与sleep区别

对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类中的。
sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。
在调用sleep()方法的过程中,线程不会释放对象锁。
而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备
获取对象锁进入运行状态。

Lock锁

在 jdk1.5 之后,并发包中新增了 Lock 接口(以及相关实现类)用来实现锁功能,Lock 接口提供了与 synchronized 关键字类似的同步功能,但需要在使用时手动获取锁和释放锁。

	Lock lock  = new ReentrantLock();
	lock.lock();
	try{
	//可能会出现线程安全的操作
	}finally{
	//一定在finally中释放锁
	//也不能把获取锁在try中进行,因为有可能在获取锁的时候抛出异常
	  lock.ublock();
	}

Lock与synchronized 关键字的区别

Lock 接口可以尝试非阻塞地获取锁 当前线程尝试获取锁。如果这一时刻锁没有被其他线程获取到,则成功获取并持有锁。
Lock 接口能被中断地获取锁 与 synchronized 不同,获取到锁的线程能够响应中断,当获取到的锁的线程被中断时,中断异常将会被抛出,同时锁会被释放。
Lock 接口在指定的截止时间之前获取锁,如果截止时间到了依旧无法获取锁,则返回

Condition用法

类似于在传统的线程技术中的,Object.wait()和Object.notify()的功能。

Condition condition = lock.newCondition();
res. condition.await();  类似wait
res. Condition. Signal() 类似notify

什么是Threadlocal

ThreadLocal提高一个线程的局部变量,访问某个线程拥有自己局部变量。
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
API:

•	void set(Object value)设置当前线程的线程局部变量的值。
•	public Object get()该方法返回当前线程所对应的线程局部变量。
•	public void remove()将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
•	protected Object initialValue()返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。

ThreadLoca实现原理:
ThreadLoca通过map集合
Map.put(“当前线程”,值);

多线程有三大特性

原子性、可见性、有序性

  • 什么是原子性?
    即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
  • 什么是可见性?
    当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
  • 什么是有序性?
    程序执行的顺序按照代码的先后顺序执行。
生产者、消费者案列
/**
 * 生产对象
 * @author xzb
 *
 */
class Demo {
	String name;
	String age;
	Boolean flag = false;
}

/**
  *  生产者
 * @author xzb
 *
 */
class Provider extends Thread{
	private Demo demo;
	public Provider(Demo demo) {
		this.demo = demo;
	}
	public void run() {
		int count = 0; // 0 1 0 1 0 1
		while (true) {
			synchronized (demo) {
				try {
					Thread.sleep(1000);
					if (demo.flag) {
						demo.wait();
					}
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				if (count == 0) {
					// 生产对象
					demo.name = "小兵";
					demo.age ="23";
				} else {
					demo.name = "小王";
					demo.age ="22";
				}
				count = (count+1) %2;
				demo.flag = true;
				demo.notify();
			}
		}
	} 
}

/**
 * 消费者
 * @author xzb
 *
 */
class Consumer extends Thread{
	private Demo demo;
	public Consumer(Demo demo) {
		this.demo = demo;
	}
	
	@Override
	public void run() {
		while(true) {
			synchronized (demo) {
				if (!demo.flag) {
					try {
						demo.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				System.out.println("消费者:" +demo.name  +","+demo.age);
				demo.flag = false;
				demo.notify();
			}
		}
	}
}


/**
 * wait()和notify() 方法必须持有相同的对象锁, 必须在synchronized同步中, (他们都在object对象中, 需要锁对象)
 * wait() 方法 阻塞当前线程, 并且释放当前所持有的锁
 * notify() 唤醒对象锁池的等待线程
 * join() 不会释放锁, 可不再同步代码块中
 * sleep() 该方法属于Thread,wait()方法属于Object,线程不会释放对象锁。
 * 
 * @author xzb
 *
 */
public class Test002  {
	public static void main(String[] args) {
		// 共享资源
		Demo demo = new Demo();
		Provider p = new Provider(demo);
		Consumer c = new Consumer(demo);
		p.start();
		c.start();
	}
}

基于队列方式实现生产者与消费者

/**
 * 队列的生产者
 * @author xzb
 *
 */
class ProviderQueue implements Runnable{
	private BlockingQueue<String> blockQueue;
	private volatile boolean FLAG = true;
	private AtomicInteger count = new AtomicInteger();
	
	public ProviderQueue(BlockingQueue<String> blockQueue) {
		this.blockQueue = blockQueue;
	}
	
	public void run() { 
		System.out.println(Thread.currentThread().getName() + "生产者开始生产。。。");
		while (FLAG) {
			String data = count.incrementAndGet() + "";
			try {
				// 存放队列
				boolean offer = blockQueue.offer(data, 2, TimeUnit.SECONDS);
				if (offer) {
					System.out.println(Thread.currentThread().getName()+",生产队列:" + data + "成功。。");
				} else {
					System.out.println(Thread.currentThread().getName()+",生产队列:" + data + "失败。。。");
				}
				Thread.sleep(1000);// 为了看效果
			} catch (Exception e) {
				// TODO: handle exception
			}
		}
		System.out.println(Thread.currentThread().getName() + ",生产者停止生产。。。");
	}
	
	public void stop () {
		this.FLAG = false;
	}
	
}

class ConsumerQueue implements Runnable {
	private BlockingQueue<String> blockQueue;
	private volatile boolean FLAG = true;
	
	public ConsumerQueue(BlockingQueue<String> blockQueue) {
		this.blockQueue = blockQueue;
	}
	public void run() {
		System.out.println(Thread.currentThread().getName() + "消费者启动消费。。。");
		while (FLAG) {
			try {
				String data = blockQueue.poll(2, TimeUnit.SECONDS);
				if (data == null) {
					this.FLAG = false;
					System.out.println("消费者超过2秒还未获取到队列消息。。。");
					return ;
				}
				System.out.println("消费者消费消息data: " + data);
			} catch (Exception e) {
				// TODO: handle exception
			}
		}
		
	}
}

public class TestDemo {
	
	public static void main(String[] args) {
		BlockingQueue<String> linkedBlockingQueue = new LinkedBlockingQueue<String>();
		ProviderQueue providerQueue = new ProviderQueue(linkedBlockingQueue);
		ConsumerQueue consumerQueue = new ConsumerQueue(linkedBlockingQueue);
		Thread p1 = new Thread(providerQueue);
		Thread c1 = new Thread(consumerQueue);
		p1.start();
		c1.start();
		// 为了模拟效果, 10秒后停止生产
		try {
			Thread.sleep(10000);
			providerQueue.stop();
		} catch (Exception e) {
			// TODO: handle exception
		}
		System.out.println("线程结束。。。");
		
	}

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值