Java并发编程:Thread类的使用

Java并发编程,是Java的高级开发部分,平时项目很少用到,主要原因还是不熟悉,从今天开始整体学习研究下,后面会有一个系列的学习,也为以后在项目中经常使用打下基础。首先来回顾下Java最基本的多线程开发,就是java.lang.Thread类。
以下是本文包含的知识点:
一、线程的基本概念
二、线程的创建和启动
三、线程的状态控制
四、线程的同步
五、生产者消费
下面开始本文的内容:
 
一、线程的基本概念
线程(英语:thread)是操作系统能夠進行運算调度的最小單位。 它被包含在进程之中,是进程中的實際運作單位。 一条 线程指的是进程中一个单一顺序的控制流,一個进程中可以並行多個 线程,每条 线程并行执行不同的任务。(来自维基百科)
简单来说:线程是一个程序内部的顺序控制流。
线程和进程的区别:
1.每个进程有独立的代码和数据空间,进程间的切换会有较大开销。
2.在一个操作系统中可以运行多个进程。
3.一个进程中可以并行运行多个线程。
4.一个进程中的多个线程共享这个进程中的资源,如代码,数据空间。
 还可以参考阮一峰的:http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html
 
二、Java线程的创建和启动
当 Java 虚拟机启动时,通常都会有单个非守护线程(它通常会调用某个指定类的  main 方法)。

在Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程) 

用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守护线程的保姆。
只要有一个非守护线程还在运行,守护线程就不能结束,当所有非守护线程都运行结束,守护线程也随着JVM一同结束。最典型的守护线程是GC(垃圾回收器)。
JVM启动时会有一个主方法(public static void main(String []args))所定义的线程。也叫主线程
Java通过创建Thread实例来创建新的线程,
每个线程都是通过某个特定的Thread对象的run()方法来完成它的操作,run()方法称为线程体。
通过调用Thread类的start()方法来启动一个线程。
通过JDK6的API文档 http://tool.oschina.net/apidocs/apidoc?api=jdk-zh,我们看到有两种创建线程的方式:

 

一种方法是将类声明为  Thread 的子类, 该子类应重写  Thread 类的  run 方法:
class PrimeThread extends Thread {
         long minPrime;
         PrimeThread(long minPrime) {
             this.minPrime = minPrime;
         }
 
         public void run() {
             // compute primes larger than minPrime
              . . .
         }
}

 然后,下列代码会创建并启动一个线程:

PrimeThread p = new PrimeThread(143);
p.start();

 另一种方法是声明实现 Runnable 接口的类。该类然后实现 run 方法:

class PrimeRun implements Runnable {
         long minPrime;
         PrimeRun(long minPrime) {
             this.minPrime = minPrime;
         }
 
         public void run() {
             // compute primes larger than minPrime
              . . .
         }
}

 然后,下列代码会创建并启动一个线程:

PrimeRun p = new PrimeRun(143);
new Thread(p).start();

 每个线程都有一个标识名,多个线程可以同名。如果线程创建时没有指定标识名,就会为其生成一个新名称。

 

三、线程的状态控制


 
线程控制的基本方法有:

isAlive():判断线程是否还“活”着,即线程是否还未终止
Thread.sleep():将当前线程睡眠指定的毫秒数
join():调用某线程的该方法,将当前线程与该线程合并,即等待该线程结束,再恢复当前线程的运行。
yield():让出CPU,当前线程进入就绪队列等待调度。
wait():当前线程进入对象的wait pool。
notify()/notifyAll():唤醒对象的wait pool中的一个/所有等待线程。
Thread.currentThrad():获得当前线程的引用。
线程的优先级:
Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程。
线程调度器按线程的优先级来决定应调度哪个线程来执行。
通过阅读源码,看到线程优先级用数字表示,从1到10,默认为5。
通过下列方法来获取或设置某个线程的优先级:
getPriority():获得线程的优先级数
setPriority():设置线程的优先级数
 
四、线程同步:
先来看下面一段代码:
public class TestSync implements Runnable {
     Timer timer = new Timer();

     public static void main(String[] args) {
          TestSync test = new TestSync();
          Thread t1 = new Thread(test);
          Thread t2 = new Thread(test);
          t1.setName( "t1");
          t2.setName( "t2");
          t1.start();
          t2.start();
     }

     public void run() {
           timer.add(Thread. currentThread().getName());
     }
}

class Timer {
     private static int num = 0;

     public void add(String name) {
           num++;
          System. out.println(name + ", 你是第" + num + "个使用timer的线程" );
     }
}
 我们期望的结果是:
t1, 你是第1个使用timer的线程
t2, 你是第2个使用timer的线程
然而,实际执行的结果却是:
t1, 你是第2个使用timer的线程
t2, 你是第2个使用timer的线程
为什么会出现意想不到的情况呢,我们按实际结果的情况来分析下:
首先,t1先执行了,将num++变成了1。
此时,t2开始执行(注意这里t1并未执行完),将num++变成了2 。
然后,t1接着执行,打印System.out输出,结果为 t1, 你是第2个使用timer的线程。
最后,t2接着执行,打印System.out输出,结果为 t2, 你是第2个使用timer的线程。
按上面的分析,我们知道出现这种情况的原因是t1,t2在执行线程体访问同一个对象的过程中被打断了。
那么如何不被打断呢,这就要用到线程同步。
在Java中引入对象互斥锁的概念,保证共享数据操作的完整性。
每个对象都对应于一个称为“互斥锁”的标记,这个标记保证在任一时刻,只能有一个线程访问该对象。
关键字synchronized来表示对象的互斥锁,当某个对象被synchronized修饰时,表示该对象在任一时刻只能由一个线程访问。
synchronized的使用方法,如对上例的修改:
1.作用于代码块上:
synchronize(this){
     num++;
     System. out.println(name + ", 你是第" + num + "个使用timer的线程" );
}
 2.放在方法声明中,表示整个方法为同步方法:
public synchronized  void  add(String name) {
    num++;
    System. out.println(name + ", 你是第" + num + "个使用timer的线程" );
}
 
五、生产者消费者
/**
 * 生产者消费者
 * 举例:生产窝头,消费窝头
 * @author Yuwl
 */
public class TestProducerConsumer {

     public static void main(String[] args) {
          SyncStack ss = new SyncStack();
          Producer p = new Producer(ss);
          Consumer c = new Consumer(ss);
           new Thread(p).start();
           new Thread(c).start();
     }

}

/**
 * 窝头
 */
class WoTou {
     int id;
     
     WoTou(int id){
           this. id = id;
     }

     public String toString() {
           return "WoTou id=" + id ;
     }
     
}

/**
 * 篮子-栈的实现[先进后出]
 */
class SyncStack {
     int index = 0;
     WoTou []arrWt = new WoTou[6];
     
     public synchronized void push(WoTou wt){
           while( index == arrWt. length){ //如果篮子满了
               try {
                    this.wait(); //让当前线程进入当前对象的wait pool
              } catch (InterruptedException e) {
                   e.printStackTrace();
              }
          }
           this.notifyAll(); //唤醒其它正在等待的线程
           arrWt[ index] = wt;
           index++;
     }
     
     public synchronized WoTou take(){
           while( index == 0){//如果篮子空了
               try {
                    this.wait();
              } catch (InterruptedException e) {
                   e.printStackTrace();
              }
          }
           this.notifyAll();
           index--;
           return arrWt[ index];
     }
     
}

/**
 * 生产者
 */
class Producer implements Runnable {
     SyncStack ss;
     
     Producer(SyncStack ss){
           this. ss = ss;
     }
     
     public void run(){
           for( int i=0; i<20; i++){
              WoTou wt = new WoTou(i);
               ss.push(wt);
              System. out.println( "生产了 "+wt);
               try {
                   Thread. sleep((long)(Math.random()*200));
              } catch (InterruptedException e) {
                   e.printStackTrace();
              }
          }
     }
}

/**
 * 消费者
 */
class Consumer implements Runnable {
     SyncStack ss;
     
     public Consumer(SyncStack ss) {
           this. ss = ss;
     }
     
     public void run(){
           for( int i=0; i<20; i++){
              WoTou wt = ss.take();
              System. out.println( "消费了 "+wt);
               try {
                   Thread. sleep((long)(Math.random()*1000));
              } catch (InterruptedException e) {
                   e.printStackTrace();
              }
          }
     }
}
 其中一次的运行结果:
生产了 WoTou id=0
消费了 WoTou id=0
生产了 WoTou id=1
生产了 WoTou id=2
消费了 WoTou id=2
生产了 WoTou id=3
生产了 WoTou id=4
生产了 WoTou id=5
生产了 WoTou id=6
生产了 WoTou id=7
消费了 WoTou id=7
生产了 WoTou id=8
消费了 WoTou id=8
生产了 WoTou id=9
生产了 WoTou id=10
消费了 WoTou id=9
消费了 WoTou id=10
生产了 WoTou id=11
生产了 WoTou id=12
消费了 WoTou id=11
生产了 WoTou id=13
消费了 WoTou id=12
消费了 WoTou id=13
生产了 WoTou id=14
消费了 WoTou id=14
生产了 WoTou id=15
生产了 WoTou id=16
消费了 WoTou id=15
消费了 WoTou id=16
生产了 WoTou id=17
消费了 WoTou id=17
生产了 WoTou id=18
消费了 WoTou id=18
生产了 WoTou id=19
消费了 WoTou id=19
消费了 WoTou id=6
消费了 WoTou id=5
消费了 WoTou id=4
消费了 WoTou id=3
消费了 WoTou id=1
 
参考:
JDK6中文API
尚学堂马士兵老师J2SE视频
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值