黑马程序员java学习日记六 线程的学习总结
2013-08-11 19:03
441 查看
线程的定义 ,概念
进程:是一个正在执行的程序.
每一个进行执行都有一个执行顺序,该顺序是一个执行路径,或叫一个控制单元.
线程:就是进程中的一个独立控制单元.
线程在控制着进行的执行.
一个进程中至少有一个线程.
java VM启动的时候会有一个进程叫java.exe.
该进程中至少有一个线程在负责java程序的执行.而且这个线程运行的代码存在于main方法中,该线程称之为主线程.
扩展:其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程.
如何在自定义代码中,自定义一个线程呢?
通过对api的查找,java已经提供了对线程这类事物的描述,就是Tread类
创建线程的第一种方式:继承Thread类.
步骤:
1.继承Thread类.
2.复写Thread类中的run方法.
目的:将自定义代码存储在run方法中,让线程运行.
3.创建该子类的对象,并调用线程的start方法.
该方法有两个作用:启动线程,调用run方法.
创建线程的第二种方式:实现Runnable接口
步骤:
1.定义类实现Runnable接口
2.覆盖Runnable接口中的run方法.
将线程要运行的代码存放在该run方法中.
3.通过Thread类建立线程对象.
4.将Runnable解救的子类对象作为实际参数传递给Thread类的构造函数.
为什么要将Runnable接口的子类对象传递给Thread的构造函数.
因为,自定义的run方法所属的对象是Runnable接口的子类对象.
所以要让线程去指定指定对象的run方法,就必须明确该run方法所属对象.
5.调用Thread类的start方法开启线程并调用Runnable接口子类的方法.
实现方式和继承方式有什么区别呢?
实现方式好处:避免了单继承的局限性.
在定义线程时,建议使用实现Runnable的方式.
两种方式的区别:
继承Thread:线程代码存放在Thread子类的run方法中.
实现Runnable:线程代码存放在接口子类的run方法中
发现运行结果每一次都不同.
因为多个线程都在获取cpu执行权,cpu执行到谁,谁就运行.
明确一点,在某一时刻,只能有一个程序在运行.(多核除外)
cpu在做着快速的切换,以达到看上去是同时运行的效果.
毕老师总结:
我们可以形象的把多线程的运行行为看做线程在互相抢夺cpu的执行权.
值就是多线程的一个特性:随机性.谁抢到谁执行,至于执行多长,cpu说的算.
为什么要覆盖run方法呢?
Thread类用于描述线程.
该类就定义了一个功能,用于存储线程要运行的代码.该存储功能就是run方法.
也就是Thread类中的run方法,是用于存储线程要运行的代码.
举例代码:
[java] view
plaincopy
class Demo extends Thread
{
public void run()
{
for (int x=0;x<60;x++)
{
System.out.println("Demo run--"+x);
}
}
}
class ThreadDemo
{
public static void main(String[] args)
{
Demo d = new Demo();
d.start();//开启线程,并执行该线程的run方法.
//d.run();//没调用线程,直接调用了run方法
for (int x=0;x<60;x++)
{
System.out.println("Hello World--"+x);
}
}
}
获取线程名称以及对象:
创建两个线程,和主线程交替运行.
原来线程都有自己的默认名称
Thread-编号,该编号从0开始.
Thread.currentThread():获取当前线程对象.
getName():获取线程名称.
设置线程名称:用setName或者构造函数.
局部变量在每一个线程中都有独立的内存空间.
[java] view
plaincopy
class DemoA extends Thread
{
//private String name;
DemoA(String name)
{
super(name);
//this.name=name;
}
public void run()
{
for (int x = 0;x<10;x++)
{
System.out.println((Thread.currentThread()==this)+this.getName()+"-run-"+x);
}
}
}
class ThreadTest
{
public static void main(String[] args)
{
new Thread().getName();
DemoA d1 = new DemoA("one---");
d1.start();
DemoA d2 = new DemoA("two+++");
d2.start();
for (int x = 0;x<10;x++)
{
System.out.println("three==="+x+x);
}
}
}
多窗口售票练习
[java] view
plaincopy
class Ticket implements Runnable
{
private int ticket = 20;
public void run()
{
while(ticket>0)
{
System.out.println(Thread.currentThread().getName()+"---"+ticket--);
}
}
}
class TicketDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();;
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
通过运行上面的多窗口售票练习出现了0,-1,-2等错票,该问题就引出了下面的知识点.
多线程的安全问题--同步代码块
多线程的运行出现了安全问题.
问题的原因:
当多条语句在操作同一个线程共享数据时.一个线程对多条语句只执行一部分,还没有执行完.
另一个线程参与进来执行.导致共享数据的错误.
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完.在执行过程中,其他线程不可以参与执行.
java对于多线程的安全问题提供了专业的解决方式.
就是同步代码块.
synchronized(对象(锁))
{
需要被同步的代码
}
对象如同锁,持有锁的线程可以再同步中执行.
没有持有锁的线程,即使获取了cpu的执行权,也进不去,因为没有锁.
毕老师说synchronized就如同火车上的卫生间---经典
同步的前提!!!!!
1.必须要有两个或两个以上的线程.
2.必须是多个线程使用同一个锁.
必须保证同步中只能有一个线程运行.
好处:解决了多线程的安全问题.
弊端:多个线程需要判断锁,较为消耗资源.
多窗口售票练习的修改版:
[java] view
plaincopy
class Ticket implements Runnable
{
private int ticket = 1000;
Object obj = new Object();
public void run()
{
while(true)
{
synchronized(obj)\\obj代表任意对象
{
if(ticket>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"---"+ticket--);
}
}
}
}
}
class TicketDemo1
{
public static void main(String[] args)
{
Ticket t = new Ticket();;
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
同步函数:同步代码块可以抽取为同步函数,以简化书写.
同步函数用的那一个锁呢?
函数需要被对象调用.函数都有一个所属对象的引用this
所以同步函数使用的锁是this.
通过该程序进行验证.
使用两个线程来卖票.
一个线程在同步代码块中.
一个线程在同步函数中.
都在执行卖票动作.
见下面代码:
[java] view
plaincopy
class Ticket implements Runnable
{
private int ticket = 200;
//Object obj = new Object();
boolean flag = true;
public void run()
{
if(flag)
{
while(true)
{
synchronized(this)
{
if(ticket>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"---code---"+ticket--);
}
}
}
}
else
while(true)
{
show();
}
}
public synchronized void show()
{
if(ticket>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"--show--"+ticket--);
}
}
}
class ThisLockDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();;
new Thread(t).start();
try{Thread.sleep(10);}catch(Exception e){}
t.flag = false;
new Thread(t).start();
}
}
如果同步函数被静态修饰后,使用的锁是什么呢?
通过验证,发现不再是this,因为静态方法中也不可以引用this.
静态进内存是,内存中没有本类对象,但是一定有该类对应的字节码文件对象.
类名.class,该了对象类型是Class.
静态的同步方法,使用的锁是该方法所在的字节码文件对象.类名.class
[java] view
plaincopy
class Ticket implements Runnable
{
private static int ticket = 200;
//Object obj = new Object();
boolean flag = true;
public void run()
{
if(flag)
{
while(true)
{
synchronized(Ticket.class)
{
if(ticket>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"---code---"+ticket--);
}
}
}
}
else
while(true)
{
show();
}
}
public static synchronized void show()
{
if(ticket>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"--show--"+ticket--);
}
}
}
class StaticLockDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();;
new Thread(t).start();
try{Thread.sleep(10);}catch(Exception e){}
t.flag = false;
new Thread(t).start();
}
}
同步的死锁问题:
死锁:2个或2个以上的进程在执行过程中,因为争夺资源而造成的一种互相等待的现象,若无外力作用,他们都无法推进下去,此时系统处于死锁状态,这些永远在互相等待的进程成为死锁进程。
产生原因:同步中嵌套同步,而锁不同;
线程之间的通信
其实就是多个线程在操作同一个资源,
但是操作的动作不同.
wait();
notify();
notifyAll();
都使用在synchronized同步中,因为要对持有监视器(锁)的线程操作.
所以要使用在同步中,因为只有同步才具有监视器(锁).
为什么这些操作线程的方法要定义在Object类中呢?
因为这些方法在操作同步中线程时,都必须要标识它们所操作的线程持有的锁.
只有同一个锁上的被等待线程,可以被同一个锁上的notify唤醒.
不可以对不同锁中的线程进行唤醒.
也就是说,等待和唤醒必须是同一个锁.
而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中.
举例代码:
[java] view
plaincopy
//输入输出类的共享资源
class Share
{
String name;
String sex;
boolean flag = false;
//供输入调用的方法
public synchronized void set(String name,String sex)
{
if (flag)
try{this.wait();}catch(Exception e){}
this.name = name;
this .sex = sex;
flag = true;
this.notify();
}
//供输出调用的方法
public synchronized void print()
{
if (!flag)
try{this.wait();}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+name+"----"+sex);
flag = false;
this.notify();
}
}
class InPut implements Runnable
{
Share s;
int x = 0;
InPut(Share s)
{
this.s = s;
}
public void run()
{
inMethod();
}
public void inMethod()
{
while (true)
{
if(x==0)
s.set("toby","man") ;
else
s.set("肖华","女女女女") ;
x = (x+1)%2;
}
}
}
class OutPut implements Runnable
{
Share s;
OutPut(Share s)
{
this.s = s;
}
public void run()
{
outMethod();
}
public void outMethod()
{
while (true)
{
s.print();
}
}
}
class DuoDemo
{
public static void main(String[] args)
{
Share s = new Share();
InPut i = new InPut(s);
OutPut o = new OutPut(s);
new Thread(i).start();
new Thread(o).start();
}
}
线程间通信--生产者消费者练习.
[java] view
plaincopy
//生产者和消费者的共享资源类
class Resource
{
private String name;
private int count = 1;
//定义一个boolean型的标记变量;
boolean flag = false;
public synchronized void set(String name)
{
while (flag)
try{wait();}catch(Exception e){}
this.name = name + count++;
System.out.println(Thread.currentThread().getName()+"----生产者----"+this.name);
flag = true;
this.notifyAll();
}
public synchronized void get()
{
while (!flag)
try{wait();}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"消费者"+this.name);
flag = false;
this.notifyAll();
}
}
class Pro implements Runnable
{
Resource r;
Pro(Resource r)
{
this.r = r;
}
public void run()
{
while (true)
r.set("商品");
}
}
class Conn implements Runnable
{
Resource r;
Conn(Resource r)
{
this.r = r;
}
public void run()
{
while (true)
r.get();
}
}
class ProConDemo1
{
public static void main(String[] args)
{
Resource s = new Resource();
new Thread(new Pro(s)).start();//0
new Thread(new Conn(s)).start();//1
new Thread(new Pro(s)).start();//2
new Thread(new Conn(s)).start();//3
}
}
线程间通信--生产者消费者练习jdk1.5升级版
[java] view
plaincopy
//导入jdk1.5版的lock类
import java.util.concurrent.locks.*;
//定义一个生产者消费者共享资源类
class Resource
{
private String name;
private int count = 1;
boolean flag = false;
//定义一个锁
Lock l = new ReentrantLock();
//定义2个锁的对象
Condition conset = l.newCondition();
Condition conout = l.newCondition();
public void set(String name)throws InterruptedException
{
l.lock ();//取代synchronized
try
{
if(flag)
conset.await();//取代wait
this.name = name + count++;
System.out.println(Thread.currentThread().getName()+"_____生产者____"+this.name);
flag = true;
conout.signal();//取代notify,唤醒调用out方法的线程
}
finally
{
l.unlock();//取代synchronized
}
}
public void out()throws InterruptedException
{
l.lock ();
try
{
if(!flag)
conout.await();
System.out.println(Thread.currentThread().getName()+"消费者"+this.name);
flag = false;
conset.signal();//唤醒调用set方法的线程
}
finally
{
l.unlock();
}
}
}
class Pro implements Runnable
{
Resource r;
Pro(Resource r)
{
this.r = r;
}
public void run()
{
while (true)
try
{
r.set("商品");
}
catch (InterruptedException i)
{
System.out.println("呵呵");
}
}
}
class Conn implements Runnable
{
Resource r;
Conn(Resource r)
{
this.r = r;
}
public void run()
{
while (true)
try
{
r.out();
}
catch (InterruptedException i)
{
System.out.println("呵呵");
}
}
}
class ProConDemo2
{
public static void main(String[] args)
{
Resource r = new Resource();
Pro p = new Pro(r);
Conn c = new Conn(r);
new Thread(p).start();
new Thread(p).start();
new Thread(c).start();
new Thread(c).start();
}
}
如何停止线程?
只有一种,run方法结束.
开启多线程运行,运行代码通常是循环结构.
只要控制住循环,就可以让run方法结束,也就是线程结束.
特殊情况:
当线程处于冻结状态.
就不会读取到标记.那么线程就不会结束.
当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除.
强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束.
Thread类中提供了该方法 interrupt();
举例代码:
[java] view
plaincopy
class StopThread implements Runnable
{
boolean flag = true;
public synchronized void run()
{
while(flag)
{
try
{
wait();
}
catch (InterruptedException i)
{
System.out.println(Thread.currentThread().getName()+"---Exception!");
flag = false;
}
System.out.println(Thread.currentThread().getName()+"---stop");
}
}
public void changeFlag()
{
flag = false;
}
}
class StopThreadDemo
{
public static void main(String[] args)
{
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.start();
t2.start();
int num = 0;
while (true)
{
if(num++ == 60)
{
//st.changeFlag();
t1.interrupt();
t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName()+"---"+num);
}
System.out.println(Thread.currentThread().getName()+"---over");
}
}
守护线程(后台线程)
Thread类中的setDaemon(boolean on)方法.
on如果为true,则将该线程标记为守护线程(后台线程).
将该线程标记为守护线程或用户线程.当正在运行的线程都是守护线程时,Java 虚拟机退出.
该方法必须在启动线程前调用.
这可能抛出 SecurityException(在当前线程中).
join方法
当A线程执行到了B线程的join()方法时,A线程就会等待.等B线程都执行完,A才会执行.
join可以用来临时加入线程执行.
举例代码:
[java] view
plaincopy
class Demo implements Runnable
{
public void run()
{
for(int x=0; x<70; x++)
{
System.out.println(Thread.currentThread().getName()+"....."+x);
}
}
}
class JoinDemo
{
public static void main(String[] args)throws Exception
{
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
t2.start();
t1.join();//等待t1终止
for (int x=0; x<80; x++ )
{
System.out.println("main....."+x);
}
System.out.println("over");
}
}
优先级和yield方法
yield()方法称为“退让”,它把运行机会让给了同等级的其他线程.
yield()只是使当前线程重新回到可执行状态,所有执行yield()的线程有可能在进入到
可执行状态后马上又被执行,所以yield()方法只能使同优先级的线程有执行的机会.
例子:
[java] view
plaincopy
class Demo implements Runnable
{
public void run()
{
for(int x=0; x<70; x++)
{
System.out.println(Thread.currentThread().toString()+"....."+x);
Thread.yield();//调用Thread的yield方法
}
}
}
class PriorityYieldDemo
{
public static void main(String[] args)throws Exception
{
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
//给t1线程命名为haha
t1.setName("haha");
//设置t1线程的优先级为最高级10,最低级为MIN_PRIORITY,中间为NORM_PRIORITY.
t1.setPriority(Thread.MAX_PRIORITY);
t2.start();
for (int x=0; x<80; x++ )
{
System.out.println("main....."+x);
}
System.out.println("over");
}
}
java之yield(),sleep(),join()区别详解
1、sleep()
使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁。也就是说如果有synchronized同步快,其他线程仍然不能访问共享数据。注意该方法要捕捉异常。
例如有两个线程同时执行(没有synchronized)一个线程优先级为MAX_PRIORITY,另一个为MIN_PRIORITY,如果没有Sleep()方法,只有高优先级的线程执行完毕后,低优先级的线程才能够执行;但是高优先级的线程sleep(500)后,低优先级就有机会执行了。
总之,sleep()可以使低优先级的线程得到执行的机会,当然也可以让同优先级、高优先级的线程有执行的机会。
!!Thread.sleep(long millis)必须带有一个时间参数。
2、join()
join()方法使调用该方法的线程在此之前执行完毕,也就是等待该方法的线程执行完毕后再往下继续执行。注意该方法也需要捕捉异常。
3、yield()
该方法与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会。
yield()方法称为“退让”,它把运行机会让给了同等级的其他线程。
yield()只是使当前线程重新回到可执行状态,所有执行yield()的线程有可能在进入到可执行状态后马上又被执行,所以yield()方法只能使同优先级的线程有执行的机会。
!!yield()没有参数.
多线程--线程的匿名内部类写法
[java] view
plaincopy
class ThreadTest
{
public static void main(String[] args)
{
new Thread()
{
public void run()
{
for (int x=0; x<30; x++)
{
System.out.println(Thread.currentThread().toString()+"..."+x);
}
}
}.start();
new Thread(new Runnable()
{
public void run()
{
for (int x=0; x<30; x++)
{
System.out.println(Thread.currentThread().toString()+"..."+x);
}
}
}).start();
for (int x=0; x<30; x++)
{
System.out.println(Thread.currentThread().toString()+"..."+x);
}
}
}
进程:是一个正在执行的程序.
每一个进行执行都有一个执行顺序,该顺序是一个执行路径,或叫一个控制单元.
线程:就是进程中的一个独立控制单元.
线程在控制着进行的执行.
一个进程中至少有一个线程.
java VM启动的时候会有一个进程叫java.exe.
该进程中至少有一个线程在负责java程序的执行.而且这个线程运行的代码存在于main方法中,该线程称之为主线程.
扩展:其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程.
如何在自定义代码中,自定义一个线程呢?
通过对api的查找,java已经提供了对线程这类事物的描述,就是Tread类
创建线程的第一种方式:继承Thread类.
步骤:
1.继承Thread类.
2.复写Thread类中的run方法.
目的:将自定义代码存储在run方法中,让线程运行.
3.创建该子类的对象,并调用线程的start方法.
该方法有两个作用:启动线程,调用run方法.
创建线程的第二种方式:实现Runnable接口
步骤:
1.定义类实现Runnable接口
2.覆盖Runnable接口中的run方法.
将线程要运行的代码存放在该run方法中.
3.通过Thread类建立线程对象.
4.将Runnable解救的子类对象作为实际参数传递给Thread类的构造函数.
为什么要将Runnable接口的子类对象传递给Thread的构造函数.
因为,自定义的run方法所属的对象是Runnable接口的子类对象.
所以要让线程去指定指定对象的run方法,就必须明确该run方法所属对象.
5.调用Thread类的start方法开启线程并调用Runnable接口子类的方法.
实现方式和继承方式有什么区别呢?
实现方式好处:避免了单继承的局限性.
在定义线程时,建议使用实现Runnable的方式.
两种方式的区别:
继承Thread:线程代码存放在Thread子类的run方法中.
实现Runnable:线程代码存放在接口子类的run方法中
发现运行结果每一次都不同.
因为多个线程都在获取cpu执行权,cpu执行到谁,谁就运行.
明确一点,在某一时刻,只能有一个程序在运行.(多核除外)
cpu在做着快速的切换,以达到看上去是同时运行的效果.
毕老师总结:
我们可以形象的把多线程的运行行为看做线程在互相抢夺cpu的执行权.
值就是多线程的一个特性:随机性.谁抢到谁执行,至于执行多长,cpu说的算.
为什么要覆盖run方法呢?
Thread类用于描述线程.
该类就定义了一个功能,用于存储线程要运行的代码.该存储功能就是run方法.
也就是Thread类中的run方法,是用于存储线程要运行的代码.
举例代码:
[java] view
plaincopy
class Demo extends Thread
{
public void run()
{
for (int x=0;x<60;x++)
{
System.out.println("Demo run--"+x);
}
}
}
class ThreadDemo
{
public static void main(String[] args)
{
Demo d = new Demo();
d.start();//开启线程,并执行该线程的run方法.
//d.run();//没调用线程,直接调用了run方法
for (int x=0;x<60;x++)
{
System.out.println("Hello World--"+x);
}
}
}
获取线程名称以及对象:
创建两个线程,和主线程交替运行.
原来线程都有自己的默认名称
Thread-编号,该编号从0开始.
Thread.currentThread():获取当前线程对象.
getName():获取线程名称.
设置线程名称:用setName或者构造函数.
局部变量在每一个线程中都有独立的内存空间.
[java] view
plaincopy
class DemoA extends Thread
{
//private String name;
DemoA(String name)
{
super(name);
//this.name=name;
}
public void run()
{
for (int x = 0;x<10;x++)
{
System.out.println((Thread.currentThread()==this)+this.getName()+"-run-"+x);
}
}
}
class ThreadTest
{
public static void main(String[] args)
{
new Thread().getName();
DemoA d1 = new DemoA("one---");
d1.start();
DemoA d2 = new DemoA("two+++");
d2.start();
for (int x = 0;x<10;x++)
{
System.out.println("three==="+x+x);
}
}
}
多窗口售票练习
[java] view
plaincopy
class Ticket implements Runnable
{
private int ticket = 20;
public void run()
{
while(ticket>0)
{
System.out.println(Thread.currentThread().getName()+"---"+ticket--);
}
}
}
class TicketDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();;
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
通过运行上面的多窗口售票练习出现了0,-1,-2等错票,该问题就引出了下面的知识点.
多线程的安全问题--同步代码块
多线程的运行出现了安全问题.
问题的原因:
当多条语句在操作同一个线程共享数据时.一个线程对多条语句只执行一部分,还没有执行完.
另一个线程参与进来执行.导致共享数据的错误.
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完.在执行过程中,其他线程不可以参与执行.
java对于多线程的安全问题提供了专业的解决方式.
就是同步代码块.
synchronized(对象(锁))
{
需要被同步的代码
}
对象如同锁,持有锁的线程可以再同步中执行.
没有持有锁的线程,即使获取了cpu的执行权,也进不去,因为没有锁.
毕老师说synchronized就如同火车上的卫生间---经典
同步的前提!!!!!
1.必须要有两个或两个以上的线程.
2.必须是多个线程使用同一个锁.
必须保证同步中只能有一个线程运行.
好处:解决了多线程的安全问题.
弊端:多个线程需要判断锁,较为消耗资源.
多窗口售票练习的修改版:
[java] view
plaincopy
class Ticket implements Runnable
{
private int ticket = 1000;
Object obj = new Object();
public void run()
{
while(true)
{
synchronized(obj)\\obj代表任意对象
{
if(ticket>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"---"+ticket--);
}
}
}
}
}
class TicketDemo1
{
public static void main(String[] args)
{
Ticket t = new Ticket();;
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
同步函数:同步代码块可以抽取为同步函数,以简化书写.
同步函数用的那一个锁呢?
函数需要被对象调用.函数都有一个所属对象的引用this
所以同步函数使用的锁是this.
通过该程序进行验证.
使用两个线程来卖票.
一个线程在同步代码块中.
一个线程在同步函数中.
都在执行卖票动作.
见下面代码:
[java] view
plaincopy
class Ticket implements Runnable
{
private int ticket = 200;
//Object obj = new Object();
boolean flag = true;
public void run()
{
if(flag)
{
while(true)
{
synchronized(this)
{
if(ticket>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"---code---"+ticket--);
}
}
}
}
else
while(true)
{
show();
}
}
public synchronized void show()
{
if(ticket>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"--show--"+ticket--);
}
}
}
class ThisLockDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();;
new Thread(t).start();
try{Thread.sleep(10);}catch(Exception e){}
t.flag = false;
new Thread(t).start();
}
}
如果同步函数被静态修饰后,使用的锁是什么呢?
通过验证,发现不再是this,因为静态方法中也不可以引用this.
静态进内存是,内存中没有本类对象,但是一定有该类对应的字节码文件对象.
类名.class,该了对象类型是Class.
静态的同步方法,使用的锁是该方法所在的字节码文件对象.类名.class
[java] view
plaincopy
class Ticket implements Runnable
{
private static int ticket = 200;
//Object obj = new Object();
boolean flag = true;
public void run()
{
if(flag)
{
while(true)
{
synchronized(Ticket.class)
{
if(ticket>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"---code---"+ticket--);
}
}
}
}
else
while(true)
{
show();
}
}
public static synchronized void show()
{
if(ticket>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"--show--"+ticket--);
}
}
}
class StaticLockDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();;
new Thread(t).start();
try{Thread.sleep(10);}catch(Exception e){}
t.flag = false;
new Thread(t).start();
}
}
同步的死锁问题:
死锁:2个或2个以上的进程在执行过程中,因为争夺资源而造成的一种互相等待的现象,若无外力作用,他们都无法推进下去,此时系统处于死锁状态,这些永远在互相等待的进程成为死锁进程。
产生原因:同步中嵌套同步,而锁不同;
线程之间的通信
其实就是多个线程在操作同一个资源,
但是操作的动作不同.
wait();
notify();
notifyAll();
都使用在synchronized同步中,因为要对持有监视器(锁)的线程操作.
所以要使用在同步中,因为只有同步才具有监视器(锁).
为什么这些操作线程的方法要定义在Object类中呢?
因为这些方法在操作同步中线程时,都必须要标识它们所操作的线程持有的锁.
只有同一个锁上的被等待线程,可以被同一个锁上的notify唤醒.
不可以对不同锁中的线程进行唤醒.
也就是说,等待和唤醒必须是同一个锁.
而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中.
举例代码:
[java] view
plaincopy
//输入输出类的共享资源
class Share
{
String name;
String sex;
boolean flag = false;
//供输入调用的方法
public synchronized void set(String name,String sex)
{
if (flag)
try{this.wait();}catch(Exception e){}
this.name = name;
this .sex = sex;
flag = true;
this.notify();
}
//供输出调用的方法
public synchronized void print()
{
if (!flag)
try{this.wait();}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+name+"----"+sex);
flag = false;
this.notify();
}
}
class InPut implements Runnable
{
Share s;
int x = 0;
InPut(Share s)
{
this.s = s;
}
public void run()
{
inMethod();
}
public void inMethod()
{
while (true)
{
if(x==0)
s.set("toby","man") ;
else
s.set("肖华","女女女女") ;
x = (x+1)%2;
}
}
}
class OutPut implements Runnable
{
Share s;
OutPut(Share s)
{
this.s = s;
}
public void run()
{
outMethod();
}
public void outMethod()
{
while (true)
{
s.print();
}
}
}
class DuoDemo
{
public static void main(String[] args)
{
Share s = new Share();
InPut i = new InPut(s);
OutPut o = new OutPut(s);
new Thread(i).start();
new Thread(o).start();
}
}
线程间通信--生产者消费者练习.
[java] view
plaincopy
//生产者和消费者的共享资源类
class Resource
{
private String name;
private int count = 1;
//定义一个boolean型的标记变量;
boolean flag = false;
public synchronized void set(String name)
{
while (flag)
try{wait();}catch(Exception e){}
this.name = name + count++;
System.out.println(Thread.currentThread().getName()+"----生产者----"+this.name);
flag = true;
this.notifyAll();
}
public synchronized void get()
{
while (!flag)
try{wait();}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"消费者"+this.name);
flag = false;
this.notifyAll();
}
}
class Pro implements Runnable
{
Resource r;
Pro(Resource r)
{
this.r = r;
}
public void run()
{
while (true)
r.set("商品");
}
}
class Conn implements Runnable
{
Resource r;
Conn(Resource r)
{
this.r = r;
}
public void run()
{
while (true)
r.get();
}
}
class ProConDemo1
{
public static void main(String[] args)
{
Resource s = new Resource();
new Thread(new Pro(s)).start();//0
new Thread(new Conn(s)).start();//1
new Thread(new Pro(s)).start();//2
new Thread(new Conn(s)).start();//3
}
}
线程间通信--生产者消费者练习jdk1.5升级版
[java] view
plaincopy
//导入jdk1.5版的lock类
import java.util.concurrent.locks.*;
//定义一个生产者消费者共享资源类
class Resource
{
private String name;
private int count = 1;
boolean flag = false;
//定义一个锁
Lock l = new ReentrantLock();
//定义2个锁的对象
Condition conset = l.newCondition();
Condition conout = l.newCondition();
public void set(String name)throws InterruptedException
{
l.lock ();//取代synchronized
try
{
if(flag)
conset.await();//取代wait
this.name = name + count++;
System.out.println(Thread.currentThread().getName()+"_____生产者____"+this.name);
flag = true;
conout.signal();//取代notify,唤醒调用out方法的线程
}
finally
{
l.unlock();//取代synchronized
}
}
public void out()throws InterruptedException
{
l.lock ();
try
{
if(!flag)
conout.await();
System.out.println(Thread.currentThread().getName()+"消费者"+this.name);
flag = false;
conset.signal();//唤醒调用set方法的线程
}
finally
{
l.unlock();
}
}
}
class Pro implements Runnable
{
Resource r;
Pro(Resource r)
{
this.r = r;
}
public void run()
{
while (true)
try
{
r.set("商品");
}
catch (InterruptedException i)
{
System.out.println("呵呵");
}
}
}
class Conn implements Runnable
{
Resource r;
Conn(Resource r)
{
this.r = r;
}
public void run()
{
while (true)
try
{
r.out();
}
catch (InterruptedException i)
{
System.out.println("呵呵");
}
}
}
class ProConDemo2
{
public static void main(String[] args)
{
Resource r = new Resource();
Pro p = new Pro(r);
Conn c = new Conn(r);
new Thread(p).start();
new Thread(p).start();
new Thread(c).start();
new Thread(c).start();
}
}
如何停止线程?
只有一种,run方法结束.
开启多线程运行,运行代码通常是循环结构.
只要控制住循环,就可以让run方法结束,也就是线程结束.
特殊情况:
当线程处于冻结状态.
就不会读取到标记.那么线程就不会结束.
当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除.
强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束.
Thread类中提供了该方法 interrupt();
举例代码:
[java] view
plaincopy
class StopThread implements Runnable
{
boolean flag = true;
public synchronized void run()
{
while(flag)
{
try
{
wait();
}
catch (InterruptedException i)
{
System.out.println(Thread.currentThread().getName()+"---Exception!");
flag = false;
}
System.out.println(Thread.currentThread().getName()+"---stop");
}
}
public void changeFlag()
{
flag = false;
}
}
class StopThreadDemo
{
public static void main(String[] args)
{
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.start();
t2.start();
int num = 0;
while (true)
{
if(num++ == 60)
{
//st.changeFlag();
t1.interrupt();
t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName()+"---"+num);
}
System.out.println(Thread.currentThread().getName()+"---over");
}
}
守护线程(后台线程)
Thread类中的setDaemon(boolean on)方法.
on如果为true,则将该线程标记为守护线程(后台线程).
将该线程标记为守护线程或用户线程.当正在运行的线程都是守护线程时,Java 虚拟机退出.
该方法必须在启动线程前调用.
这可能抛出 SecurityException(在当前线程中).
join方法
当A线程执行到了B线程的join()方法时,A线程就会等待.等B线程都执行完,A才会执行.
join可以用来临时加入线程执行.
举例代码:
[java] view
plaincopy
class Demo implements Runnable
{
public void run()
{
for(int x=0; x<70; x++)
{
System.out.println(Thread.currentThread().getName()+"....."+x);
}
}
}
class JoinDemo
{
public static void main(String[] args)throws Exception
{
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
t2.start();
t1.join();//等待t1终止
for (int x=0; x<80; x++ )
{
System.out.println("main....."+x);
}
System.out.println("over");
}
}
优先级和yield方法
yield()方法称为“退让”,它把运行机会让给了同等级的其他线程.
yield()只是使当前线程重新回到可执行状态,所有执行yield()的线程有可能在进入到
可执行状态后马上又被执行,所以yield()方法只能使同优先级的线程有执行的机会.
例子:
[java] view
plaincopy
class Demo implements Runnable
{
public void run()
{
for(int x=0; x<70; x++)
{
System.out.println(Thread.currentThread().toString()+"....."+x);
Thread.yield();//调用Thread的yield方法
}
}
}
class PriorityYieldDemo
{
public static void main(String[] args)throws Exception
{
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
//给t1线程命名为haha
t1.setName("haha");
//设置t1线程的优先级为最高级10,最低级为MIN_PRIORITY,中间为NORM_PRIORITY.
t1.setPriority(Thread.MAX_PRIORITY);
t2.start();
for (int x=0; x<80; x++ )
{
System.out.println("main....."+x);
}
System.out.println("over");
}
}
java之yield(),sleep(),join()区别详解
1、sleep()
使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁。也就是说如果有synchronized同步快,其他线程仍然不能访问共享数据。注意该方法要捕捉异常。
例如有两个线程同时执行(没有synchronized)一个线程优先级为MAX_PRIORITY,另一个为MIN_PRIORITY,如果没有Sleep()方法,只有高优先级的线程执行完毕后,低优先级的线程才能够执行;但是高优先级的线程sleep(500)后,低优先级就有机会执行了。
总之,sleep()可以使低优先级的线程得到执行的机会,当然也可以让同优先级、高优先级的线程有执行的机会。
!!Thread.sleep(long millis)必须带有一个时间参数。
2、join()
join()方法使调用该方法的线程在此之前执行完毕,也就是等待该方法的线程执行完毕后再往下继续执行。注意该方法也需要捕捉异常。
3、yield()
该方法与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会。
yield()方法称为“退让”,它把运行机会让给了同等级的其他线程。
yield()只是使当前线程重新回到可执行状态,所有执行yield()的线程有可能在进入到可执行状态后马上又被执行,所以yield()方法只能使同优先级的线程有执行的机会。
!!yield()没有参数.
多线程--线程的匿名内部类写法
[java] view
plaincopy
class ThreadTest
{
public static void main(String[] args)
{
new Thread()
{
public void run()
{
for (int x=0; x<30; x++)
{
System.out.println(Thread.currentThread().toString()+"..."+x);
}
}
}.start();
new Thread(new Runnable()
{
public void run()
{
for (int x=0; x<30; x++)
{
System.out.println(Thread.currentThread().toString()+"..."+x);
}
}
}).start();
for (int x=0; x<30; x++)
{
System.out.println(Thread.currentThread().toString()+"..."+x);
}
}
}
相关文章推荐
- 黑马程序员---java线程Timer学习与总结
- 黑马程序员-关于线程学习的若干总结
- 黑马程序员-----java线程学习与总结
- 黑马程序员——总结下线程学习中的wait(),notify()的用法。
- 黑马程序员-----学习日记<3>------基础总结2
- 黑马程序员:Java基础总结----线程间通信
- 黑马程序员——反射学习总结(上)
- 黑马程序员学习log第六篇基础知识:JAVA的面向对象之IO总结
- 【黑马程序员】关于线程的学习
- 黑马程序员 (高新技术) 动态代理学习总结
- 黑马程序员之IOS学习总结——02C语言的流程控制之选择结构
- unix c学习总结--线程部分(apue)
- java基础学习总结-----线程(一)
- 黑马程序员_多线程的线程间通信学习笔记
- Java学习笔记—多线程(Java线程常用方法总结)
- 黑马程序员--Java常用正则表达式学习及总结
- 黑马程序员之ios学习总结——09 OC语言的点语法、@property和@synthesize
- 黑马程序员 日记六:集合的学习总结
- 黑马程序员——C语言基础学习(四)---数组和指针的总结学习
- 黑马程序员_学习笔记1泛型及动态代理类的总结