我的java日记(多线程)

来源:互联网 发布:java逆序输出数组 编辑:程序博客网 时间:2024/05/16 10:39


经验是积累出来的,只有每天不断的进步,才能超越,只有每天不断的学习才能进步.通过这些天看毕向东老师的视频,让我学到了很多的东西,以前不懂的现在也逐渐的明了了.

下面是我这两天学习多线程总结的知识点.:

多线程:
1:进程:当前正在进行的程序,代表的是应用程序在内存中的执行区域.
  线程:是进程中的一个执行单元,执行路径,负责的是程序的执行.一个进程中至少有一个线程在负责程序的执行.
  单线程程序:一个正在运行的程序只有一条执行路径(单元),那么,这个程序就称为单线程程序.
  多线程程序:一个正在运行的程序有多条执行路径(单元),这个程序就称为多线程程序.
    例:360图形管理界面可以同时运行电脑体检和木马查杀等,这就相当于在360中有多天执行路径;
  迅雷下载工具可以同时下载多个.
2:jvm启动至少启动两个线程:主方法和垃圾回收.
    假如:jvm的启动时单线程的,那么,程序在执行过程中就会有内存溢出这样的隐患,而我们使用java程序时,
  没有这个问题.说明垃圾回收机制是早就启动了.
    jvm启动有两条执行程序,一条用于执行程序,一条用于执行垃圾回收.所以jvm的启动是多线程的.

3:线程是进程的执行路径,而进程是由windows创建的,java语言不能直接操作系统底层.

4:
(1)创建线程的第一种方式:
 A:继承Thread类
 B:覆写Thread类中的run方法;
 C:创建自定义类的对象.
 D:调用start()方法,开启线程


(2)创建线程的第二种方式:Runnable
 A:自定义类实现Runnable接口.
 B:实现该接口中的run方法
 C:创建Thread类的对象,把自定义的对象传递给Thread类的构造方法中.
 D:调用Thread类中的start方法.
例:public class RunnableDemo implements Runnable{
 public void run(){
  //code
 }
}
public class RunnableDemoTest{
 public static void main(String[] args){
  Runnable r=new Runnable();
  Thread t1=new Thread(r);
  Thread t2=new Thread(r)
  t1.start();
  t2.start();
 }
}
在Runnable中使用Thread.currentThread().getName();取得线程名称.

5:为什么要继承Thread类,并重写run方法呢?
 因为:我们没有办法直接创建线程类,必须通过API提供的Thread类来做.
      线程执行的代码都是放在run方法中,如果我们不重写run,那么将调用的是Thread类的默认处理方式.

6:Thread类中的常用方法:
start():开启线程,Java虚拟机默认调用该线程的run方法.
String getName():返回当前线程的名称.如果没有指定线程名称,默认会给出名字:Thread-x(0,1...);主线程的名字是main
static Thread currentThread():返回当前正在执行的线程对象的引用,
 用于不是Thread类子类的类获取线程名称:Thread.currentThread().getName();
void setName("线程名字");为线程定义名字.
Thread(String name):通过构造方法给线程设置名称.

7:线程的随机性原理:
CPU的特点:在任一时刻(点),只能有一个线程在执行.windows中的任务同时执行,其实就是多个应用程序同时在执行,
          而每一个应用程序的执行是由线程控制的.
windows系统是多线程的操作系统.
由于我们所说的同时执行,其实是多个应用程序不断的争抢CPU的资源.谁抢到,就具有执行权,那么,这个应用程序就会执行.
 因为CPU的切换速度是xx分之一毫秒.

8:线程的运行状态:
线程的声明周期中每个状态的特点:
 新建:通过继承Thread类,并在测试类中创建了对象.
 就绪:通过start()方法,具有了执行资格,没有执行权.
 运行:具有执行资格,具有执行权.
 (阻塞):运行过程中由于某些原因(wait,sleep),释放了执行权和执行资格.
  当然,他们可以回到运行状态,只不过,不是直接回到.
 死亡:对象在内存中变成垃圾,(1)run方法执行完毕.(2)stop()
notify()唤醒
sleep()睡眠

9:(1)产生线程安全的原因
  A:多个线程访问出现延迟。
  B:线程访问的随机性

  注:线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响是非常大的。
 (2)如果判断一个线程程序有没有安全问题呢?
  A:多个线程操作共享数据
  B:这个操作是分多条语句执行的。
 (3)解决线程安全问题的方式
  A:同步代码块
  B:格式:
   synchronized(对象)
   {
    //需要被同步的代码
   }

10:线程同步:为了解决安全问题.
格式: synchronized(对象){需要被同步的代码;}例:火车上上厕所.
(1)同步代码块的锁是任意同一对象都可以的.
(2)当你发现你使用了同步后,任然没有解决线程安全问题,这个时候,就需要考虑使用的是否是同一把锁.

同步的前提:
 同步需要两个或者两个以上的线程.
 多个线程使用的是同一个锁.
 未满足这两个条件,不能称其为同步.

同步的弊端:
 当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗资源的,无形中会降低程序的运行效率.

一:同步函数:解决线程安全问题的第二种方法:
格式:就在方法上加synchronized关键字.

同步方法的锁到底是哪个对象呢?

通过同步测试,发现,同步方法锁对象是this.
 即:synchronized(this){ }
静态方法的锁是:当前类的class文件的类对象.

A:当一个方法的方法体都被同步代码块包围起来了.我们就可以使用另外一种方式来表示.就死同步方法
B:同步方法的使用格式非常简单,就是在方法声明上加同步关键字.
C:同步方法的锁是什么呢?如何测试呢?
 同步方法的锁是this对象.
 通过在run里面同步使用同步方法和同步代码块来测试的.
D:静态方法的锁是什么呢?
 静态方法的锁是当前类class文件描述类对象.

当局部有可能出现问题时用同步代码块.

 


11:死锁:
产生原因:有两个锁,在A锁中使用B锁,在B锁中使用A锁.就会造成死锁问题.
解决方案:不写出死锁的代码.
例:
public class DieLock implements Runnable{
 private boolean flag;
 
 public DieLock(){}
 
 public DieLock(boolean flag){
  this.flag = flag;
 }
 
 @Override
 public void run() {
  if(flag){
   synchronized (MyLock.myLocka) {
    System.out.println(Thread.currentThread().getName()+"if mylocka");
    synchronized (MyLock.myLockb) {
     System.out.println(Thread.currentThread().getName()+"if mylockb");
    }
   }
  }
  else
  {
   synchronized (MyLock.myLockb) {
    System.out.println(Thread.currentThread().getName()+"else mylockb");
    synchronized (MyLock.myLocka) {
     System.out.println(Thread.currentThread().getName()+"else mylocka");
    }
   }
  }
 }
}
public class DieLockTest {
 public static void main(String[] args) {
  DieLock d1 = new DieLock(false);
  DieLock d2 = new DieLock(true);
  
  Thread t1 = new Thread(d1);
  Thread t2 = new Thread(d2);
  
  t1.start();
  t2.start();
 }
}
12:线程间的交互(通信):
多个线程加锁的时候要注意:
 A:对多个线程都要加锁.
 B:这多个线程必须使用同一把锁

 void wait() 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
 释放执行权,并释放锁.被唤醒的线程会继续往下执行.
例:
public class Student {
 private Student(){}
 private static Student s=new Student ();
 public static Student getSingle() {
  return s;
 }
 String name;
 int age;
 boolean flag = false;
}
public class InputStudent implements Runnable {
 Student s=Student.getSingle();
 public InputStudent() {}
 public InputStudent(Student s) {
  this.s = s;
 }
 public void run() {
  int x = 0;
  while (true) {
   synchronized (s) {
    //如果我们有值,我们就等待。
    if(s.flag){
     try {
      s.wait();
     } catch (InterruptedException e) {
      e.printStackTrace();
     }
    }
    if (x % 2 == 0) {
     s.name = "林青霞";
     s.age = 20;
    } else {
     s.name = "刘德华";
     s.age = 40;
    }
    //唤醒打印线程
    s.flag = true;
    s.notify();
   }
   x++;
  }
 }
}
public class OutputStudent implements Runnable {
 Student s=Student.getSingle();
 public OutputStudent() {
 }
 public OutputStudent(Student s) {
  this.s = s;
 }
 @Override
 public void run() {
  while (true) {
   synchronized (s) {
    if(!s.flag){
     try {
      s.wait();
     } catch (InterruptedException e) {
      e.printStackTrace();
     }
    }    
    System.out.println(s.name + "***" + s.age);
    s.flag = false;
    s.notify();
   }
  }
 }
}
public class ResourceTest {
 public static void main(String[] args) {
  Student s = Student.getSingle();
  
  InputStudent is = new InputStudent(s);
  OutputStudent os = new OutputStudent(s);
  
  Thread t1 = new Thread(is);
  Thread t2 = new Thread(os);
  
  t1.start();
  t2.start();
 }
}

13:wait和sleep的区别:
 A:对于时间的指定:
  sleep()必须要指定时间
  wait()方法有重载的形式,可以指定时间,也可以不指定时间.
 B:执行权和锁的释放:
  sleep()释放执行权,不释放锁.
  wait()释放执行权,释放锁.

14:停止线程:
 A:run方法自动结束
 B:使用interrupt()方法
 C:可以通过控制循环条件结束.

15:常用方法:
(1) interrupt():用于中断一个线程,抛出一个InterruptedException异常.
例:
public class InterruptDemo {
 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 count = 0;
  while (true) {
   if (count++ == 50) {
    t1.interrupt();
    t2.interrupt();
    break;
   }
   System.out.println(Thread.currentThread().getName() + "***" + count);
  }
 }
}
(2) setDaemon(boolean b):将该线程标记为守护或用户线程.也称为后台线程.当主线程结束后,后台自动结束.
例:
public class SetDaemonDemo {
 public static void main(String[] args) {
  StopThread st = new StopThread();

  Thread t1 = new Thread(st, "刘恺威");
  Thread t2 = new Thread(st, "杨幂");
  
  //设置守护线程
  t1.setDaemon(true);
  t2.setDaemon(true);

  t1.start();
  t2.start();

  // 让主线程也参与
  int count = 0;
  while (true) {
   if (count++ == 50) {
    break;
   }
   System.out.println(Thread.currentThread().getName() + "***" + count);
  }
 }
}
(3) join():加入线程,让其他的所有线程都给加入线程让路(即先执行加入线程).
  加入线程执行完毕后,其他线程继续抢夺资源执行.
例:
public class JoinDemo {
 public static void main(String[] args) {

  Demo d = new Demo();
  
  Thread t1 = new Thread(d,"宋江");
  Thread t2 = new Thread(d,"方腊");
  
  t1.start();
  
  try {
   t1.join();
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
  
  t2.start();
  
  for (int x = 0; x < 100; x++) {
   System.out.println(Thread.currentThread().getName() + "***" + x);
  }
 }
}
(4) setPriority(int newPriority):更改线程的优先级
 getPriority():得到线程的优先级.
  线程优先级1-10;线程的默认优先级是5.
  static int MAX_PRIORITY   线程可以具有的最高优先级。10
  static int MIN_PRIORITY   线程可以具有的最低优先级。5
  static int NORM_PRIORITY  分配给线程的默认优先级。  1
例:
public class PriorityDemo {
 public static void main(String[] args) {
  Demo d = new Demo();

  Thread t1 = new Thread(d, "关羽");
  Thread t2 = new Thread(d, "赵云");
  Thread t3 = new Thread(d, "张飞");

  // System.out.println(t1.getPriority());
  // System.out.println(t2.getPriority());
  // System.out.println(t3.getPriority());
  t3.setPriority(10);
  t1.setPriority(1);

  t1.start();
  t2.start();
  t3.start();

  // System.out.println(t1);
  // System.out.println(t2);
  // System.out.println(t3);
 }
}
(5) toString():返回该线程的字符串表示形式,包括线程名称、优先级和线程组。
(6) yield():暂停当前正在执行的线程对象,并执行其他线程,让线程的执行趋近于平衡(礼让)。Thread.yield();


原创粉丝点击