您的位置:首页 > 编程语言 > Java开发

Java基础——多线程

2014-11-27 16:52 218 查看

多线程

进程:正在运行的程序,一个进程中至少有一个线程

线程:线程就是进程中的一个控制单元,线程控制着进程的执行

多线程:每个线程在“同时”运行,是因为cpu在做着快读的切换,以达到同时运行的效果


体现:程序主线程在执行,垃圾回收也在执行,这是多线程的体现

1.Thread(继承类)

字段摘要
static int
MAX_PRIORITY
 

          线程可以具有的最高优先级。10
static int
MIN_PRIORITY
 

          线程可以具有的最低优先级。5
static int
NORM_PRIORITY
 

          分配给线程的默认优先级。1
 
构造方法摘要
Thread()
 

          分配新的 
Thread
 对象。
Thread(Runnable target)
 

          分配新的 
Thread
 对象。
Thread(Runnable target, String name)
 

          分配新的 
Thread
 对象。
Thread(String name)
 

          分配新的 
Thread
 对象。
 
方法摘要
static Thread
currentThread()
 

          返回对当前正在执行的线程对象的引用。
 long
getId()
 

          返回该线程的标识符。
 String
getName()
 

          返回该线程的名称。
 int
getPriority()
 

          返回线程的优先级。
 Thread.State
getStat()
 

          返回该线程的状态。
 void
setName(String name)
 

          改变线程名称,使之与参数 
name
 相同。
 void
interrupt()
 

          中断线程。对线程冻结状态进行清除,强制让线程恢复到状态中来,可以操作标记让线程结束
 void
setDaemon(boolean on)
 

          将该线程标记为守护线程或用户线程。当前台线程都结束后,守护线程自动结束(后台线程)
 void
join()
 

          等待该线程终止。获得主线程执行权,等待该线程终止,主线程才执行。只有main方法叫主线程。 throws InterruptedException
 String
toString()
 

          返回该线程的字符串表示形式,包括线程名称、优先级和线程组。
 void
setPriority(int newPriority)
 

          更改线程的优先级。所有线程优先级默认为5
static void
yield()
 

          暂停当前正在执行的线程。临时释放线程的执行权

1.1 要点:

(1)步骤:Thread子类需要重写run()方法,运行run()方法可通过start()方法。此时Java进程下就有两个线程了,主线程和我们创建的线程

(2)run方法:其就是一个存储器,存储线程要运行的代码

(3)start方法:开启新线程并运行。不用start()方法运行run()方法,程序还是单线程

(4)分析:运行结果每一次都不一样,因为多个线程都在争抢cpu执行权,不过每一时刻只能有一个线程在执行(多核除外)

/**
*需求:站台售票实例,通过继承实现多线程
*思路:因为要多个对象共享票的数量,需要对票数进行静态修饰
*/
class ticket extends Thread
{
private static int tic = 100;//票数
public ticket(String stageName){//站台名
super(stageName);
}

public void run(){//线程的方法体
while(true)
if(tic>0)
System.out.println(Thread.currentThread().getName()+"....还剩"+tic--+"张票");
//System.out.println("票已经售完");
}
}

class java
{
public static void main(String[] args){
new ticket("站台1").start();
new ticket("站台2").start();
new ticket("站台3").start();
new ticket("站台4").start();

}
}

1.2 线程运行的状态

(1)被创建:待运行

(2)运行:拥有运行资格以及执行权

(3)冻结:放弃了运行资格

(4)阻塞:拥有运行资格却没有执行权

(5)消亡:没有运行资格




2.Runnable(实现接口)

方法摘要
 void
run()
 

          使用实现接口 
Runnable
 的对象创建一个线程时,启动该线程将导致在独立执行的线程中调用对象的 
run
 方法。

2.1 步骤

class MyThread implements Runnable
{
public void run(){
//多线程要执行的方法体;
System.out.println(Thread.currentThread().getName());
}
}

class java
{
public static void main(String[] args){
//建立自定义线程类对象
Runnable r = new MyThread();

//建立线程类,并通过Thread(Runnable r)来传入自定义线程类对象
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);

//运行多线程
t1.start();
t2.start();

//简易写法
new Thread(r).start();

2.2 特点

(1)好处:Java只支持单继承,如果该类已经是一个体系中子类,那么不能够运用继承来实现该类方法的多线程形式

(2)现状:实现方式最常用

3. 线程的安全

3.1 问题产生原因

(1)重要:当多条语句在操作同一个线程的共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误

3.2 问题的解决(synchronized——同步)

(1)同步代码格式如下

//同步代码块格式
synchronized(对象){//对象为任意对象
需要被同步的代码块
}

//同步函数格式
synchronized 作为函数的修饰符
(2)同步的含义:同步就是确保同一时间段内只有一个线程能访问这段代码
(3)同步的前提

     <1>.必须要有一个或两个以上的线程

     <2>.必须是多个线程共同使用一把锁、监视器(锁就是对象,每一把锁都有自己的状态值,每进去一个线程此状态值都会变化)

(4)注意:同步较为消耗资源,所以尽可能减少判断的同步的次数,同步尽可能少的代码

(5)同步函数的要点

     <1>.同步函数使用的锁是 this

     <2>.静态同步函数使用的锁是 类名.class(字节码文件对象)

(6)死锁:同步嵌套同步,而且必须互相调用时,可能会发生。代码如下:

//死锁实现

//死锁类,自定义线程类
class DeadLock implements Runnable
{
private boolean flag;//标记
DeadLock(boolean flag){
this.flag = flag;
}

public void run(){
if(flag)
synchronized(MyLock.locka){//死锁嵌套形式
System.out.println(Thread.currentThread().getName()+"......locka");

synchronized(MyLock.lockb){
System.out.println(Thread.currentThread().getName()+"......lockb");
}
}
else
synchronized(MyLock.lockb){
System.out.println(Thread.currentThread().getName()+"......lockb");

synchronized(MyLock.locka){
System.out.println(Thread.currentThread().getName()+"......locka");
}
}
}
}

//提供锁的类
class MyLock
{
public static Object locka = new Object();
public static Object lockb = new Object();

}

class java
{
public static void main(String[] args){
new Thread(new DeadLock(true)).start();//多线程的简便写法,会创建两个定义一线程类对象
new Thread(new DeadLock(false)).start();
}
}

4.线程间通信

4.1 情况阐述:

(1)多个线程中每个线程运行的代码都不同

4.2 等待唤醒机制

(1)方法摘要(Object类)

方法摘要
 void
wait()
 

          在其他线程调用此对象的 
notify()
 方法或 
notifyAll()
 方法前,导致当前线程等待。throws InterruptedException
 void
notify()
 

          唤醒在此对象监视器上等待的单个线程。
 void
notifyAll()
 

          唤醒在此对象监视器上等待的所有线程。
 void
wait(long timeout)
 

          在其他线程调用此对象的 
notify()
 方法或 
notifyAll()
 方法,或者超过指定的时间量前,导致当前线程等待。throws InterruptedException
(2)特点
      <1>.操作这些方法的对象是同步中的锁对象,因为可以通过监视器来确定线程。只有在同步中才能使用这些方法

      <2>.等待和唤醒必须是同一把锁,只有同一把锁的等待线程能被同一把锁的notify唤醒

      <3>.因为锁可以是任意对象,所以这些方法定义在Object类中


(3)输入和输出的代码体现(繁琐代码)————wait()、notify()的应用

/**
*需求:等待唤醒机制通过输入输出的形式体现。只适合两个线程,一个输入一个输出
*思路:1.建立资源类,输入类,输出类
*	  2.输入类、输出类是多线程的实现类,并且分别用一个线程来控制其输入输出过程
*	  3.同步解决线程完全问题、等待唤醒机制实现输入一次输出一次的形式
*/
class Res
{
String name;
String sex;
boolean b = false;//等待唤醒的判断依据
}

class Input implements Runnable
{
//资源类的引用
Res r;

//保证传入对象唯一
Input(Res r){
this.r = r;
}

public void run(){
int x = 0;//自定义的判断条件
while(true){
synchronized(r){
if(r.b)
try{r.wait();}catch(Exception e){}//等待机制
if(x==0){
r.name="张欣";
r.sex="女";//输入
}
else{
r.name="Tom";
r.sex="man";
}
x = (x+1)%2;//通过此运算x的值始终为0或1
r.notify();//唤醒机制
r.b = true;
}
}
}
}

class Output implements Runnable
{
//同上
Res r;
Output(Res r){
this.r = r;
}

public void run(){
while(true){
synchronized(r){
if(!r.b)
try{r.wait();}catch(Exception e){}//等待机制
System.out.println(r.name+"......"+r.sex);//输出
r.notify();
r.b = false;
}
}
}
}

class java
{
public static void main(String[] args){
Res r = new Res();//建立唯一的资源类对象

new Thread(new Input(r)).start();
new Thread(new Output(r)).start();//运行多线程
}
}
(4)生产者和消费者的代码体现(简化代码)————while多次判断标记、nitifyAll()唤醒全部等待线程

/**
*需求:用生产者消费者的形式来实现线程通信(有超过两个线程操作程序)
*思路;1.建立资源类、生产者类、消费者类
*	  2.资源类中建立生产消费的函数,来被生产者消费者调用
*	  3.用同步解决线程安全问题,用等待唤醒全部机制保证生产一个消费一个
*/
class Resource
{
private String name;//商品名称
private int count;//商品序号
private boolean flag = false;//判断等待唤醒的标记

Resource(String name){
this.name = name;
}

//生产函数
public synchronized void pdt(){
while(flag)//多次判断标记
try{wait();}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"...生产者..."+(++count)+"."+name);//生产一个商品,每生产一个序号+1
flag = true;
notifyAll();//唤醒全部等待线程
}

//销售函数
public synchronized void sell(){
while(!flag)
try{wait();}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"...消费者........."+count+"."+name);//销售一个商品
flag = false;
notifyAll();
}
}

class Produce implements Runnable
{
private Resource res;//资源引用
Produce(Resource res){//保证资源对象唯一
this.res = res;
}

//多线程run方法,存放生产的过程
public void run(){
while(true){
res.pdt();
}
}
}

class Customer implements Runnable
{
private Resource res;//资源引用
Customer(Resource res){//保证对象唯一
this.res = res;
}

//多线程run方法,存放销售的过程
public void run(){
while(true){
res.sell();
}
}
}

class java
{
public static void main(String[] args){
//建立要用的唯一资源对象
Resource res = new Resource("手办");

//建立两个生产线程
new Thread(new Produce(res)).start();
new Thread(new Produce(res)).start();

//建立两个销售线程
new Thread(new Customer(res)).start();
new Thread(new Customer(res)).start();
}
}

类 ReentrantLock

5. JDK5.0升级

5.1 知识

(1)JDK5.0升级后多线程的同步与等待唤醒机制被替代,工具包位置为 java.util.concurrent.locks

(2)synchronized关键字的功能被lock接口子类ReentrantLock中的方法替代,wait()、nitify()、notifyAll()被Condition接口子类 中的方法所替代

5.2 类概要

Lock接口(常用子类——ReentrantLock)

方法摘要
 void
lock()
 

          获取锁。
 void
unlock()
 

          释放锁。
Condition
newCondition()
 

          返回绑定到此 
Lock
 实例的新 
Condition
 实例。
Condition接口

方法摘要
 void
await()
 

          造成当前线程在接到信号或被中断之前一直处于等待状态。throws InterruptedException
 void
signal()
 

          唤醒一个等待线程。
 void
signalAll()
 

          唤醒所有等待线程。


5.3 实现代码(实现本方只唤醒对方的操作,上边生产者消费者代码的转型)

/**
*需求:用JDK5.0新特性实现生产者和消费者线程通信
*思路;1.建立资源类、生产者类、消费者类
*	  2.资源类中建立生产消费的函数,来被生产者消费者调用
*	  3.用同步解决线程安全问题,用等待唤醒全部机制保证生产一个消费一个
*/
import java.util.concurrent.locks.*;

class Resource
{
private String name;//商品名称
private int count;//商品序号
private boolean flag = false;//判断等待唤醒的标记

private Lock lock = new ReentrantLock();//同步对象

private Condition conditionPro = lock.newCondition();//锁对象
private Condition conditionCus = lock.newCondition();

Resource(String name){
this.name = name;
}

//生产函数
public void pdt(){
lock.lock();//锁住
try{
while(flag)//多次判断标记
conditionPro.await();
System.out.println(Thread.currentThread().getName()+"...生产者..."+(++count)+"."+name);//生产一个商品,每生产一个序号+1
flag = true;
conditionCus.signalAll();//指定唤醒哪些线程
}catch(Exception e){}
finally{
lock.unlock();//释放锁
}
}

//销售函数
public void sell(){
lock.lock();
try{
while(!flag)
conditionCus.await();
System.out.println(Thread.currentThread().getName()+"...消费者........."+count+"."+name);//销售一个商品
flag = false;
conditionPro.signalAll();
}catch(Exception e){}
finally{
lock.unlock();//释放锁
}
}
}

class Produce implements Runnable
{
private Resource res;//资源引用
Produce(Resource res){//保证资源对象唯一
this.res = res;
}

//多线程run方法,存放生产的过程
public void run(){
while(true){
res.pdt();
}
}
}

class Customer implements Runnable
{
private Resource res;//资源引用
Customer(Resource res){//保证对象唯一
this.res = res;
}

//多线程run方法,存放销售的过程
public void run(){
while(true){
res.sell();
}
}
}

class java
{
public static void main(String[] args){
//建立要用的唯一资源对象
Resource res = new Resource("手办");

//建立两个生产线程
new Thread(new Produce(res)).start();
new Thread(new Produce(res)).start();

//建立两个销售线程
new Thread(new Customer(res)).start();
new Thread(new Customer(res)).start();
}
}


知识点:当多个不操作共享数据的程序运行时,分别定义多线程效率较高,可以使用匿名内部类的方式
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息