多线程基础
多线程:
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();
}
}