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