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视频
相关推荐
读者将通过使用java.lang.thread类、synchronized和volatile关键字,以及wait、notify和notifyall方法,学习如何初始化、控制和协调并发操作。此外,本书还提供了有关并发编程的全方位的详细内容,例如限制和同步、...
本资源致力于向您介绍 Java 并发编程中的线程基础,涵盖了多线程编程的核心概念、线程的创建和管理,以及线程间通信的基本方法。通过深入学习,您将建立扎实的多线程编程基础,能够更好地理解和应用多线程编程。 多...
本文的主题是关于具有java语言风格的Thread、synchronized、volatile,以及J2SE5中新增的概念,如锁(Lock)、原子性(Atomics)、并发集合类、线程协作摘要、Executors。开发者通过这些基础的接口可以构建高并发、线程...
其方法签名为executor(Runnable command),该方法接收一个Runable实例,它用来执行一个任务,任务即一个实现了Runnable接口的类,一般来说,Runnable任务开辟在新线程中的使用方法为:new Thread(new RunnableTask())...
本书全面介绍了如何使用Java 2平台进行并发编程,较上一版新增和扩展的内容包括:, ·存储模型 ·取消 ·可移植的并行编程 ·实现并发控制的工具类, Java平台提供了一套广泛而功能强大的api,工具和技术。...
Java并发编程---Thread类!!
然后介绍了Thread类和Runnable接口,它们是Java并发API的重要组成部分。接着讨论了如何利用Java并发API的各种元素(从基本技巧到高级技巧),以及如何在强大的真实并发应用程序中实现它们。最后详细介绍了测试并发...
java7在并发编程方面,带来了很多令人激动的新功能,这将使你的应用程序具备更好的并行任务性能。 《Java 7并发编程实战手册》是Java 7并发编程的实战指南,介绍了Java 7并发API中大部分重要而有用的机制。全书分为9...
这本书名为《Java并发编程实践》有些抹杀了它的价值,其中并非只讲述了Java的多线程设施,对一般的并发编程的rationale也有相当透彻的阐述。之前看过各种线程库,pThread, Boost Thread, Java Thread, Qt Thread,...
这本书名为《Java并发编程实践》有些抹杀了它的价值,其中并非只讲述了Java的多线程设施,对一般的并发编程的rationale也有相当透彻的阐述。之前看过各种线程库,pThread, Boost Thread, Java Thread, Qt Thread,...
│ Java并发编程.png │ ppt+源码.rar │ 高并发编程第二阶段01讲、课程大纲及主要内容介绍.wmv │ 高并发编程第二阶段02讲、介绍四种Singleton方式的优缺点在多线程情况下.wmv │ 高并发编程第二阶段03讲、...
Java并发编程 背景介绍 并发历史 必要性 进程 资源分配的最小单位 线程 CPU调度的最小单位 线程的优势 (1)如果设计正确,多线程程序可以通过提高处理器资源的利用率来提升系统吞吐率 ...
掌握Java的高级特性:掌握Java的反射、泛型、注解、并发编程等高级特性,以及Java集合框架和并发包的使用。 掌握Java Web开发:掌握Java Web开发的相关技术,如Servlet、JSP、Spring、Hibernate等,能够开发基于Web...
有关java高并发知识总结:三种线程创建方式 深入理解Thread构造函数 Thread API #### CAS缺陷 ##### 循环时间长开销大,自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。 ##### 只能保证一个共享变量...
Java并发编程实战(中文版)
第一章 Java 并发编程实践基础..............................................................1 1.1 进程与线程.................................................................................................
第三部分详细、深入地介绍volatile关键字的语义,volatile关键字在Java中非常重要,可以说它奠定了Java核心并发包的高效运行,在这一部分中,我们通过实例展示了如何使用volatile关键字以及非常详细地介绍了Java内存...
学习Java其实应该上升到如何学习程序设计这种境界,其实学习程序设计又是接受一种编程思想。每一种语言的程序设计思想大同小异,只是一些由语言特性的而带来的细微差别,比如Java中的Interface,你几乎在以前的学习...
并发编程艺术
java并发编程源码分析