黑马程序员——Java多线程--------------------
---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<a
href="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! -------------------------------
一、基本概念:
线程:进程中一个独立的控制单元
多线程:一个程序中负责程序执行的单元(执行路径),一个程序有多个执行路径称多线程。
jvm启动至少两个线程在运行,一个main函数线程,一个Java垃圾回收机制线程。
二、线程创建:
创建线程的方式:1.继承Thread类,2实现Runable接口
1.定义继承 Thread 类
2.复写 Thread 类中的run()方法,自定义代码存在run()方法中,让线程运行
3.调用线程star()方法,作用:启动线程,调用run()方法
class Demo extends Thread
{
private String name;
Demo(String name){
super(name);
}
public void run(){ //自定义线程任务的函数
for(int i=0;i<10;i++){
System.out.println(name+"....i"=i+".....name="+Thread.currentThread().getName());
}
}
}
class ThreadDemo
{
public static void main(String args[]){
Demo d=new Demo();//创建好一个线程
Thread t1=new Thread(d);
Thread t2=new Thread(d);
t1.start();
t2.start();
// Demo d1=new Demo("小布");
// Demo d2=new Demo("小强");
//d1.start(); //开启线程并执行该线程run()方法
// d2.start(); //仅对象调用方法,线程创建了,并没有执行
d.run();
System.out.println("线程完成了"+Thread.currentThread().getName());
}
}
习题:
创建线程,和主线程进行交替
线程有名称,通过set/get
package com.study;
public class Test {
public static void main(String[] args) {
Thread t1=new Thread("one----");
Thread t2=new Thread("two+++++");
t1.start();
t2.start();
t1.run();
t2.run();
for(int x=0;x<60;x++){
System.out.println(Thread.currentThread().getName()+"run...."+x);//getName获取当前线程名称
}
}
}
class ThreadTest extends Thread{
private String name;
ThreadTest(String name){
this.name=name;
}
public void run(){
for(int x=0;x<60;x++){
System.out.print(this.name+"run...."+x);
}
}
}
线程状态:
1.新建:start()启动线程
2.运行:run();具备执行资格和执行权
3.冻结:wait()等待,notify()唤醒、sleep(time)睡眠
4.阻塞:线程有CPU执行资格,但是没有CPU执行权
5.消亡:stop
创建线程方式二: 实现Runable接口
1.定义类实现Runnable接口
2.覆盖Runnable接口中的run方法,将线程任务代码封装到run方法中。
3.通过 Thread 类创建对象,将 Runnable 接口中的子类对象作为 Thread 的构造参数进行传递,
线程的任务封装在Run()方法中,要让线程对象明确运行的run方法的所属对象。
4.调用Thread类中的Start()方法开启线程
实现Runable接口好处:
1.将线程任务从线程子类中分离出来,进行单独封装,按照面向对象的思想,将任务封装成对象。
2.避免Java单继承的局限性。
package com.study;
public class TickDemo {
/**
*多线程实现多个窗口卖票
*/
public static void main(String[] args) {
Ticket t=new Ticket(); //创建买票对象,使用其run方法
Thread t1=new Thread(t); //创建对个线程,传人t作为Thread 的构造参数进行传递实现对个窗口买票
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start(); //启动线程,调用Tick run方法
t2.start();
t3.start();
t4.start();
}
}
class Ticket implements Runnable{
private int tick=100;//票数
@Override
public void run() {
while(tick>0){
System.out.println(Thread.currentThread().getName()+"....sale:"+tick--);
}
}
}
三、线程安全问题:
1.多个线程同时操作共享的数据。
2.操作共享数据线程代码有多条。
当一个线程执行操作共享数据时,可能被另外一个线程也在进行访问操作。
(一个在执行修改操作,另一个删除的话就导致线程安全问题)
解决线程安全问题:
同步代码块:
将多条共享数据线程代码块封装起来,有线程访问时,其他线程停止操作不参与运算,当前线程执行完,其他才进行运算。
Java 专业解决方式:同步代码块
synchronized(对象) --对象锁,标志(火车卫生间)
{
需要同步的共享代码块
}
对象如同锁,持有锁的线程可以再同步中执行,没有锁的线程即使获得CPU执行权也无进不去。
同步好处:解决线程安全问题
同步的弊端:相对降低效率,同步的线程都会判断同步锁,消耗资源。
同步的前提:1.必须有多个线程(两个或两个以上)并使用同一个对像锁。
2.保证同步中只有一个线程运行
package com.study;
public class TickDemo {
/**
*多线程实现多个窗口卖票
*/
public static void main(String[] args) {
Ticket t=new Ticket(); //创建买票对象,使用其run方法
Thread t1=new Thread(t); //创建对个线程,传人t,实现对个窗口买票
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Ticket implements Runnable{
private int tick=1000;//票数
Object obj=new Object();
@Override
public void run() {
while(true){
synchronized (obj) { //线程同步对象锁,如线程t1执行是上锁,其它线程2、3、4无法进入线程中
if(tick>0){
try {
Thread.sleep(10);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println(Thread.currentThread().getName()+"....sale:"+tick--);
}
}
}
}
}
案例:银行存钱案例,同步函数更方便
package com.study;
public class BankDemo {
/**
* 银行储户,两个,每个都到银行存钱每次100元,共存了3次
*/
public static void main(String[] args) {
Cus c=new Cus();
Thread t1=new Thread(c); //创建线程对象
Thread t2=new Thread(c);
t1.start(); //启动线程
t2.start();
}
}
class Bank{
private int sum;//
//private Object obj=new Object();
public synchronized void add(int num){ //同步函数 ,存钱动作
sum=sum+num;
try {
Thread.sleep(10); //调用Thread.sleep() 方法
} catch (Exception ex) {
ex.printStackTrace();
}
System.out.println("总存了"+sum);
}
}
class Cus implements Runnable
{
private Bank b=new Bank();
@Override
public void run() {
for(int i=0;i<3;i++){
b.add(100);
}
}
}
同步函数:1.函数需要被对象调用,函数都有一个所属对象的引用,就是this
2.同步函数使用的对象锁是this
3 当同步函数被static 静态方法修饰,对象锁不再是this
原因:(重要)
静态进内存没有本类对象,但该类有字节码对象,不存在this对象,
类名.class 该对象类型是class,格式synchronized (对象名.class)。
验证:使用两个线程卖票
一个线程同步代码块
一个线程同步函数
都在执行卖票动作
package com.study;
public class TickDemo {
/**
*多线程实现多个窗口卖票
*/
public static void main(String[] args) {
Ticket t=new Ticket(); //创建买票对象,使用其run方法
Thread t1=new Thread(t); //创建对个线程,传人t,实现对个窗口买票
Thread t2=new Thread(t);
t1.start();
//t.flag=false; //主线程有执行权,瞬间执行完t2结束
try {
Thread.sleep(10);
} catch (Exception e) {
// TODO: handle exception
}
//t.flag=false;
t2.start();
}
}
class Ticket implements Runnable{
private int tick=100;//票数
boolean flag=true; //标记线程
Object obj=new Object();
@Override
public void run() {
if(flag){
while(true){
synchronized (this) { //执行同步代码块 (如果static修饰同步函数 对象为Ticket.class)
if(tick>0){
try {
Thread.sleep(10);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println(Thread.currentThread().getName()+"....code。。。。:"+tick--);
}
show();
}
}
}else{
while(true)
show();
}
}
public synchronized void show(){ //执行同步函数,this对象锁
if(tick>0){
try {
Thread.sleep(10);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println(Thread.currentThread().getName()+"....show。。。:"+tick--);
}
}
}
多线程的单例模式
单例模式:保证一个类在内存中的对象唯一性,一个类就一个对象实例。
步骤
1,私有化构造函数;
2,创建私有并静态的本类对象;
3,定义公有并静态的方法,返回该对象。
1.饿汉模式
类加载的时候就创建实例
class Single{
private static Single(){} //1.私有构造函数,防止外部创建实例对象
private static Single s=new Single();//2.创建静态私有本类对象
public static Single getInstance(){ //3.定义共有静态方法提供外部访问,返回该对象
return s
}
}
懒汉模式:
优点:实例延迟加载。
用于多线程,有安全问题,同步函数比较低效,使用同步代码块,多重判断提高效率
class Single
{
private Single(){}//私有构造函数,防止外部创建实例对象
private static Single s=null;//2.创建对象
public static Single getInstance(){ //3.提供方法
if(s==null){ //双重判断提高效率
synchronized(Single.class){ //静态对象锁不再是this,而是 类.class
if(s==null)
s=new Single();
}
return s;
}
}
class SingleDemo
{
public static void main(String [] args){
System.out.print("hell,world");
}
}
多线程同步锁:死锁
1.当两个线程相互调用 Thread.join ()
2.当两个线程使用嵌套的同步块,一个线程占用了另外一个线程必需的锁,互相等待时被阻塞就有可能出现死锁。
通俗讲:同步中嵌套同步,A锁嵌套B、B锁又嵌套A 互相僵持不释放锁 就死锁啦。
package com.itheima.thread.demo;
public class LockDemo {
/**
* @param args
*/
public static void main(String[] args) {
Thread t1=new Thread(new TestLock(true));
Thread t2=new Thread(new TestLock(false));
t1.start();
t2.start();
}
}
class TestLock implements Runnable{
private boolean flag; //定义一个标记
TestLock(boolean flag){
this.flag=flag;
}
@Override
public void run() {
if(flag){
while(true){
synchronized(MyLock.locka){
System.out.println("这是锁a");
synchronized(MyLock.lockb){
System.out.print("这是锁b");
}
}
}
}else{
while(true){
synchronized(MyLock.lockb){
System.out.println("这是锁b");
synchronized(MyLock.locka){
System.out.print("这是锁a");
}
}
}
}
}
}
class MyLock{
static Object locka=new Object();
static Object lockb=new Object();
}
线程间的通讯:线程间的通讯就是多个线程操作同一个资源,但是操作动作不同
package com.itheima.thread.demo;
public class Res {
/**
* @param 胡田
*/
String name;
String sex;
public static void main(String[] args) {
// 创建资源
Res r=new Res();
Input in=new Input(r);
Output out=new Output(r);
//执行路径
Thread t1=new Thread(in);
Thread t2=new Thread(out);
t1.start();
t2.start();
}
}
class Input implements Runnable{
//Res rs=new Res();
private Res r;
Input(Res r){ //初始化动作传人要操作的内容
this.r=r;
}
@Override
public void run() {
int x=0;
while(true){
synchronized(r) //对象锁
{
if(x==0){
r.name="小强";
r.sex="男";
}else{
r.name="小红";
r.sex="女";
}
x=(x+1)%2;
}
}
}
}
class Output implements Runnable{
//Res rs=new Res();
private Res r;
Output(Res r){ //初始化传人要操作的内容
this.r=r;
}
@Override
public void run() {
System.out.println(r.name+"........"+r.sex);
}
}
Lock接口:
Lock接口
Lock 替代了synchronized
Condition 替代了 Object监视器方法
好处:将同步synchronized 替换成了 Lock
将object中的wait notify notifyAll 替换成了 Condition对象
该对象可以Lock锁进行获取。一个锁可以对应多个Condition对象
注意:释放锁的工作一定要执行
。
示例代码
private Lock lock=new ReentrantLock();
private Condition condition =lock.newCondition();
public void cet(String name ) throws
{
lock.lock();
try
{
while(flag)
contidition.await();
this.name=name+"--"+count++;
sop(Thread.currentThread().getName()+"...生产者..."+this.name)
flag=true;
condition.signalAll();
}
finally
{
lock.unlock();
}
}
---多生产者多消费模式-----
注意:if 判断标记与notify()方法对应,只判断一次,会导致不改运行的线程运行起来
while 判断标记跟notifyAll()方法对应,解决获取执行权后是否要运行
package com.itheima.thread.demo;
public class ProductDemo {
/**
* @param args
*/
public static void main(String[] args) {
Resource res=new Resource();
Producter pd=new Producter(res);
Consumer cos=new Consumer(res);
Thread t1=new Thread(pd);
Thread t2=new Thread(cos);
t1.start();
t2.start();
}
}
class Resource{
private String name;
private int count=1;
private static boolean flag=false;
//生产
public synchronized void set(String name){
while(flag) //不用if,if判断标记与notify()方法对应,只判断一次,会导致不改运行的线程运行起来
try {
this.wait();
} catch (Exception e) {
// TODO: handle exception
}
this.name=name+"..."+count++; //烤鸭1、烤鸭2、烤鸭3
System.out.print(Thread.currentThread().getName()+"......生产者"+this.name);
flag=true;
notifyAll(); //唤醒所有等待的线程
}
//消费
public synchronized void Out (){
while(!flag) //标记while 每次notify 都会判断一次
try {
wait();
} catch (Exception e) {
// TODO: handle exception
}
System.out.print(Thread.currentThread().getName()+"......消费者"+this.name);
flag=false;
notifyAll();
}
}
class Producter implements Runnable{
private Resource res;
Producter(Resource res){
this.res=res;
}
@Override
public void run() {
while(true){
res.set("北京烤鸭");
}
}
}
class Consumer implements Runnable{
private Resource res;
Consumer(Resource res){
this.res=res;
}
@Override
public void run() {
while(true){
res.Out();
}
}
}
总结:
等待唤醒机制:
notify:唤醒线程池中的一个线程,本线程唤醒自己没有意义,while判断标记+notify 会导致线程死锁的。。
notifyAll:解决本线程线程一定会唤醒对方线程的问题。
wait():让线程处于冻结等待状态,wait的线程会被存储到线程池中。。。
sleep:线程进入睡眠状态,并不释放锁
notify、wait、notifyAll 要对持有监视器对象(锁)的操作,所以用在同步中synchnized 中
wait和sleep的区别:
1.wait可以指定时间也可以不指定,sleep必须指定睡眠时间,sleep(10)
2.同步时wait线程等待释放执行权,释放锁,交给别的线程执行
sleep 睡眠状态释放执行权,但并不释放锁。
为什么操作线程锁的方法在Object类中?
因为notify、wait、notifyAll 这些方法操作线程时,都必须标识他们所操作的线程所持有的锁
只有在同一个锁上的被等待的线程,才可以被同一个锁的notify线程唤醒,而锁是任意对象、
所以可以任意对象调用的方法定义在Object中
---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<a
href="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! -------------------------------
---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<a
href="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! -------------------------------
一、基本概念:
线程:进程中一个独立的控制单元
多线程:一个程序中负责程序执行的单元(执行路径),一个程序有多个执行路径称多线程。
jvm启动至少两个线程在运行,一个main函数线程,一个Java垃圾回收机制线程。
二、线程创建:
创建线程的方式:1.继承Thread类,2实现Runable接口
1.定义继承 Thread 类
2.复写 Thread 类中的run()方法,自定义代码存在run()方法中,让线程运行
3.调用线程star()方法,作用:启动线程,调用run()方法
class Demo extends Thread
{
private String name;
Demo(String name){
super(name);
}
public void run(){ //自定义线程任务的函数
for(int i=0;i<10;i++){
System.out.println(name+"....i"=i+".....name="+Thread.currentThread().getName());
}
}
}
class ThreadDemo
{
public static void main(String args[]){
Demo d=new Demo();//创建好一个线程
Thread t1=new Thread(d);
Thread t2=new Thread(d);
t1.start();
t2.start();
// Demo d1=new Demo("小布");
// Demo d2=new Demo("小强");
//d1.start(); //开启线程并执行该线程run()方法
// d2.start(); //仅对象调用方法,线程创建了,并没有执行
d.run();
System.out.println("线程完成了"+Thread.currentThread().getName());
}
}
习题:
创建线程,和主线程进行交替
线程有名称,通过set/get
package com.study;
public class Test {
public static void main(String[] args) {
Thread t1=new Thread("one----");
Thread t2=new Thread("two+++++");
t1.start();
t2.start();
t1.run();
t2.run();
for(int x=0;x<60;x++){
System.out.println(Thread.currentThread().getName()+"run...."+x);//getName获取当前线程名称
}
}
}
class ThreadTest extends Thread{
private String name;
ThreadTest(String name){
this.name=name;
}
public void run(){
for(int x=0;x<60;x++){
System.out.print(this.name+"run...."+x);
}
}
}
线程状态:
1.新建:start()启动线程
2.运行:run();具备执行资格和执行权
3.冻结:wait()等待,notify()唤醒、sleep(time)睡眠
4.阻塞:线程有CPU执行资格,但是没有CPU执行权
5.消亡:stop
创建线程方式二: 实现Runable接口
1.定义类实现Runnable接口
2.覆盖Runnable接口中的run方法,将线程任务代码封装到run方法中。
3.通过 Thread 类创建对象,将 Runnable 接口中的子类对象作为 Thread 的构造参数进行传递,
线程的任务封装在Run()方法中,要让线程对象明确运行的run方法的所属对象。
4.调用Thread类中的Start()方法开启线程
实现Runable接口好处:
1.将线程任务从线程子类中分离出来,进行单独封装,按照面向对象的思想,将任务封装成对象。
2.避免Java单继承的局限性。
package com.study;
public class TickDemo {
/**
*多线程实现多个窗口卖票
*/
public static void main(String[] args) {
Ticket t=new Ticket(); //创建买票对象,使用其run方法
Thread t1=new Thread(t); //创建对个线程,传人t作为Thread 的构造参数进行传递实现对个窗口买票
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start(); //启动线程,调用Tick run方法
t2.start();
t3.start();
t4.start();
}
}
class Ticket implements Runnable{
private int tick=100;//票数
@Override
public void run() {
while(tick>0){
System.out.println(Thread.currentThread().getName()+"....sale:"+tick--);
}
}
}
三、线程安全问题:
1.多个线程同时操作共享的数据。
2.操作共享数据线程代码有多条。
当一个线程执行操作共享数据时,可能被另外一个线程也在进行访问操作。
(一个在执行修改操作,另一个删除的话就导致线程安全问题)
解决线程安全问题:
同步代码块:
将多条共享数据线程代码块封装起来,有线程访问时,其他线程停止操作不参与运算,当前线程执行完,其他才进行运算。
Java 专业解决方式:同步代码块
synchronized(对象) --对象锁,标志(火车卫生间)
{
需要同步的共享代码块
}
对象如同锁,持有锁的线程可以再同步中执行,没有锁的线程即使获得CPU执行权也无进不去。
同步好处:解决线程安全问题
同步的弊端:相对降低效率,同步的线程都会判断同步锁,消耗资源。
同步的前提:1.必须有多个线程(两个或两个以上)并使用同一个对像锁。
2.保证同步中只有一个线程运行
package com.study;
public class TickDemo {
/**
*多线程实现多个窗口卖票
*/
public static void main(String[] args) {
Ticket t=new Ticket(); //创建买票对象,使用其run方法
Thread t1=new Thread(t); //创建对个线程,传人t,实现对个窗口买票
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Ticket implements Runnable{
private int tick=1000;//票数
Object obj=new Object();
@Override
public void run() {
while(true){
synchronized (obj) { //线程同步对象锁,如线程t1执行是上锁,其它线程2、3、4无法进入线程中
if(tick>0){
try {
Thread.sleep(10);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println(Thread.currentThread().getName()+"....sale:"+tick--);
}
}
}
}
}
案例:银行存钱案例,同步函数更方便
package com.study;
public class BankDemo {
/**
* 银行储户,两个,每个都到银行存钱每次100元,共存了3次
*/
public static void main(String[] args) {
Cus c=new Cus();
Thread t1=new Thread(c); //创建线程对象
Thread t2=new Thread(c);
t1.start(); //启动线程
t2.start();
}
}
class Bank{
private int sum;//
//private Object obj=new Object();
public synchronized void add(int num){ //同步函数 ,存钱动作
sum=sum+num;
try {
Thread.sleep(10); //调用Thread.sleep() 方法
} catch (Exception ex) {
ex.printStackTrace();
}
System.out.println("总存了"+sum);
}
}
class Cus implements Runnable
{
private Bank b=new Bank();
@Override
public void run() {
for(int i=0;i<3;i++){
b.add(100);
}
}
}
同步函数:1.函数需要被对象调用,函数都有一个所属对象的引用,就是this
2.同步函数使用的对象锁是this
3 当同步函数被static 静态方法修饰,对象锁不再是this
原因:(重要)
静态进内存没有本类对象,但该类有字节码对象,不存在this对象,
类名.class 该对象类型是class,格式synchronized (对象名.class)。
验证:使用两个线程卖票
一个线程同步代码块
一个线程同步函数
都在执行卖票动作
package com.study;
public class TickDemo {
/**
*多线程实现多个窗口卖票
*/
public static void main(String[] args) {
Ticket t=new Ticket(); //创建买票对象,使用其run方法
Thread t1=new Thread(t); //创建对个线程,传人t,实现对个窗口买票
Thread t2=new Thread(t);
t1.start();
//t.flag=false; //主线程有执行权,瞬间执行完t2结束
try {
Thread.sleep(10);
} catch (Exception e) {
// TODO: handle exception
}
//t.flag=false;
t2.start();
}
}
class Ticket implements Runnable{
private int tick=100;//票数
boolean flag=true; //标记线程
Object obj=new Object();
@Override
public void run() {
if(flag){
while(true){
synchronized (this) { //执行同步代码块 (如果static修饰同步函数 对象为Ticket.class)
if(tick>0){
try {
Thread.sleep(10);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println(Thread.currentThread().getName()+"....code。。。。:"+tick--);
}
show();
}
}
}else{
while(true)
show();
}
}
public synchronized void show(){ //执行同步函数,this对象锁
if(tick>0){
try {
Thread.sleep(10);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println(Thread.currentThread().getName()+"....show。。。:"+tick--);
}
}
}
多线程的单例模式
单例模式:保证一个类在内存中的对象唯一性,一个类就一个对象实例。
步骤
1,私有化构造函数;
2,创建私有并静态的本类对象;
3,定义公有并静态的方法,返回该对象。
1.饿汉模式
类加载的时候就创建实例
class Single{
private static Single(){} //1.私有构造函数,防止外部创建实例对象
private static Single s=new Single();//2.创建静态私有本类对象
public static Single getInstance(){ //3.定义共有静态方法提供外部访问,返回该对象
return s
}
}
懒汉模式:
优点:实例延迟加载。
用于多线程,有安全问题,同步函数比较低效,使用同步代码块,多重判断提高效率
class Single
{
private Single(){}//私有构造函数,防止外部创建实例对象
private static Single s=null;//2.创建对象
public static Single getInstance(){ //3.提供方法
if(s==null){ //双重判断提高效率
synchronized(Single.class){ //静态对象锁不再是this,而是 类.class
if(s==null)
s=new Single();
}
return s;
}
}
class SingleDemo
{
public static void main(String [] args){
System.out.print("hell,world");
}
}
多线程同步锁:死锁
1.当两个线程相互调用 Thread.join ()
2.当两个线程使用嵌套的同步块,一个线程占用了另外一个线程必需的锁,互相等待时被阻塞就有可能出现死锁。
通俗讲:同步中嵌套同步,A锁嵌套B、B锁又嵌套A 互相僵持不释放锁 就死锁啦。
package com.itheima.thread.demo;
public class LockDemo {
/**
* @param args
*/
public static void main(String[] args) {
Thread t1=new Thread(new TestLock(true));
Thread t2=new Thread(new TestLock(false));
t1.start();
t2.start();
}
}
class TestLock implements Runnable{
private boolean flag; //定义一个标记
TestLock(boolean flag){
this.flag=flag;
}
@Override
public void run() {
if(flag){
while(true){
synchronized(MyLock.locka){
System.out.println("这是锁a");
synchronized(MyLock.lockb){
System.out.print("这是锁b");
}
}
}
}else{
while(true){
synchronized(MyLock.lockb){
System.out.println("这是锁b");
synchronized(MyLock.locka){
System.out.print("这是锁a");
}
}
}
}
}
}
class MyLock{
static Object locka=new Object();
static Object lockb=new Object();
}
线程间的通讯:线程间的通讯就是多个线程操作同一个资源,但是操作动作不同
package com.itheima.thread.demo;
public class Res {
/**
* @param 胡田
*/
String name;
String sex;
public static void main(String[] args) {
// 创建资源
Res r=new Res();
Input in=new Input(r);
Output out=new Output(r);
//执行路径
Thread t1=new Thread(in);
Thread t2=new Thread(out);
t1.start();
t2.start();
}
}
class Input implements Runnable{
//Res rs=new Res();
private Res r;
Input(Res r){ //初始化动作传人要操作的内容
this.r=r;
}
@Override
public void run() {
int x=0;
while(true){
synchronized(r) //对象锁
{
if(x==0){
r.name="小强";
r.sex="男";
}else{
r.name="小红";
r.sex="女";
}
x=(x+1)%2;
}
}
}
}
class Output implements Runnable{
//Res rs=new Res();
private Res r;
Output(Res r){ //初始化传人要操作的内容
this.r=r;
}
@Override
public void run() {
System.out.println(r.name+"........"+r.sex);
}
}
Lock接口:
Lock接口
Lock 替代了synchronized
Condition 替代了 Object监视器方法
好处:将同步synchronized 替换成了 Lock
将object中的wait notify notifyAll 替换成了 Condition对象
该对象可以Lock锁进行获取。一个锁可以对应多个Condition对象
注意:释放锁的工作一定要执行
。
示例代码
private Lock lock=new ReentrantLock();
private Condition condition =lock.newCondition();
public void cet(String name ) throws
{
lock.lock();
try
{
while(flag)
contidition.await();
this.name=name+"--"+count++;
sop(Thread.currentThread().getName()+"...生产者..."+this.name)
flag=true;
condition.signalAll();
}
finally
{
lock.unlock();
}
}
---多生产者多消费模式-----
注意:if 判断标记与notify()方法对应,只判断一次,会导致不改运行的线程运行起来
while 判断标记跟notifyAll()方法对应,解决获取执行权后是否要运行
package com.itheima.thread.demo;
public class ProductDemo {
/**
* @param args
*/
public static void main(String[] args) {
Resource res=new Resource();
Producter pd=new Producter(res);
Consumer cos=new Consumer(res);
Thread t1=new Thread(pd);
Thread t2=new Thread(cos);
t1.start();
t2.start();
}
}
class Resource{
private String name;
private int count=1;
private static boolean flag=false;
//生产
public synchronized void set(String name){
while(flag) //不用if,if判断标记与notify()方法对应,只判断一次,会导致不改运行的线程运行起来
try {
this.wait();
} catch (Exception e) {
// TODO: handle exception
}
this.name=name+"..."+count++; //烤鸭1、烤鸭2、烤鸭3
System.out.print(Thread.currentThread().getName()+"......生产者"+this.name);
flag=true;
notifyAll(); //唤醒所有等待的线程
}
//消费
public synchronized void Out (){
while(!flag) //标记while 每次notify 都会判断一次
try {
wait();
} catch (Exception e) {
// TODO: handle exception
}
System.out.print(Thread.currentThread().getName()+"......消费者"+this.name);
flag=false;
notifyAll();
}
}
class Producter implements Runnable{
private Resource res;
Producter(Resource res){
this.res=res;
}
@Override
public void run() {
while(true){
res.set("北京烤鸭");
}
}
}
class Consumer implements Runnable{
private Resource res;
Consumer(Resource res){
this.res=res;
}
@Override
public void run() {
while(true){
res.Out();
}
}
}
总结:
等待唤醒机制:
notify:唤醒线程池中的一个线程,本线程唤醒自己没有意义,while判断标记+notify 会导致线程死锁的。。
notifyAll:解决本线程线程一定会唤醒对方线程的问题。
wait():让线程处于冻结等待状态,wait的线程会被存储到线程池中。。。
sleep:线程进入睡眠状态,并不释放锁
notify、wait、notifyAll 要对持有监视器对象(锁)的操作,所以用在同步中synchnized 中
wait和sleep的区别:
1.wait可以指定时间也可以不指定,sleep必须指定睡眠时间,sleep(10)
2.同步时wait线程等待释放执行权,释放锁,交给别的线程执行
sleep 睡眠状态释放执行权,但并不释放锁。
为什么操作线程锁的方法在Object类中?
因为notify、wait、notifyAll 这些方法操作线程时,都必须标识他们所操作的线程所持有的锁
只有在同一个锁上的被等待的线程,才可以被同一个锁的notify线程唤醒,而锁是任意对象、
所以可以任意对象调用的方法定义在Object中
---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<a
href="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! -------------------------------