您的位置:首页 > 职场人生

黑马程序员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);

}

}

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