[置顶] 黑马程序员——Java基础---多线程

时间:2021-03-06 00:42:58

                                                                                  ------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

                                                                                                          Java基础---多线程

一、进程

是一个正在执行中的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。

线程:就是进程中的一个独立的控制单元。线程在控制着进程的执行。 一个进程中至少有一个线程。

 java虚拟机启动的时候会有一个进程java.exe。而且这个线程运行的代码存在于main方法中,该线程称为主程。

扩展:其实更细节说明JVM,JVM启动不止一个线程,还有负责垃圾回收机制的线程。

注意:还有一个状态(临时状态) 阻塞 具备执行资格,但没有执行权,可能cpu在执行别的线程。当冻结的线程被唤醒了他最初会到阻塞状态,再判断CPU是否空闲,空闲时到运行状态 。

                             [置顶]        黑马程序员——Java基础---多线程

(线程运行状态) 

                 [置顶]        黑马程序员——Java基础---多线程

二、多线程的创建方式

         Thread线程是程序中的执行线程,如何在自定义代码中,自定义一个线程呢?

通过对API的查找,java已经提供了对线程这类事物的描述,就是Thread类。

        创建线程的第一种方式:继承Thread类

        步骤:(1)定义类继承Thread

                   (2)复写Thread类中的run方法。

       目的:将自定义代码存储在run方法中,让线程运行。

                    (3) 调用线程的start方法,该方法有两个作用:启动线程,调用run方法。

    (当然,如果你想直接调用run方法也是可以的,但是那就不是多线程了)

//代码示例:
class ThreadTest extends Thread
{
public void run()
{
线程运行代码;
}
}
class Test
{
public static void main(String[] args)
{
ThreadTest threadTest = new ThreadTest();
thread.start();
}
}

//简化格式: 
class Test
{
public static void main(String[] args)
{
new Thread()
{
public void run()
{
线程运行代码;
}
}.start();
}
}

创建线程的第二种方式:实现Runnable接口

步骤:(1)定义类实现Runnable接口

      (2)覆盖Runnable接口中的run方法。将线程要运行的代码存放在该run方法中。

      (3)通过Thread类建立线程对象。

      (4)将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。

    (5)调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。 

//代码示例: 
class RunnableTest implements Runnable
{
public void run()
{
线程运行代码;
}
}
class Test
{
public static void main(String[] args)
{
RunnableTest runnableTest = new RunnableTest();
Thread thread = new Thread(runnableTest);
thread.start();
/*
简化到一步到为,new Thread(new Runnable()).start();
*/
}
}

//简化格式: 
class Test
{
public static void main(String[] args)
{
new Thread(new Runnable()
{
public void run()
{
线程运行代码;
}
}).start();
}
}
为什么要将Runnable接口的子类对象传递给Thread的构造函数。 
因为,自定义的run方法所属的对象是Runnable接口的子接口对象。 所以要让线程去指定指定对象的run方法。就必须明确该run方法所属对象。 
实现方式和继承方式有什么区别:
(1)、实现方式好处:避免了单继承的局限性 
(2)、在定义线程时:建议使用实现方式
(3)、两种方式区别:
      1.继承Thread:线程代码存放在 Thread 子类 run 方法中 
      2.实现Runnable:线程代码存放在接口的子类的 run 方法中 
     总结建议:建议使用第二种线程创建方法。因为第二种方式更加体现面向对象思想

   多线程练习题

/*继承Thread类*/ 
class ThreadTest extends Thread
{
ThreadTest(){}
ThreadTest(String ThreadName)
{
//把传递进行的线程名字赋值给父类的线程名字
super(ThreadName);
}
/*复写Thread类中的run方法*/
public void run()
{
/*把代码封装在run方法里面*/
while(true)
{
/*打印下当前线程的名字*/
System.out.println(Thread.currentThread().getName()+"...run");
}
}
}
class Test
{
public static void main(String[] args)
{
/*创建了一个线程对象*/
ThreadTest t = new ThreadTest ();
t.start();/*开启一个线程,并调用线程的run方法*/
//t.run();可以吗?可以的,只不过没有开启线程哦
/*注意:主线程也是一个线程哦。我们把代码定义在main方法中就等于又开启了一个线程哦*/
t.setName("ThreadTest");
while(true)
{
/*打印下当前线程的名字*/
System.out.println(Thread.currentThread().getName()+"...run");
//Thread-0...run
}
}
}

三、 多线程的特性

       发现运行结果每次都不一样,因为多个线程都获取CPU的执行权,CPU执行到谁,谁就运行,明确一点,在某一

个时刻,只能有一个程序在运行(多核除外)CPU在做着快速的切换,以达到看上去是同时运行的效果。我们可以形

象的把多线程的运行形容为在互相抢夺CPU的执行权,这就是多线程的一个特性:随机性,谁抢到谁知晓,至于执行

多长,CPU说了算。

      为什么要覆盖run方法?

      Thread类用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法,也就是

所Thread类中的run方法,用于存储线程要运行的代码。

<span style="font-size:18px;">class Demo extends Thread
{
public void run()
{
for(int x=0;x<60;x++)
System.out.println("demo run--"+x);
}
}</span>
<span style="font-size:18px;">class  ThreadDemo{public static void main(String[] args) {//System.out.println("Hello World!");Demo d=new Demo();//创建好一个线程d.start();//开启线程并执行该线程的run方法//d.run();仅仅是对象调用方法,而线程创建了,并没有运行。for(int x=0;x<60;x++)System.out.println("Hello World!--"+x);}}</span>

四、练习

创建两个线程,和主线程交替运行

<span style="font-size:18px;"><span style="font-size:18px;">class Test extends Thread
{
private String name;
Test(String name)
{
this.name=name;
}
public void run()
{
for(int x=0; x<60; x++)
System.out.println(name+"run!--"+x);
}
}</span></span>
<span style="font-size:18px;"><span style="font-size:18px;">class  ThreadDemo{public static void main(String[] args) {Test t1 = new Test("one");Test t2 = new Test("two");t1.start();t2.start();for(int x=0; x<60; x++){System.out.println("main!--"+x);}}}</span></span>


原来线程都有自己默认的名称:Thread-编号,该编号从0开始。


<span style="font-size:18px;">class Test extends Thread
{
private String name;
Test(String name)
{
this.name=name;
}
public void run()
{
System.out.println(this.getName());
}
}</span>
<span style="font-size:18px;">class  ThreadDemo{public static void main(String[] args) {Test t1 = new Test("one");Test t2 = new Test("two");t1.start();t2.start();}}</span>

设置线程名称

<span style="font-size:18px;">class Test extends Thread
{
Test(String name)
{
super(name);
}
public void run()
{
System.out.println(this.getName());
}
}</span>
<span style="font-size:18px;">class  ThreadDemo{public static void main(String[] args) {Test t = new Test();t.start();}}</span>

返回正在执行的线程:static Thread currentThread();获取当前线程对象

getName();获取线程名称

设置线程名称:setName()或者构造函数。

<span style="font-size:18px;">class Test extends Thread
{
Test(String name)
{
super(name);
}
public void run()
{
System.out.println(Thread.currentThread().getName());
}
}</span>
<span style="font-size:18px;">class  ThreadDemo{public static void main(String[] args) {Test t = new Test();t.start();}}</span>

五、简单的卖票程序

需求:
简单的卖票程序,多个窗口卖票。 
思路:
票是一个共享数据被多个窗口所操作。 
窗口是一个线程,多个窗口应该是一个多线程。 
步骤:
票,是共享数据,应该被static修饰起来。 
多窗口,是多线程,应该创建多个线程来操作票。 

class Ticket extends Thread
{
/*把线程创建传递进来的名字丢给父类带参数的构造函数*/
Ticket(StringThreadName)
{
super(ThreadName);
}
/*定义票数据,定义为共享数据,因为多个窗口卖的是同一个票资源*/
private static int tick =100;
public void run()
{
while(true)
{
/*如果还有票就打印下几号客户在买票,然后卖出去一张票,减一张票*/
if(tick>0)
{
System.out.println(tick+"号客户买票\n"+Thread.currentThread().getName()+"为"+tick+"号客户服务\t...卖第"+tick--+"票");
}
/*如果没有票了,就向客户致敬,然后跳出return跳出循环,方法结束*/
else
{
System.out.println(Thread.currentThread().getName()+":哥们票卖完了,打烊了");
return;
}
}
}
}
class Test
{
public static void main(String[] args)
{
new Ticket("1号窗口").start();//创建匿名线程对象,调用对象的start()方法。
new Ticket("2号窗口").start();
new Ticket("3号窗口").start();
new Ticket("4号窗口").start();
}
}

需求: 
简单的卖票程序,多个窗口卖票。 
思路: 
票是一个共享数据被多个窗口所操作。 
窗口是一个线程,多个窗口应该是一个多线程。 
步骤: 
票,是共享数据,应该被static修饰起来。 
多窗口,是多线程,应该创建多个线程来操作票。 

创建线程的第二种方式:实现Runnable接口

  步骤:

(1),定义类实现Runnable 接口

(2),覆盖Runnable接口中的run方法。将线程要运行的代码存放在该run方法中

(3),通过Thread类建立线程对象

(4),将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数

为什么要将Runnable接口的子类对象传递给Thread的构造函数

因为自定义的run方法所属的对象是Runnable接口的子类对象,所以要让线程去指定对象的run方法。就必须明确run方法所属的对象。

 (5),调用Thread类的start方法开启线程并调用Runnable接口的run方法。

<span style="font-size:18px;">class Ticket implements Runnable
{
/*private Object obj = new Object();*/
/*定义票数据,因为创建的是一个对象资源,所以保证了数据的共享,不用static也可以*/
private int tick =100;
public void run()
{
while(true)
{
/*如果还有票就打印下几号客户在买票,然后卖出去一张票,减一张票*/
synchronized(this)
{
if(tick>0)
{
try
{
Thread.sleep(10);
/*由于考虑到如果客户有可能操作慢而导致延迟,所以加入线程延迟进行测试,我们是程序员必须要做到一些错误的排除,当然不能保证到万无一失,但也要做到尽量避免发生错误*/
}
catch(Exception e)
{ }
System.out.println(tick+"号客户买票\n"+Thread.currentThread().getName()+"为"+tick+"号客户服务\t...卖第"+tick--+"票");
}
/*如果没有票了,就向客户致敬,然后跳出return跳出循环,方法结束*/
else
{
System.out.println(Thread.currentThread().getName()+":哥们票卖完了,打烊了");
return;
}
}
}
}
}
class Test
{
public static void main(String[] args)
{
/*
这时要设置线程的名字的话,就要用Thread类的方法了,因为Runnable是
父接口,而设置线程的方法是在Thread类中的,所以可以直接用Thread对象调用。
*/
Ticket t = new Ticket();
Thread t1 = new Thread (t);
Thread t2 = new Thread (t);
Thread t3 = new Thread (t);
Thread t4 = new Thread (t);
t1.setName("1号窗口");
t2.setName("2号窗口");
t3.setName("3号窗口");
t4.setName("4号窗口");
t1.start();
t2.start();
t3.start();
t4.start();
/*创建线程对象,把资源当成参数进行传递(保证了资源的唯一),调用Thread类的start()方法。*/
}
}
</span>


六、实现方式和继承方式的区别:

实现好处:避免了单继承的局限性,在定义线程时,建议使用实现方式。

继承Thread:线程代码存放在Thread子类run方法中,

实现Runnable:线程代码存放在接口的子类run方法中

<span style="font-size:18px;">class Ticket implements Runnable//extends Thread
{
private int tick=100;
public void run()
{
while(true)
{
if(tick>0)
{
try
{
Thread.sleep(10);
}
catch(Exception e)
{

}
System.out.println(Thread.currentThread().getName()+"sale:"+tick--);
}
}

}
}</span>

通过分析发现,打印出0,-1,-2,等错票。

多线程的运行出现安全问题 .

问题的原因:

    当多条语句在操作同一个 线程共享数据时,一个线程对多条语句只执行了一部分,还没执行完,另一个线程参与执行,导致共享数据的错误。

解决办法:对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行

Java对于多线程的安全问题提供了专业的解决方式。就是同步代码块。

synchronized(对象)

{
需要同步的代码; 

}

对象如同锁,持有锁的线程可以在同步中执行没有持有锁的线程即使获取CPU的执行权, 也进不去, 因为没 有获取锁。

同步的前提:

(1),必须要有两个及以上的线程。

(2),必须是多个线程使用同一个锁。
必须保证同步中只能有一个线程在执行。

好处:解决了多线程的安全问题

弊端:多个线程都需要判断锁,较为消耗资源,

<span style="font-size:18px;">class Ticket implements Runnable//extends Thread
{
private int tick=100;
Object obj = new Object;
public void run()
{
while(true)
{
synchronized(obj)
{
if(tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"sale:"+tick--);
}
}
}
}
}</span>


七、需求:

银行有一个金库,有两个储户分别存300元,每次存100,存3次。

目的:该程序是否有安全问题,若果有,怎么解决?
如何找问题:

(1),哪些代码是多线程运行代码

(2),明确共享数据

(3),明确多线程运行代码中哪些语句是操作共享数据的

<span style="font-size:18px;">class Bank
{
private int sum;
Object obj = new Object();
public void add(int n)
{
synchronized(obj)
{
sum+=n;
try
{
Thread.sleep(10);
}
catch (Exception e)
{
}
System.out.println("sum="+sum);
}

}
}</span>
<span style="font-size:18px;">class Cus implements Runnable{private Bank b = new Bank();public void run(){for(int x=0; x<3; x++){b.add(100);}}}</span>
<span style="font-size:18px;">class BankDemo{public static void main(String [] ars){Cus c = new Cus();Thread t1 = new Thread(c);Thread t1 = new Thread(c);t1.start();t2.start();}}</span>

同步函数

<span style="font-size:18px;">class Bank
{
private int sum;
public synchronized void add(int n)
}
sum+=n;
try
{
Thread.sleep(10);
}
catch (Exception e)
{
}
System.out.println("sum="+sum);
}</span>
<span style="font-size:18px;">}</span>

同步函数:用的是哪一个锁呢?

函数需要被对象调用,那么函数都有一个所属对象引用,就是this。所以同步函数使用的锁是this。

使用两个线程来卖票 一个线程在同步代码块中,另一个线程在同步函数中,都在执行卖票动作。

<span style="font-size:18px;">class Ticket implements Runnable
{
private int tick=100;
Object obj = new Object;
boolean flag=true;
public void run()
{
if(flag)
{
while(true)
{
synchronized(obj)
{
if(tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"---code"+tick--);
}
}
}
else
while(true)
show();
}
public synchronized void show()
{
if(tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"---show---"+tick--);
}
}
}</span>
<span style="font-size:18px;">class TicketDemo{public static void main(String [] args){Ticket t = new Ticket();Thread t1 = new Thread(t);//创建了一个线程Thread t2 = new Thread(t);//创建了一个线程t1.start();try{Thread.sleep(10);}catch(Exception e){}t.flag = false;t2.start();}}</span>

如果同步函数被静态修饰后,使用的锁是什么呢?

通过验证,发现不再是this ,因为静态中不可以定义this。静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。类名.class ,该对象的类型是Class     .静态的同步方法使用的锁是该方法所在类的字节码文件对象,类名.class

<span style="font-size:18px;">class Ticket implements Runnable
{
private int tick=100;
//Object obj = new Object;
boolean flag=true;
public void run()
{
if(flag)
{
while(true)
{
synchronized(Ticket.class)
{
if(tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"---code"+tick--);
}
}
}
else
while(true)
show();
}
public static synchronized void show()
{
if(tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"---show---"+tick--);
}
}
}</span>
<span style="font-size:18px;">class TicketDemo{public static void main(String [] args){Ticket t = new Ticket();Thread t1 = new Thread(t);//创建了一个线程Thread t2 = new Thread(t);//创建了一个线程t1.start();try{Thread.sleep(10);}catch(Exception e){}t.flag = false;t2.start();}}</span>

八、单例设计模式
饿汉式。
<span style="font-size:18px;">class Single
{
private static final Single s = new Single();
private Single(){}
public static Single getInstance()
{
return s;
}
}</span>
懒汉式

<span style="font-size:18px;">class Single
{
private static Single s = null;
private Single(){}
public static synchronized Single getInstance()
{
if(s==null)
s = new Single();
return s;
}
}</span>
<span style="font-size:18px;">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;}} }}</span>

九、死锁:同步中嵌套同步,而锁却不同

<span style="font-size:18px;">class Ticket implements Runnable
{
private int tick=100;
Object obj = new Object;
boolean flag=true;
public void run()
{
if(flag)
{
while(true)
{
synchronized(obj)
{
show();
}
}
else
while(true)
show();
}
public static synchronized void show()//this锁
{
synchronized(obj)
if(tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"---show---"+tick--);
}
}
}</span>
<span style="font-size:18px;">class TicketDemo{public static void main(String [] args){Ticket t = new Ticket();Thread t1 = new Thread(t);//创建了一个线程Thread t2 = new Thread(t);//创建了一个线程t1.start();try{Thread.sleep(10);}catch(Exception e){}t.flag = false;t2.start();}}</span>

十、线程间的通讯通信

1、线程间通信

--Input-->资源--Output--->

思考1:wait(),notify(),notify All(),用来操作线程为什么定义在Object类中?

(1),这些方法存在与同步中。

(2),使用这些方法时必须要标识所属的同步锁

(3),锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。
思考2:wait(),sleep(),有什么区别?

(1),wait():释放资源,释放锁。

(2),sleep:释放资源,不释放锁。

2、解决安全问题
class Res
{
String name;
String sex;
}
<pre name="code" class="java">class Input implements Runnable{private Res r;Input(Res r){this.r=r;}public void run(){int x = 0;while(true){synchronized(r){if(x==0){r.name = "mike";r.sex = "man";}else{r.name = "丽丽";r.sex = "女女女女女";}x =(x+1)%2;}}}}
 
class Output implements Runnable
{
private Res r;
Output(Res r)
{
this.r=r;
}
public void run()
{
while(true)
{
synchronized(r)
{
System.out.println(r.name+"---"+r.sex);
}

}
}
}
class  {public static void main(String[] args) {System.out.println("Hello World!");}}
3、等待唤醒机制
class Res
{
private String name;
private String sex;
private 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 out()
{
if(!flag)
try{this.wait();}catch(Exception e){}
System.out.println(name+"---"+sex);
flage = false;
this.notify();
}
}
class Input implements Runnable{private Res r;Input(Res r){this.r=r;}public void run(){int x = 0;while(true){if(x==0){r.set("mike","man");}else{r.set("丽丽","女");}x =(x+1)%2;}}}
class Output implements Runnable{private Res r;Output(Res r){this.r = r;}public void run(){while(true){r.out();}}}
class InputOutputDemo {public static void main(String[] args) {Res r = new Res();new Thread (new Input(r)).start();new Thread (new Output(r)).start();/*Input in = new Input(r);Output out = new Output(r);Thread t1 = new Thread(in);Thread t2 = new Thread(out);t1.start();t2.start();*/}}
wait(); n otify();  notifyAll(); 都使用在同步中,因为要对持有监视器(锁)的线程操作。 所以要使用在同步中,因为

只有同步才具有锁。为什么这些操作线程的方法要定义Object类中呢?因为这些方法在操作同步中线程时,都必须要标识它们所操作线程持有的锁,只有同一个锁上的被等待的线程,才可以被同一个锁上的notify唤醒不可以对不同锁中的线程进行唤醒也就是说,等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中notify();存放在线程池中,只有存放在,线程池中的线程才能被唤醒,通常唤醒第一个被等待的线程。

生产者消费者

对于多个生产者和消费者为什么要定义while判断标记

原因:让被唤醒的线程再判断标记。

为什么定义notifyAll,

   因为需要唤醒对方线程,因为只用notify,容易出现只唤醒本方线程的情况,导致程序中的所有线程都等待。

<span style="font-size:18px;">class ProducerConsumerDemo
{
public static void main(String[] args)
{
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(con);
t1.start();
t2.start();
}
}</span>
<span style="font-size:18px;">class Resourse{private String name;private int count =1;private boolean flag =false;public synchronized void set(String name){while(flag)try{this.wait();}catch(Exception e){}this.name = name +"--"+ count++;System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);flag = true;this.notifyAll();}public synchronized void out(){while(!flag)try{this.wait();}catch(Exception e){}System.out.println(Thread.currentThread().getName()+"...消费者.."+this.name);flag = false;this.notifyAll();}}</span>
<span style="font-size:18px;">class Producer implements Runnable{private Resource res;Producer(Resource res){this.res = res;}public void run(){while(true){res.set("++商品++");}}}</span>
<span style="font-size:18px;">class Consumer implements Runnable{private Resource res;Consumer(Resource res){this.res = res;}public void run(){while(true){res.out();}}}</span>
十一、API 1.5
JDK1.5 中提供了多线程升级解决方案
将同步synchronized替换成现实Lock操作将Object中的wait,notify,notifyAll替换成Condition对象

该对象可以Lock锁进行获取

该示例中,实现了本方只唤醒对方操作。

import java.util.concurrent.locks.*;  
class ProducerConsumerDemo
{
public static void main(String[] args)
{
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(con);
t1.start();
t2.start();
}
}
class Resourse
{
private String name;
private int count =1;
private boolean flag =false;
private Lock lock = new ReentrantLock();
private Condition condition_pro = lock.newCondition();
private Condition condition_con = lock.newCondition();
public void set(String name)throws InterruptedException
{
lock.lock();
try
{
while(flag)
condition_pro.await();
this.name = name +"--"+ count++;
System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
flag = true;
condition_con.signalAll();
}
finally
{
lock.unlock();//释放锁的动作一定会执行
}
}
public void out()throws InterruptedException
{
lock.lock()
try
{
while(!flag)
condition_con.await();
System.out.println(Thread.currentThread().getName()+"...消费者.."+this.name);
flag = false;
condition_pro.signalAll();
}
finally
{
lock.unlock();
}
}
}
class Producer implements Runnable
{
private Resource res;
Producer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
try
{
res.set("++商品++");
}
catch (InterruptedException e)
{
}
}
}
}
class Consumer implements Runnable
{
private Resource res;
Consumer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
try
{
res.out();
}
catch (InterruptedException e)
{
}
}
}
}
十二、停止线程

(1),定义循环结束标记

因为线程运行代码一般都是循环,只要控制了循环即可。

(2),使用interrupt(中断)方法

该方法是结束线程的冻结状态,使线程回到运行状态中来

注意:stop方法已经过时不再使用。

stop方法已经过时,如何停止线程?只有一种,run方法结束开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束,也就是线程结束。

特殊情况:
     当线程处于了冻结状态,就不会读取到标记,那么线程就不会结束。当没有指定方式让冻结的线程恢复到    运行状
态时,这时需要对冻结进行清除。强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束.

Thread类提供该方法interrupt();  

守护线程setDaemon();在启动线程前调用,当前台线程结束,后天线程自动结束。

class StopThread implements Runnable
{
private boolean flag = true;
public void run()
{
while(flag)
{
System.out.println(Thread.currentThread().getName()+"....run");
}
}
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();
break;
}
System.out.println(Thread.currentThread().getName()+"...."+num);
}
}
}
(3)join()示例
join 特点:当A线程执行到B线程的.join()方法时,就会等待,等待B线程都执行完,A才会执行。
join可以用来临时加入线程执行。

<span style="font-size:18px;">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();
t1.join();//抢夺CPU执行权
t2.start();
for(int x=0; x<80; x++)
{
System.out.prinln("main..."+x);
}
System.out.println("over");
}
}
class ThreadTest
{
public static void main(String [] args)
{
new Thread()
{
public void run()
{
for(int x=0; x<100; x++)
{
System.out.println("...."+x);
}
}
}.start();
for(int x=0; x<100; x++)
{
System.out.println("...."+x);
}
Runnable r = new Runnable()
{
public void run()
{
for(int x=0; x<100; x++)
{
System.out.println("...."+x);
}
}
};
new Thread(r).start();
}
}</span>