Java线程学习(一):线程并发和线程忙等待

1.前序:

说到Java线程大家应该都听说过,但真正了解和熟悉线程,却并不容易。从这篇文章开始,我将以自学和实践的方式,和大家一起学习线程的内容。本篇主要讲java线程的并发和忙等待。


2.内容:

java线程最基本的两个内容在这里提一下,那就是线程的创建以及生命周期。

①java线程的创建:可以通过继承Thread类或实现Runnable接口。

②线程的生命周期:线程的创建(初始化)→调用start方法(等待cpu分配资源)→得到资源后运行run方法→阻塞(可能被sleep,wait进入阻塞状态,可通过interrupt或notify唤醒)→线程死亡(线程内程序运行完毕后)。

线程因为运行时,数据都是存在每个线程的各自的堆栈之中,而由于现在计算机的硬件结构原因,很多数据甚至是先存在线程各自CPU缓存和CPU寄存器中,所以在多个线程进行共享资源的写入问题上,就会出现所谓的线程安全问题。例如:

package com.jokerchen.test;

/**
 * 线程安全问题
 * @author Administrator
 *
 */
public class ThreadDemo {
	
	private int count = 0;
	
	public int add()
	{
		return count++;
	}
	
	class ActionThread extends Thread
	{
		public ActionThread(String name)
		{
			this.setName(name);
		}
		
		@Override
		public void run() {
			// TODO Auto-generated method stub
			int num = add();
			System.out.println("当前线程为:"+Thread.currentThread().getName()+",现在count等于:"+count);
		}
	}
	
	public static void main(String[] args) {
		ThreadDemo threadDemo = new ThreadDemo();
		ActionThread at_one = threadDemo.new ActionThread("AA");
		ActionThread at_two = threadDemo.new ActionThread("BB");
		at_one.start();
		at_two.start();
	}
	
}
(这里偷个懒,直接用的内部类来创建的线程类对象)结果为:


很奇怪,这里出现了两种结果对不对。而这里就是因为线程数据的存储问题,因其中一个线程的改写数据,没有及时更新到主存中,导致其他的线程也没有识别更新后的数据。
在说说并发和并行。个人理解:并发就是指多个线程在一时间段内通过cup资源的分配切换来运行;而并行则是指多个线程在同一时刻同时运行。就比如最经典的卖票实例:
package com.jokerchen.test;

public class ThreadTest {
	
	class Ticket implements Runnable
	{
		public int num = 100;
		@Override
		public void run() {
			// TODO Auto-generated method stub
			while(num > 0){
				try {
					Thread.currentThread().sleep(10);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				num--;
				System.out.println("当前线程为:"+Thread.currentThread().getName()+",目前所剩票数为:"+num);
			}
			
		}
		
	}
	
	public static void main(String[] args) {
		Ticket tt = new ThreadTest().new Ticket();
		new Thread(tt,"AA").start();
		new Thread(tt,"BB").start();
		
	}
	
}
结果如下:


大家也能看到,出现了同号票和负数票。这就是在线程切换时所产生的并发问题。这是因为在线程切换期间,因为票数做减法操作时,还没来及的判断和打印,另外的线程就已经运行了。那么此时我们需要加入同步块来解决这种情况:
class Ticket implements Runnable
	{
		public int num = 100;
		@Override
		public void run() {
			// TODO Auto-generated method stub
			while(true){
				try {
					Thread.currentThread().sleep(10);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				synchronized (this) {
					num--;
					if(num < 0) break;
					System.out.println("当前线程为:"+Thread.currentThread().getName()+",目前所剩票数为:"+num);
				}
				
			}
			
		}
		
	}
那么此时的重票和负票情况,就得以解决了,结果如下:


再来谈下线程的忙等待情形。忙等待即是一个线程在等待另外一个线程所给出的信号,而这个信号必然是在一个共享对象中存在的,这样才能达到多线程的信息共享。当等到信号之后才会进行下一步运行的情况。当然,这里也就涉及到了另外一种情况,如果一直等不到呢,那么就会进入我们常说的 死锁 状态。下面来看一下我所写的忙等待实例:
实体类(共享对象)代码
package com.jokerchen.test;

public class MyProcess {
	
	private boolean flag = false;
	
	public synchronized boolean getProcess()
	{
		return this.flag;
	}
	
	public synchronized void setProcess(boolean bool)
	{
		this.flag = bool;
	}
	
}
实现类
package com.jokerchen.test;

public class BusyWait {
	
	
	/**
	 * 忙等待
	 */
	public static void main(String[] args) {
		//实例共享对象
		final MyProcess mp = new MyProcess();
		Thread thread1 = new Thread("AA"){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				
				try {
					//睡眠五秒
					Thread.currentThread().sleep(5000);
					mp.setProcess(true);
					System.out.println("当前线程为:"+Thread.currentThread().getName()+",并已设置共享对象信号属性flag为true!");
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		};
		
		Thread thread2 = new Thread("BB")
		{
			@Override
			public void run() {
				// TODO Auto-generated method stub
				//等待其他线程的信号
				while (true) {
					//睡眠1秒
					try {
						Thread.currentThread().sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					if(mp.getProcess())
					{
						System.out.println("当前线程为:"+Thread.currentThread().getName()+",已经获得其他线程的信号!");
						break;
					}
					else
					{
						System.out.println("当前线程为:"+Thread.currentThread().getName()+",正在等待其他线程的信号......");	
					}
				}
			}
		};
		
		//开始忙等待....
		thread1.start();
		thread2.start();
	}
	
}
运行结果如下:


3.总结

多线程使用起来,有时候能简化我们的运行程序。当然,在多数情况下,还是比单体运行程序要复杂一些,所以需要注意很多问题。因博主也是刚开始深入学习和熟悉多线程,所以文章中可能有不足或者有太正确的观点,希望大家在借鉴之时也能提出宝贵的意见,让我们一起学习一起进步。。

发布了43 篇原创文章 · 获赞 39 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/yy339452689/article/details/78414285