周一,有些累

   记得再继续讲解java第六天的时候,正好是周一,那个时候真的是太累了,没这么辛苦过。

今天所讲的知识点
A 进程与线程的区别
B Java的线程实现(继承Thread类或Runnable接口)
C 线程的操作状态
D 线程的操作方法
E 线程的同步与死锁
F 线程的经典案例:生产者---消费者
G 线程的声明周期
H Java的泛型


我对知识点的分析

内容
今天所讲的知识点(如果表格不够,可在备注中继续填写)
A 进程与线程的区别
B Java的线程实现(继承Thread类或Runnable接口)
C 线程的操作状态
D 线程的操作方法
E 线程的同步与死锁
F 线程的经典案例:生产者---消费者
G 线程的声明周期
H Java的泛型
I
J
K
备注:

我对知识点的分析(如果表格不够,可在备注中继续填写)
A 进程与线程的区别:
进程就是一个应用程序,每运行一个程序,就会在任务管理器的进程列表中添加一个进程;
而线程呢则是一个进程的更细的划分,或者说这个程序运行时,多个功能同时在运行完成多项服务;
例如:
把一个银行想象成一个进程,而它的里面分了好几个窗口,同时提供多个服务或者同一个服务分为多个窗口一起完成,这每一个窗口就如同一个线程;

B 在Java中如果要想实现多线程的操作,有两种实现方法:
(1)继承Thread类
(2)实现Runnable接口

一、继承Thread类
一个类只要继承了Thread类,那么此类就可以是一个多线程的操作类,Thread类是在java.lang包中定义的类,所以此类可以自动导入并使用。
注意:单单只是继承Thread类还不能完成线程的功能,还必须覆写Thread类中的run()方法。
语法格式:
class 线程类 extends Thread{  // 继承Thread类
public void run(){  // 覆写run()方法
  // 此方法编写的是线程的主体
}
}
问题一:直接调用run方法将不能同时运行多个线程,需要调用由Thread类继承下来的start()方法
例如:
package mldn.lin.thread;
//Thread在java.lang包中,不用手动导入java.lang包,可自动导入
public class ThreadDemo01 extends Thread{
private String name;
public ThreadDemo01(String name) {
  super();
  this.name = name;
}
public ThreadDemo01() {
  super();
}
//此处不可以覆写getName和setName方法,因为在Thread类中这两个方法定义为final
// public String getName() {
//  return name;
// }
// public void setName(String name) {
//  this.name = name;
// }

//覆写Thread的run方法
public void run(){
  for(int i=0;i<30;i++){
   System.out.println(this.getName()+":i="+i);  
  }
}
}
-----------------------------------------------------
package mldn.lin.thread;
public class ThreadDemo01Test {
public static void main(String[] args) {
  ThreadDemo01 t1=new ThreadDemo01("线程1");
  ThreadDemo01 t2=new ThreadDemo01();
  ThreadDemo01 t3=new ThreadDemo01("线程3");
  ThreadDemo01 t4=new ThreadDemo01();
  t1.run();
  t2.run();
  t3.run();
  t4.run();
}
}
结果:发现t1、t2、t3、t4四个线程是顺序执行的,并没有真正的实现多线程?
修改代码如下:
package mldn.lin.thread;
public class ThreadDemo01Test {
public static void main(String[] args) {
  ThreadDemo01 t1=new ThreadDemo01("线程1");
  ThreadDemo01 t2=new ThreadDemo01();
  ThreadDemo01 t3=new ThreadDemo01("线程3");
  ThreadDemo01 t4=new ThreadDemo01();
  t1.start();
  t2.start();
  t3.start();
  t4.start();
}
}
修改后结果:
没有直接调用run()方法,不过也执行了run()方法中的语句;而且几个线程发生交替执行的现象;
说明这些线程是同时启动的,只不过都受CPU调度安排,交替运行,即谁抢到了CPU资源,就执行谁;
原因:
如果要想在程序中正确的启动多线程,则必须使用Thread类中的start()方法“public void start()”,而此方法启动后,将由JVM自动去调用线程的run方法。
Thread类的start()方法的源代码:
public synchronized void start() {
  if (threadStatus != 0)
   throw new IllegalThreadStateException();    会有一个异常抛出
  group.add(this);
  start0();  此处调用了start0()
  if (stopBeforeStart) {
   stop0(throwableFromStop);
  }
}
private native void start0();   此方法为私有的,而且使用了native关键字声明

多线程的实现是需要操作系统的支持的,而start()方法中调用的start0()声明的时候加了关键字native,(JNI---Java Native Interface,本地自然接口),此技术是调用操作系统的库函数,这样才能和CPU调度有联系。所以一旦调用了start()方法,就意味着调用着操作系统的低层支持,以运行多线程的程序。

问题二:只能实现单继承
解决方法:采用实现Runnable接口的方法类实现多线程

二、实现Runnable接口
此接口也是在java.lang包中定义的。
Runnable接口源代码:
public interface Runnable{
public void run() ;
}
发现此接口中并没有start()方法,而多线程的启动和实现必须要用如上start()方法来和操作系统打交道。
解决方法:
因为Thread类提供了的构造方法中有:public Thread(Runnable target)
这样就可以用Runnable子类对象去创建一个Thread类对象,然后调用start()方法。
示例代码如下:
package mldn.lin.runnable;
//Runnable在java.lang包中,不用手动导入java.lang包,可自动导入
public class RunnableDemo implements Runnable{
private String name;
public RunnableDemo(String name) {
  super();
  this.name = name;
}
public RunnableDemo() {
  super();
}
public String getName() {
  return name;
}
public void setName(String name) {
  this.name = name;
}
//实现Runnable的run方法
public void run(){
  for(int i=0;i<30;i++){
   System.out.println(this.getName()+":i="+i);  
  }
}
}
-----------------------------------------------------
package mldn.lin.runnable;
public class RunnableDemoTest {
public static void main(String[] args) {
  RunnableDemo t1=new RunnableDemo("线程1");
  RunnableDemo t2=new RunnableDemo();
  RunnableDemo t3=new RunnableDemo("线程3");
  RunnableDemo t4=new RunnableDemo();
  new Thread(t1).start();
  new Thread(t2).start();
  new Thread(t3).start();
  new Thread(t4).start();
}
}


三、两种实现方法的区别
1、直接继承Thread类存在单继承的局限,而采用实现Runnable接口的方法就不会;
2、使用Runnable接口还可以方便的实现数据的共享操作,而采用继承Thread类的方法就不能实现
3、观察Thread类的定义格式:public class Thread extends Object implements Runnable,发现Thread类也是对Runnable接口的实现。
那么如上的Runnable接口的子类对象,最后通过Thread类对象启动多线程,就是采用了代理设计模式;

示例代码:(共享数据)
package mldn.lin.MutilThread;
public class MutilThreadDemo implements Runnable{
private int ticket=3;
public void run(){
  for(int i=0;i<10;i++){
   if(this.ticket>0){
    System.out.println(this.getClass().getName()+"剩余的票数:"+this.ticket--);  
   }
  }
}
}
-----------------------------------------------------------------------------------------------
package mldn.lin.MutilThread;
public class MutilThreadDemoTest {
public static void main(String[] args) {
  MutilThreadDemo t1=new MutilThreadDemo();
  new Thread(t1).start();//实例化一个线程
  new Thread(t1).start();//实例化一个线程
  new Thread(t1).start();//实例化一个线程
}
}
因为用了同一个Runnable子类对象去实例化的线程,所以共享同一个数据

C 线程的操作状态:

第一步创建一个线程的实例化对象。
第二步启动多线程,调用start()方法之后并不是立刻执行多线程操作,而是跑到就绪状态。
第三步等待CPU进行调度,调度之后进入到运行状态。
第四步如果现在假设运行一段时间之后,被其他线程抢先了,则出现了阻塞状态。
第五步,从阻塞状态要重新回到就绪状态,等待CPU的下一次调度
第六步,当全部的操作执行完成之后,线程将终止执行。
虽然在上述的代码上操作是有先有后,但是所有的线程肯定都是同时启动的,所以在线程的开发中没有顺序而言。哪个线程抢占到了CPU资源,哪个线程就先执行。

D 在Thread类中定义了线程的操作方法:
一、取得当前的线程、设置和取得名字
No. 方法名称 类型 描述
1 public static Thread currentThread() 普通 返回当前正在执行的线程对象
2 public final String getName() 普通 取得线程的名字
3 public final void setName(String name) 普通 设置线程的名字
4 public Thread(Runnable target,String name) 构造 接收Runnable对象,并指定线程名称
5 public Thread(String name) 构造 为线程指定一个名字
例如:
package mldn.lin.runnableclass.method;
public class ThreadMethod implements Runnable {
public void run() {
  for(int i=0;i<5;i++){
   System.out.println(Thread.currentThread().getName()+"-->运行");
  }
}
}
-----------------------------------------------------------------------------------------------
package mldn.lin.runnableclass.method;
public class ThreadMethodTest {
public static void main(String[] args) {
  ThreadMethod t1=new ThreadMethod();
  new Thread(t1,"线程1").start();
  new Thread(t1).start();
  new Thread(t1,"线程2").start();
  new Thread(t1).start();
  t1.run();// 直接调用run()方法   直接调用的时候出现了main线程
}
}
发现:
①没有设置线程名字的线程对象所有的命名都采用“Thread-xxx”的形式出现。因为在Thread类中肯定存在一个static的属性,用于记录每次的增长结果。
②结果中出现了main线程,主方法本身也是一个线程。
提示:Java每次运行的时候至少两个线程:主线程、GC线程
二、判断线程的运行状态
判断一个线程是否存活:public final boolean isAlive()
示例代码:
package mldn.lin.threadactive;
class ThreadAlive implements Runnable {
public void run() {
  for(int i=0;i<2;i++){
   System.out.println(Thread.currentThread().getName()+"-->运行");
  }
}
}
public class ThreadAliveDemo {
public static void main(String[] args) {
  ThreadAlive t1=new ThreadAlive();
  Thread my=new Thread(t1,"测试线程");
  System.out.println("线程启动之前:"+my.isAlive());//false
  my.start();
  System.out.println("线程启动之后:"+my.isAlive());//true
  for(int i=0;i<100;i++){
   System.out.println("等待测试线程执行完,故意写的");
  }
  System.out.println("线程执行完之后:"+my.isAlive());//false
}
}
三、线程的强制执行
No. 方法名称 类型 描述
1 public final void join() throws InterruptedException 普通 强制运行
2 public final void join(long millis) throws InterruptedException 普通 规定强制运行的毫秒数
3 public final void join(long millis,int nanos) throws InterruptedException 普通 规定强制运行的毫秒和纳秒数
示例代码:
package mldn.lin.threadapi.joindemo;
public class JoinDemo implements Runnable {
public void run() {
  for (int x = 0; x < 10; x++) { // 取得当前运行的线程名称
   System.out.println(Thread.currentThread().getName() + " --> 运行,x = " + x);
  }
}
}
-------------------------------------------------------------------------
package mldn.lin.threadapi.joindemo;
public class JoinDemoTest {
public static void main(String[] args) {
  Thread t = new Thread(new JoinDemo(),"测试线程"); // 声明线程对象
  t.start();
  try {
   t.join();
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
  System.out.println("测试线程执行完成后,才回到主方法");
}
}
四、线程的休眠
No. 方法名称 类型 描述
1 public static void sleep(long millis) throws InterruptedException 普通 休眠指定的毫后继续执行
2 public static void sleep(long millis,int nanos) throws InterruptedException 普通 修面指定豪秒和纳秒之后继续执行
package mldn.lin.threadapi.sleepdemo;
public class ThreadSleepTest {
public static void main(String[] args) {
  for(int i=0;i<5;i++){
   System.out.println("第"+i+"次打印");
   try {
    System.out.println("main线程休眠");
    Thread.sleep(1000);
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
   System.out.println("main线程休眠完毕");
  }
}
}
五、线程的中断
中断线程运行:public void interrupt()
判断线程是否被中断:public boolean isInterrupted()
示例代码:
package mldn.lin.threadapi.interruptdemo;
public class ThreadInterruptTest {
public static void main(String[] args) {
  Thread tt=Thread.currentThread();
  System.out.println("人为中断main线程");
  tt.interrupt();
  System.out.println("main线程是否被中断:"+tt.isInterrupted());
}
}
六、线程的优先级
No. 方法及常量名称 类型 描述
1 public static final int MAX_PRIORITY 常量 最高优先级
2 public static final int NORM_PRIORITY 常量 普通优先级
3 public static final int MIN_PRIORITY 常量 最低优先级
4 public final int getPriority() 普通 得到线程的优先级
5 public final void setPriority(int newPriority) 普通 设置线程的优先级
示例代码:
package mldn.lin.threadapi.prioritydemo;
public class PriorityDemoTest {
public static void main(String[] args) {
  Thread tt=new Thread("测试线程");
  tt.setPriority(Thread.MAX_PRIORITY);
  System.out.println("测试线程的优先级为:"+tt.getPriority());
  System.out.println("main线程的优先级为:"+Thread.currentThread().getPriority());
}
}

E 在多线程的操作中,多个线程有可能同时处理同一个资源,这个时候如果不加入同步处理,会使得程序很不安全。例如:
package mldn.lin.threadsynchronizeddemo;
public class ThreadSynchronizedDemo implements Runnable {
private int ticket = 3;
public void run() {
  for (int i = 0; i < 100; i++) {
   if (this.ticket > 0) {
    try {
     //为了测试效果,人为的添加延迟,模拟现实
     Thread.sleep(100);
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
    System.out.println(this.getClass().getName() + "剩余的票数:"
      + this.ticket--);
   }
  }
}
}
-------------------------------------------------------------------------------------------------------
package mldn.lin.threadsynchronizeddemo;
public class ThreadSynchronizedDemoTest {
public static void main(String[] args) {
  ThreadSynchronizedDemo t1=new ThreadSynchronizedDemo();
  new Thread(t1).start();//实例化一个线程
  new Thread(t1).start();//实例化一个线程
  new Thread(t1).start();//实例化一个线程
}
}
运行效果:
mldn.lin.threadsynchronizeddemo.ThreadSynchronizedDemo剩余的票数:3
mldn.lin.threadsynchronizeddemo.ThreadSynchronizedDemo剩余的票数:2
mldn.lin.threadsynchronizeddemo.ThreadSynchronizedDemo剩余的票数:1
mldn.lin.threadsynchronizeddemo.ThreadSynchronizedDemo剩余的票数:0
mldn.lin.threadsynchronizeddemo.ThreadSynchronizedDemo剩余的票数:-1
结果出现了与现实需要相违背的情况。
这个就是多线程共享数据的不安全性的体现。
以上的程序的执行步骤:
1、 判断是否还有票
2、 修改票数
此时,所有的线程会同时进入到操作的方法之中。所以,当一个线程刚刚判断完ticket为1,进入到if语句块中,而因为延迟或还没来得及输出剩余票数,下一个线程已经把其改为0,当它再次来输出剩余票数的时候,取得已经是当前的ticket值而不是当初判断的值了。

一、线程的同步
使用synchronized关键字完成。
在Java中要想对线程进行同步,有以下两种方法:第一种使用同步代码块,第二种使用同步方法。
1、同步代码块
如果要想使用同步代码块的话,则必须指定要对那个对象进行同步,所以同步代码块的执行格式如下:
synchronized(要同步的对象){
要同步的操作 ;
}
例如:修改以上部分代码
synchronized(this){ // 对当前操作的对象进行同步
     try {
      //为了测试效果,人为的添加延迟,模拟现实
      Thread.sleep(100);
     } catch (InterruptedException e) {
      e.printStackTrace();
     }
     System.out.println(this.getClass().getName() + "剩余的票数:"
       + this.ticket--);
    }
2、使用同步方法
public void run() {
  this.sale();
}
public synchronized void sale(){//定义同步方法
  for (int i = 0; i < 100; i++) {
   if (this.ticket > 0) {
    synchronized(this){
     try {
      //为了测试效果,人为的添加延迟,模拟现实
      Thread.sleep(100);
     } catch (InterruptedException e) {
      e.printStackTrace();
     }
     System.out.println(this.getClass().getName() + "剩余的票数:"
       + this.ticket--);
    }
   }
  }
}


二、线程的死锁
但是,同步问题虽然解决了数据的安全问题,但是如果同步太多,又会导致另外一个线程的问题,即死锁问题。死锁问题是说当一个线程占用了共享操作块时,因为某种原因没有交出该块的使用权,那么其他的线程就将永远等待,这使得程序陷入死锁状态。
总之,“程序中如果要想进行资源的共享操作,则肯定需要同步,如果同步过多,则就有可能造成死锁”

F 线程的经典案例:生产者---消费者
主要思想就是:
当生产者完整的生产完一个产品之后,消费者才能取走产品,并且只有等消费者完整的取走产品之后,生产者才能继续生产下一个产品。
①生产者完整的生产产品和消费者完整的取产品的方法采用同步的处理,这样才不会发生错误匹配;
②那么什么时候开始生产,什么时候开始取货,衔接问题是需要用一个指示灯的,即做个标记
• 如果boolean的值是true的话,表示可以生产,但是不能取走
• 如果boolean的值是false的话,表示可以取走,但是不能生产
③在生产者还没有生产好产品时,消费者只能等待,等其生产好之后,需要唤醒消费者取走产品,当消费者还没有取走产品时,生产方也只能等待,当消费者取走产品后,需要唤醒生产者;
在Object类中提供了等待及唤醒的机制。
No. 方法名称 类型 描述
1 public final void wait() throws InterruptedException 普通 等待
2 public final void wait(long timeout) throws InterruptedException 普通 等待,并设置等待时间
3 public final void wait(long timeout,int nanos) throws InterruptedException 普通 等待,并设置等待的时间
4 public final void notify() 普通 唤醒第一个等待的线程
5 public final void notifyAll() 普通 唤醒所有等待的线程,任它们自己抢占


G

在Thread类中有如下三个方法是不使用的:
  1、 suspend():表示暂时挂起线程
  2、 resume():表示恢复已挂起的线程
  3、 stop():表示停止线程运行
在Java doc文档中已经明确的标记出此方法都属于“Deprecated”声明,表示此操作已经不建议继续使用了。 以上的三个操作都会存在死锁的情况出现。
那么如果现在要想完成一个线程的停止操作,该如何进行呢?采用标记位停止操作
H JDK 1.5之后出现了新的技术 —— 泛型,此技术的最大特点是类中的属性的类型可以由外部决定,而且在声明类的时候应该采用如下的形式:
class 类名称<泛型类型1,泛型类型2,…>{

}


备注:
A 进程与线程的区别
B Java的线程实现(继承Thread类或Runnable接口)
C 线程的同步与死锁
D 泛型
E
F
F
备注:



今天白天因为头疼,效率较低,所以晚上回来又重新把这些代码自己写一遍,特别是线程部分。不过泛型部分,因为太晚了,有些代码还没有完成自己编写的过程,我一定会在今后两天抽空把它补上的。
接下来的学习任务似乎更重了,身体的健康显得更加重要了。
加油↖(^ω^)↗
同时也希望李老师也注意身体!不要太累了!

你可能感兴趣的:(jdk,多线程,thread,应用服务器,F#)