黑马程序员-多线程

------- android培训java培训、期待与您交流! ----------

1.创建一个线程

方式一:继承Thread类。
步骤:
1,定义一个类继承Thread。
2,复写Thread类中的run方法。
3,将自定义代码存储在run方法。让线程运行。
4,调用线程的start方法,可以开启线程。

示例代码:
class ManyThread extends Thread
{
public void run()
{
      执行代码
}}

 main(){

   new ManyThread().start();

}

方式二 实现Runnable接口并覆盖run方法

步骤

class MyThread implements  Runnable

{

public void run()

{

    执行代码

}}

main(){

   new Thread(new MyThread()).start();

}

   例如,卖票的例子

   当用第一种实现方式时,那么票数必须被静态(为共享数据),由多个对象共享

   当用第二种实现方式时,那么票数就不许被静态修饰(静态生命周期太长),只创建一个Runable子类的对象,由多个线程开启

 

实现方式和继承方式有什么区别

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

 

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

    问题的原因

         当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完

         另一个线程参与进来执行,导致共享数据的错误

 

解决的办法

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

       解决办法就是同步代码块

synchronized(对象)

{

        需要被同步的代码

     对象如同锁,持有锁的线程可以在同步中执行。

     没有持有锁的线程即使获取CPU的执行权,也进不去,因为没有获取锁

同步的前提

  1  必须要有两个或者两个以上的线程

  2  必须是多个线程使用同一个锁

     必须保证同步中只能有一个线程在运行

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

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

如果存在安全问题,怎么找问题

   1 明确哪些代码是多线程运行代码

   2 明确共享数据

   3 明确多线程运行代码中哪些语句时操纵共享数据的

同步函数用的是哪个锁呢

  函数需要被对象调用,那么函数都有一个所属对象引用,就是this

  所以同步函数使用锁是this

同步静态函数用的是哪个锁

    当静态函数进内存时,因为静态方法中不可以定义this,所以不会是this锁,

    内存中没有本类对象,但是一定有该类对应的字节码文件对象,

    类名.class    该对象的类型是Class

 

 

 

 

2.终止线程
stop方法已经过时。
如何停止线程?
只有一种,run方法结束。
开启多线程运行,运行代码通常是循环结构。
只要控制住循环,就可以让run方法结束,也就是线程结束。
特殊情况:
当线程处于了冻结状态。
就不会读取到标记。那么线程就不会结束。
当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除。
强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。
Thread类提供该方法 interrupt();

3.守护进程

class zczthread implements Runnable
{
}
class main
{
public static void main(String[] args)
{
   zczthread t = new zczthread();
  Thread a = new Thread(t); 
  a.setDaemon(true); //将a线程设置为守护线程,该行代码不能放在start()方法下面

  a.start();    
}
}
守护线程即后台线程
当所有前台线程都结束时,后台线程自动结束
也就是说 当所有线程都为守护线程时,java虚拟机退出

线程唤醒机制
wait()
notify()
notifyAll()

都使用在同步中,因为要对持有监视器(锁)的线程操作
所以要使用在同步中,因为只有同步才具有锁

为什么这些操作线程的方法要定义在Object类中?
 因为这些方法在操作同步中的线程时,都必须要标示它们所操作线程的锁
只有同一个锁上的被等待线程,可以被同一个锁notify唤醒
不可以对不同的锁中的线程进行唤醒

也就是,等待与唤醒必须要是同一个锁

而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中



在单CPU的硬件平台下,某一时刻只能有一个线程在运行,线程是由操作系统调度的。

启动一个线程不能直接调用run()方法,这样不会创建一个新的线程,只是简单的在当前线程中

执行了run()方法,
而应该调用start()方法,这样就会创建一个新的线程,在这个新的线程上执行run()方法上的

代码。
当run()方法执行完后,线程也就相应的结束,我们可以通过控制run()方法中循环的条件来控

制线程的结束。
(一个进程里面的多个线程都是无规律的交替运行)

Thread类中的setDaemon(true)设置一个线程为后台线程,该方法必须在线程启动之前调用,也

就是在调用start()方法之前调用。
如果一个进程中只有后台线程在运行,这个进程就会结束。

thread1.join()的作用是把thread1所对应的线程合并到调用thread1.join()的线程中。

yield()方法可以让当前正在运行的线程对象临时暂停,让别的线程运行。

sleep(...)方法可以让当前正在运行的线程进入睡眠状态。

获取和更改线程的优先级分别用Thread对象的getPriority()方法和setPriority(int

newPriority)方法。线程的默认优先级的值是5。

创建一个线程类有两种方法:
1,直接继承Thread类;
2, 实现Runnable接口,然后用new Thread(实现Runnable接口的类的对象)来产生一个线程类。

使用Runnable接口创建多线程,适合多个相同程序代码的线程去处理同一份资源的情况。还可

以避免由于java的单继承特性带来的局限。
事实上,几乎所有的多线程应用都可用Runnable接口方式。

每个线程都会有一个运行的时间片,都会有开始和时间片到期的时候。
所以我们要注意了:一个线程的时间片到期的时候,此线程有可能执行到程序的任何一个位置

而被暂停,并没有完全执行完一个原子性的模块,
然后就进入时间片的轮换,这样就导致多线程的运行结果不可意料。此时我们就需要对线程进

行同步处理。

线程同步关键字是:synchronized

同步代码块放在如下的大括号中:
synchronized(obj){     //用于synchronized的obj叫lock旗标(锁旗标),可以是任何对象,

我们通常称之为同步对象或者监视器对象。
}

同步方法只须在方法前加synchronized关键字修饰即可。
同步非静态方法的监视器是this,而同步静态方法的监视器是当前所在的类的Class对象。

只要使用相同的同步对象(监视器),synchronized方法和synchronized代码块也可以实现同步

在编写多线程同步的时候一定要注意避免死锁的发生。

String str1 = "";
String str2 = "";

while(true) {
 synchronized(str1) {
  ...
  synchronized(str2){
   ...
  }
  ...
 }
}

while(true) {
 synchronized(str2) {
  ...
  synchronized(str1){
   ...
  }
  ...
 }
}

如果上面的两个while块里面的代码分别由两个线程执行,那么就形成了死锁。

我们在编写线程安全的程序代码的时候也必须防止一个线程对共享的数据仅仅做了部分的操作

的时候,这个线程就结束了,这种情况下就会破坏数据的一致性。

要实现同步的线程所检查的监视器对象必须是同一个对象,才能保证线程的同步。

不同的类中的方法或者语句块也可以实现同步,只要是同一个监视器对象就可以了。

多线程编程里面的run()方法里面通常都是一个while循环程序。

Object类里面有几个方法是用于线程通信的:wait(),notify(),notifyAll()。由于任何对象

都可以作为监视器对象,所以任何对象都可以调用这几个方法。
但是这些方法只能在被synchronized修饰的方法或者代码块中调用。
每个对象除了有一个锁旗标之外,还有一个线程等待队列(wait set),一个对象刚创建的时

候,它的等待队列是空的。

如果一个线程里面有while循环,我们可以设置一个boolean flag变量作为while的循环条件,

while(flag),
当我们需要结束这个线程的时候就可以在另外一个线程中将这个flag设置为false即可。
但是有时候我们还需要结合interrupt()方法来结束一个线程,见如下例子:
package com.heima.exam;

class StopThreadTest {
 public static void main(String[] args) {
 
  ThreadInStop t1 = new ThreadInStop();
  t1.start();
  int index = 0;
 
  while(true) {
   if(index++ == 500){
    t1.stopThread();
    t1.interrupt();
    break;
   }
   System.out.println(Thread.currentThread().getName());
  }
  System.out.println("main() exit");
 }
}

class ThreadInStop extends Thread {
 
 private boolean bStop = false;
 
 public synchronized void run() {
 
  while(!bStop) {
   try {
    wait();
   } catch(InterruptedException e) {
    if(bStop) {
     return;
    }
   }
   System.out.println(getName());
  }
 }
 
 public void stopThread() {
  bStop = true;
 }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值