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

JavaSE学习笔记--多线程基础

2011-10-12 17:22 453 查看
多线程基础

多线程:

1,线程与进程

进程:当前正在执行的程序。代表一个应用程序在内存中的执行区域。

一个进程中可以有多个线程,线程是CPU调度和分派的基本单位。我们可以理解为线程就是程序运行中的一条路径。

一个进程中如果只有一个执行路径,这个程序称为单线程。

一个进程中有多个执行路径时,这个程序成为多线程。

2,举出一个日常生活中多线程的例子。

JVM启动是单线程,还是多线程的呢?

jvm的启动其实就是多线程程序。

其中有一个程序负责从主函数开始执行,并控制程序运行的流程。

同时为了提高效率,还启动了另一个控制单元(执行路径)专门负责堆内存中的垃圾回收。

在程序正常执行过程中,如果出现了垃圾,这是另一个负责收垃圾的线程会在不定时的时间进行垃圾的处理。

这两个程序是同时执行的。

负责执行正常代码的线程,称为主线程。该线程执行的代码都存放于主函数中。

负责收垃圾代码执行的线程,成为垃圾回收线程。该线程要执行的代码在finalize中。

3,两种创建线程的方式

通过API查阅,发现Thread类描述时,有两种创建线程的方式。

方式一:定义一个类继承Thread类,并覆盖Thread类中run方法。

作为了解掌握:

为什么要继承Thread,为什么要覆盖run

其实直接建立Thread类对象即可。并开启线程执行就可以了。

但是虽然线程执行了,可是执行的代码是该线程默认的代码,该代码就存放在run方法中。

可是定义线程的目的是为了执行自定义的代码。

而线程运行代码都存储在run方法中,所以只有覆盖了run方法,才可以运行自定义的内容,

想要覆盖,必须先要继承。

主线程运行的代码都在main函数中,

自定义线程运行的代码都在run方法中。

直接创建Thread类的子类对象就是创建了一个线程。

在内存中其实:1,堆内存中产生了一个对象,2,需要调用了底层资源,去创建执行路径。

如果直接调用该对象的run方法。

这时,底层资源并没有完成线程的创建和执行。

仅仅是是简单的对象调用方法的过程。所以这时,执行控制流程的只有主线程 .

如果想要开启线程,需要去调用Thread类中另一个方法完成。

start方法完成:该方法做了两件事,1,开启线程,2,调用了线程的run方法。

4,线程的名称:

多线程的创建,为了对各个线程进行标识,他们有一个自己默认的名称。

格式:Thread-编号,编号从0开始。

static Thread currentThread():获取当前线程对象。

String getName():获取线程名称。

void setName():设置 线程的名称。

Thread(String name):构造函数,线程对象一建立就可以指定名称。

例如构造函数的方法:

class Demo extends Thread

{

private String name;

Demo(String name)

{

super(name);

//this.name = name;

}

public void run()

{

for(int x=0; x<10; x++)

{

//for(int y=-9999999; y<99999999; y++){}//为了减缓程序的执行。

//因为Demo是Thread类的子类,所以可以直接使用Thread类中的getName方法。获取当前线程的名字。

System.out.println(getName()+"....."+name);

}

}

}

class ThreadDemo3

{

public static void main(String[] args)

{

Demo d1 = new Demo("变形金刚");//创建了一个Thread类的子类对象,其就是在创建一个线程。

Demo d2 = new Demo("哈利波特");

// d1.setName("小强");

// d2.setName("旺财");

d1.start();

d2.start();

for(int x=0; x<20; x++)

{

//如何获取到主线程对象呢?

// //通过Thread类中的一个方法。currentThread()返回当前线程对象。该方法是静态的。

System.out.println(Thread.currentThread().getName()+"---"+x);

}/**/

//System.out.println("MAIN===="+4/0);

}

}

5,线程对象的创建(理解)

创建线程的两种方式:

1,继承Thread类。

步骤:

1,继承Thread类

2,覆盖Thread类的run方法

3,创建Thread类的子类对象创建线程

4,调用Thread类中的start方法开启线程,并执行类中的run方法。

特点:当类去描述事物,事物中有属性和行为。

如果行为中有部分代码需要被多线程执行,同时还在操作属性。就需要该类继承Thread类,

那么产生该类的对象作为线程对象,可是这样做会导致,每个对象都存储一份属性数据。

无法在多个线程中共享该数据。加上静态,虽然实现了共享,但是生命周期太长。

2,实现Runnalbe接口:

步骤:

1,定义类实现Runnalbe接口

2,覆盖接口中的run方法。

3,通过Thread类创建线程对象,并将实现了Runnable接口的子类对象

作为实际参数传递给Thread类的构成函数。

4,调用Thread类中start方法,开启线程,并执行Runnable接口子类中的run方法。

特点:

1,描述失误的类中封装了属性和行为,如果有部分代码被多线程所执行。同时还在操作属性,那么可以通过实现Runnable接口

的方式。

因为该方式是定义一个Runnable接口的子类独显,可以被多个相处所操作实现了数据的共享。

2,实现Runnable的接口的好处,避免了单继承的局限性。

如果一个类明确了自己的父类,那么很遗憾,他就不可以继承Thread。因为java不支持多继承。

6, 创建线程时,如果是通过继承Thread来实现多线程的话,可以通过创建Thread子类对象

子类来实现多线程。但是你是通过实现Runnalbe的方式,需要创建Thread对象来实现,因为

Runnable接口不是Thread的子类。使用时需要将实现Runnalbe的对象作为实参传入线程的

参数列表来实现多线程执行指定的代码。

7,对Runnable的由来(掌握):

实际上,Runnable是将多线程要运行的代码存储的位置抽取出来定义到了Runnable接口当中,同时该接口的初相避免单继承的局限性。

接口中的方法没有抛过异常,只能处理。

8,线程安全问题问题:(Very重要)

线程安全问题,因为线程的随机性,有可能导致多线程在操作数据时发生数据错误的产生。

线程安全问题产生的原因:

当线程中多条代码在操作同一个共享数据时,一个线程将部分执行完,还没有继续执行其他代码时,

被另一个线程获取CPU执行权,这时共享数据有可能出现数据错误。

简单说:多条操作共享数据的代码被多个线程分开执行造成的。

安全问题涉及的内容:

1,共享数据。

2,是否被多条语句操作。

这也是判断多线程程序是否存在安全隐患的依据。

解决安全问题的方式:

java中提供了一个同步机制。

解决原理:让多条操作共享数据的代码在某一时间段,被一个线程执行完,在执行过程中,其他线程不可以参与运算。

同步格式:

同步代码块:

synchronized(对象){

需要同步的代码块;

}

同步的原理:通过一个对象锁,将多条操作数据的代码进行了封装并加锁。

只有持有这个锁的线程才有机会进入同步中去执行,在执行期间,

即使其他线程获取对到执行权,因为没有获取到锁,所以只能在外面等。

只有同步中线程执行完同步中的代码时,才会释放这个锁,那么其他程序

线程才有机会去获取这个锁,并只能有一个获取到而且进入到同步中。

举例:火车上的卫生间,锁机制的最好体现。

同步的好处:同步的出现解决了多线程的安全问题。

同步弊端:

因为多线程每次都要判断这个锁,所以效率会降低。

以后写同步你会发现这样一个问题,如果出现了安全问题后:加入了同步,安全问题已让存在。

因为同步是有前提的:

同步前提:(Very important)

1,必须是两个或者两个以上的线程才可以需要同步。

2,必须要保证多个线程使用同一个线程,才可以实现多个线程被同步。

如果出现加上同步安全问题依然存在,就按照两个前提来排查问题。

//实际问题分析:(银行存款问题)

实现同步中的锁可以有三种情况:

1,同步代码块: 可以使用任意的锁 (建议使用)

2,同步函数: 只能使用this关键字

3,静态同步函数: 该类的字节码文件: xxxooo.class

class TicketWin implements Runnable

{

private int tickets = 100;

Object obj = new Object();

boolean flag = true;

public void run()

{

if(flag)

while(true)

{

synchronized(this)

{

if(tickets>0)

{

try{Thread.sleep(10);}catch(InterruptedException e){}

System.out.println(Thread.currentThread().getName()+"....code...."+tickets--);

}

}

}

else

while(true)

show();

}

public synchronized void show()

{

if(tickets>0)

{

try{Thread.sleep(10);}catch(InterruptedException e){}

System.out.println(Thread.currentThread().getName()+"....show...."+tickets--);

}

}

}

class ThisLockDemo

{

public static void main(String[] args)

{

TicketWin t = new TicketWin();

Thread t1 = new Thread(t);

Thread t2 = new Thread(t);

t1.start();

try{Thread.sleep(10);}catch(Exception e){}//让主线程睡眠10毫秒。让t1有获取到cpu的执行权。去if中的代码块执行。

//让10毫秒后,主线程在有执行资格,获取到执行权,将标记改为false。

//并开启t2.这时t2一定去else中的同步函数执行。

t.flag = false;

t2.start();

}

}

在单例设计模式中的线程同步问题:

/*

单例模式有两种体现形式:

1,饿汉式。

/*

class Single

{

private static final Single s = new Single();

private Single(){}

public static Single getInstance()

{

return s;

}

}

*/

//2,懒汉式。(延迟加载)

/*

当多个线程并发执行getInstance方法时,容易发生线程安全问题。

因为s是共享数据,有多条语句在操作共享数据。

解决方式很简单。只要让getInstance方法具备同步性即可.

这虽然解决了线程安全问题,但是多个线程每一次获取该实例,都要

调用这个方法,每次调用都判断一次锁,所以效率会比较低.

为了保证安全,同时为了提高效率.可以通过双重判断的形式来完成。

原理:就是减少线程判断的锁次数。

虽然解决安全问题,也解决了效率问题,但是代码过多。

所以建议使用饿汉式体现单例设计模式。

但是面试时,考的都是懒汉式。

*/

class Single

{

private static Single s = null;

private Single(){}

public static Single getInstance()

{

if(s==null)

{

synchronized(Single.class)

{

if(s==null)

{

s = new Single();

}

}

}

return s;

}

}

同步的弊端:

1,效率会降低。

2,容易引发死锁。

死锁经常出现的现状为:同步嵌套。

死锁事例:

class Test implements Runnable

{

private boolean flag;

Test(boolean flag)

{

this.flag = flag;

}

public void run()

{

if(flag)

{

while(true)

{

synchronized(MyLock.locka)

{

System.out.println(Thread.currentThread().getName()+"...if......locka");

synchronized(MyLock.lockb)

{

System.out.println(Thread.currentThread().getName()+"...if......lockb");

}

}

}

}

else

{

while(true)

{

synchronized(MyLock.lockb)

{

System.out.println(Thread.currentThread().getName()+"...else..........lockb");

synchronized(MyLock.locka)

{

System.out.println(Thread.currentThread().getName()+"...else..........locka");

}

}

}

}

}

}

class MyLock

{

public static Object locka = new Object();

public static Object lockb = new Object();

}

class DeadLockTest

{

public static void main(String[] args)

{

Test t1 = new Test(true);

Test t2 = new Test(false);

Thread th1 = new Thread(t1,"小强");

Thread th2 = new Thread(t2,"旺财");

th1.start();

th2.start();

}

}

9,线程通信和JDK1.5之后的同步方法(Very重要)

/*

在jdk1.5版本之后,

出现了一些新的特性,将原理的线程进行了改良。

在java.util.concurrent.locks包中提供了一个接口Lock。替代了synchronized。

synchronized。使用的是锁操作是隐式的。

Lock接口,使用的锁操作是显示的。

由两个方法来完成:

lock():获取锁。

unlock():释放锁。

还有一个对象,Condition.

该对象的出现替代了Object中的wait notify notifyAll这些操作监视器的方法。

替代后的方式:await signal signalAll.

接下来,把下列代码替换成JDK1.5版本只有的新对象。

新功能最大好处,就是在一个Lock锁上,可以添加多组监视器对象。

这样就可以实现本方只唤醒对方的线程

锁,是同步的机制.通过锁来控制同步.监视器是用于同步中对象的操作.

比如wait,notify notifyAll.每一组监视器方法对应一个锁.

到了jdk1.5以后,将监视器的方式从Object中,封装到了Condition对象中,

每一个锁lock,可以对应多组监视器对象,这就可以实现本方只唤醒对方的操作。

*/

import java.util.concurrent.locks.*;

class Res

{

private String name;

private int count = 0;

private boolean b = false;

//定义一个锁。

Lock lock = new ReentrantLock();

//通过指定的锁,创建了一个该锁上可以使用了监视器对象。

Condition proCon = lock.newCondition();

//升级后的lock可以对应多组监视器对象。

Condition cusCon = lock.newCondition();

public void set(String name)

{

//获取锁。

lock.lock();

try

{

while(b)

proCon.await();

this.name = name+"--------"+count;

count++;

System.out.println(Thread.currentThread().getName()+".....生产者...."+this.name);

b = true;

cusCon.signal();

}

catch(InterruptedException e)

{

}

finally

{

//释放锁

lock.unlock();

}

}

public void out()

{

lock.lock();

try

{

while(!b)

cusCon.await();

System.out.println(Thread.currentThread().getName()+"----消费者---"+this.name);

b = false;

proCon.signal();

}

catch (InterruptedException e)

{

}

finally

{

lock.unlock();

}

}

}

class Pro implements Runnable

{

private Res r;

Pro(Res r)

{

this.r = r;

}

public void run()

{

while(true)

{

r.set("产品");

}

}

}

class Cus implements Runnable

{

private Res r;

Cus(Res r)

{

this.r = r;

}

public void run()

{

while(true)

{

r.out();

}

}

}

class ProCusDemo2

{

public static void main(String[] args)

{

Res r = new Res();

Pro p = new Pro(r);

Cus c = new Cus(r);

Thread t1 = new Thread(p);

Thread t2 = new Thread(p);

Thread t3 = new Thread(c);

Thread t4 = new Thread(c);

//t1,t2都是生产者。

//t3,t3都是消费者。

t1.start();

t2.start();

t3.start();

t4.start();

}

}

以上

多线程:

1,线程与进程

进程:当前正在执行的程序。代表一个应用程序在内存中的执行区域。

一个进程中可以有多个线程,线程是CPU调度和分派的基本单位。我们可以理解为线程就是程序运行中的一条路径。

一个进程中如果只有一个执行路径,这个程序称为单线程。

一个进程中有多个执行路径时,这个程序成为多线程。

2,举出一个日常生活中多线程的例子。

JVM启动是单线程,还是多线程的呢?

jvm的启动其实就是多线程程序。

其中有一个程序负责从主函数开始执行,并控制程序运行的流程。

同时为了提高效率,还启动了另一个控制单元(执行路径)专门负责堆内存中的垃圾回收。

在程序正常执行过程中,如果出现了垃圾,这是另一个负责收垃圾的线程会在不定时的时间进行垃圾的处理。

这两个程序是同时执行的。

负责执行正常代码的线程,称为主线程。该线程执行的代码都存放于主函数中。

负责收垃圾代码执行的线程,成为垃圾回收线程。该线程要执行的代码在finalize中。

3,两种创建线程的方式

通过API查阅,发现Thread类描述时,有两种创建线程的方式。

方式一:定义一个类继承Thread类,并覆盖Thread类中run方法。

作为了解掌握:

为什么要继承Thread,为什么要覆盖run

其实直接建立Thread类对象即可。并开启线程执行就可以了。

但是虽然线程执行了,可是执行的代码是该线程默认的代码,该代码就存放在run方法中。

可是定义线程的目的是为了执行自定义的代码。

而线程运行代码都存储在run方法中,所以只有覆盖了run方法,才可以运行自定义的内容,

想要覆盖,必须先要继承。

主线程运行的代码都在main函数中,

自定义线程运行的代码都在run方法中。

直接创建Thread类的子类对象就是创建了一个线程。

在内存中其实:1,堆内存中产生了一个对象,2,需要调用了底层资源,去创建执行路径。

如果直接调用该对象的run方法。

这时,底层资源并没有完成线程的创建和执行。

仅仅是是简单的对象调用方法的过程。所以这时,执行控制流程的只有主线程 .

如果想要开启线程,需要去调用Thread类中另一个方法完成。

start方法完成:该方法做了两件事,1,开启线程,2,调用了线程的run方法。

4,线程的名称:

多线程的创建,为了对各个线程进行标识,他们有一个自己默认的名称。

格式:Thread-编号,编号从0开始。

static Thread currentThread():获取当前线程对象。

String getName():获取线程名称。

void setName():设置 线程的名称。

Thread(String name):构造函数,线程对象一建立就可以指定名称。

例如构造函数的方法:

class Demo extends Thread

{

private String name;

Demo(String name)

{

super(name);

//this.name = name;

}

public void run()

{

for(int x=0; x<10; x++)

{

//for(int y=-9999999; y<99999999; y++){}//为了减缓程序的执行。

//因为Demo是Thread类的子类,所以可以直接使用Thread类中的getName方法。获取当前线程的名字。

System.out.println(getName()+"....."+name);

}

}

}

class ThreadDemo3

{

public static void main(String[] args)

{

Demo d1 = new Demo("变形金刚");//创建了一个Thread类的子类对象,其就是在创建一个线程。

Demo d2 = new Demo("哈利波特");

// d1.setName("小强");

// d2.setName("旺财");

d1.start();

d2.start();

for(int x=0; x<20; x++)

{

//如何获取到主线程对象呢?

// //通过Thread类中的一个方法。currentThread()返回当前线程对象。该方法是静态的。

System.out.println(Thread.currentThread().getName()+"---"+x);

}/**/

//System.out.println("MAIN===="+4/0);

}

}

5,线程对象的创建(理解)

创建线程的两种方式:

1,继承Thread类。

步骤:

1,继承Thread类

2,覆盖Thread类的run方法

3,创建Thread类的子类对象创建线程

4,调用Thread类中的start方法开启线程,并执行类中的run方法。

特点:当类去描述事物,事物中有属性和行为。

如果行为中有部分代码需要被多线程执行,同时还在操作属性。就需要该类继承Thread类,

那么产生该类的对象作为线程对象,可是这样做会导致,每个对象都存储一份属性数据。

无法在多个线程中共享该数据。加上静态,虽然实现了共享,但是生命周期太长。

2,实现Runnalbe接口:

步骤:

1,定义类实现Runnalbe接口

2,覆盖接口中的run方法。

3,通过Thread类创建线程对象,并将实现了Runnable接口的子类对象

作为实际参数传递给Thread类的构成函数。

4,调用Thread类中start方法,开启线程,并执行Runnable接口子类中的run方法。

特点:

1,描述失误的类中封装了属性和行为,如果有部分代码被多线程所执行。同时还在操作属性,那么可以通过实现Runnable接口

的方式。

因为该方式是定义一个Runnable接口的子类独显,可以被多个相处所操作实现了数据的共享。

2,实现Runnable的接口的好处,避免了单继承的局限性。

如果一个类明确了自己的父类,那么很遗憾,他就不可以继承Thread。因为java不支持多继承。

6, 创建线程时,如果是通过继承Thread来实现多线程的话,可以通过创建Thread子类对象

子类来实现多线程。但是你是通过实现Runnalbe的方式,需要创建Thread对象来实现,因为

Runnable接口不是Thread的子类。使用时需要将实现Runnalbe的对象作为实参传入线程的

参数列表来实现多线程执行指定的代码。

7,对Runnable的由来(掌握):

实际上,Runnable是将多线程要运行的代码存储的位置抽取出来定义到了Runnable接口当中,同时该接口的初相避免单继承的局限性。

接口中的方法没有抛过异常,只能处理。

8,线程安全问题问题:(Very重要)

线程安全问题,因为线程的随机性,有可能导致多线程在操作数据时发生数据错误的产生。

线程安全问题产生的原因:

当线程中多条代码在操作同一个共享数据时,一个线程将部分执行完,还没有继续执行其他代码时,

被另一个线程获取CPU执行权,这时共享数据有可能出现数据错误。

简单说:多条操作共享数据的代码被多个线程分开执行造成的。

安全问题涉及的内容:

1,共享数据。

2,是否被多条语句操作。

这也是判断多线程程序是否存在安全隐患的依据。

解决安全问题的方式:

java中提供了一个同步机制。

解决原理:让多条操作共享数据的代码在某一时间段,被一个线程执行完,在执行过程中,其他线程不可以参与运算。

同步格式:

同步代码块:

synchronized(对象){

需要同步的代码块;

}

同步的原理:通过一个对象锁,将多条操作数据的代码进行了封装并加锁。

只有持有这个锁的线程才有机会进入同步中去执行,在执行期间,

即使其他线程获取对到执行权,因为没有获取到锁,所以只能在外面等。

只有同步中线程执行完同步中的代码时,才会释放这个锁,那么其他程序

线程才有机会去获取这个锁,并只能有一个获取到而且进入到同步中。

举例:火车上的卫生间,锁机制的最好体现。

同步的好处:同步的出现解决了多线程的安全问题。

同步弊端:

因为多线程每次都要判断这个锁,所以效率会降低。

以后写同步你会发现这样一个问题,如果出现了安全问题后:加入了同步,安全问题已让存在。

因为同步是有前提的:

同步前提:(Very important)

1,必须是两个或者两个以上的线程才可以需要同步。

2,必须要保证多个线程使用同一个线程,才可以实现多个线程被同步。

如果出现加上同步安全问题依然存在,就按照两个前提来排查问题。

//实际问题分析:(银行存款问题)

实现同步中的锁可以有三种情况:

1,同步代码块: 可以使用任意的锁 (建议使用)

2,同步函数: 只能使用this关键字

3,静态同步函数: 该类的字节码文件: xxxooo.class

class TicketWin implements Runnable

{

private int tickets = 100;

Object obj = new Object();

boolean flag = true;

public void run()

{

if(flag)

while(true)

{

synchronized(this)

{

if(tickets>0)

{

try{Thread.sleep(10);}catch(InterruptedException e){}

System.out.println(Thread.currentThread().getName()+"....code...."+tickets--);

}

}

}

else

while(true)

show();

}

public synchronized void show()

{

if(tickets>0)

{

try{Thread.sleep(10);}catch(InterruptedException e){}

System.out.println(Thread.currentThread().getName()+"....show...."+tickets--);

}

}

}

class ThisLockDemo

{

public static void main(String[] args)

{

TicketWin t = new TicketWin();

Thread t1 = new Thread(t);

Thread t2 = new Thread(t);

t1.start();

try{Thread.sleep(10);}catch(Exception e){}//让主线程睡眠10毫秒。让t1有获取到cpu的执行权。去if中的代码块执行。

//让10毫秒后,主线程在有执行资格,获取到执行权,将标记改为false。

//并开启t2.这时t2一定去else中的同步函数执行。

t.flag = false;

t2.start();

}

}

在单例设计模式中的线程同步问题:

/*

单例模式有两种体现形式:

1,饿汉式。

/*

class Single

{

private static final Single s = new Single();

private Single(){}

public static Single getInstance()

{

return s;

}

}

*/

//2,懒汉式。(延迟加载)

/*

当多个线程并发执行getInstance方法时,容易发生线程安全问题。

因为s是共享数据,有多条语句在操作共享数据。

解决方式很简单。只要让getInstance方法具备同步性即可.

这虽然解决了线程安全问题,但是多个线程每一次获取该实例,都要

调用这个方法,每次调用都判断一次锁,所以效率会比较低.

为了保证安全,同时为了提高效率.可以通过双重判断的形式来完成。

原理:就是减少线程判断的锁次数。

虽然解决安全问题,也解决了效率问题,但是代码过多。

所以建议使用饿汉式体现单例设计模式。

但是面试时,考的都是懒汉式。

*/

class Single

{

private static Single s = null;

private Single(){}

public static Single getInstance()

{

if(s==null)

{

synchronized(Single.class)

{

if(s==null)

{

s = new Single();

}

}

}

return s;

}

}

同步的弊端:

1,效率会降低。

2,容易引发死锁。

死锁经常出现的现状为:同步嵌套。

死锁事例:

class Test implements Runnable

{

private boolean flag;

Test(boolean flag)

{

this.flag = flag;

}

public void run()

{

if(flag)

{

while(true)

{

synchronized(MyLock.locka)

{

System.out.println(Thread.currentThread().getName()+"...if......locka");

synchronized(MyLock.lockb)

{

System.out.println(Thread.currentThread().getName()+"...if......lockb");

}

}

}

}

else

{

while(true)

{

synchronized(MyLock.lockb)

{

System.out.println(Thread.currentThread().getName()+"...else..........lockb");

synchronized(MyLock.locka)

{

System.out.println(Thread.currentThread().getName()+"...else..........locka");

}

}

}

}

}

}

class MyLock

{

public static Object locka = new Object();

public static Object lockb = new Object();

}

class DeadLockTest

{

public static void main(String[] args)

{

Test t1 = new Test(true);

Test t2 = new Test(false);

Thread th1 = new Thread(t1,"小强");

Thread th2 = new Thread(t2,"旺财");

th1.start();

th2.start();

}

}

9,线程通信和JDK1.5之后的同步方法(Very重要)

/*

在jdk1.5版本之后,

出现了一些新的特性,将原理的线程进行了改良。

在java.util.concurrent.locks包中提供了一个接口Lock。替代了synchronized。

synchronized。使用的是锁操作是隐式的。

Lock接口,使用的锁操作是显示的。

由两个方法来完成:

lock():获取锁。

unlock():释放锁。

还有一个对象,Condition.

该对象的出现替代了Object中的wait notify notifyAll这些操作监视器的方法。

替代后的方式:await signal signalAll.

接下来,把下列代码替换成JDK1.5版本只有的新对象。

新功能最大好处,就是在一个Lock锁上,可以添加多组监视器对象。

这样就可以实现本方只唤醒对方的线程

锁,是同步的机制.通过锁来控制同步.监视器是用于同步中对象的操作.

比如wait,notify notifyAll.每一组监视器方法对应一个锁.

到了jdk1.5以后,将监视器的方式从Object中,封装到了Condition对象中,

每一个锁lock,可以对应多组监视器对象,这就可以实现本方只唤醒对方的操作。

*/

import java.util.concurrent.locks.*;

class Res

{

private String name;

private int count = 0;

private boolean b = false;

//定义一个锁。

Lock lock = new ReentrantLock();

//通过指定的锁,创建了一个该锁上可以使用了监视器对象。

Condition proCon = lock.newCondition();

//升级后的lock可以对应多组监视器对象。

Condition cusCon = lock.newCondition();

public void set(String name)

{

//获取锁。

lock.lock();

try

{

while(b)

proCon.await();

this.name = name+"--------"+count;

count++;

System.out.println(Thread.currentThread().getName()+".....生产者...."+this.name);

b = true;

cusCon.signal();

}

catch(InterruptedException e)

{

}

finally

{

//释放锁

lock.unlock();

}

}

public void out()

{

lock.lock();

try

{

while(!b)

cusCon.await();

System.out.println(Thread.currentThread().getName()+"----消费者---"+this.name);

b = false;

proCon.signal();

}

catch (InterruptedException e)

{

}

finally

{

lock.unlock();

}

}

}

class Pro implements Runnable

{

private Res r;

Pro(Res r)

{

this.r = r;

}

public void run()

{

while(true)

{

r.set("产品");

}

}

}

class Cus implements Runnable

{

private Res r;

Cus(Res r)

{

this.r = r;

}

public void run()

{

while(true)

{

r.out();

}

}

}

class ProCusDemo2

{

public static void main(String[] args)

{

Res r = new Res();

Pro p = new Pro(r);

Cus c = new Cus(r);

Thread t1 = new Thread(p);

Thread t2 = new Thread(p);

Thread t3 = new Thread(c);

Thread t4 = new Thread(c);

//t1,t2都是生产者。

//t3,t3都是消费者。

t1.start();

t2.start();

t3.start();

t4.start();

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: