day20进程、线程

来源:互联网 发布:小游戏制作软件下载 编辑:程序博客网 时间:2024/06/04 18:48
补充我们要学习线程,由于线程依赖于进程存在的,要先学习进程!


1:什么是进程?
观察任务管理器,发现正在运行的程序(桌面的应用程序,客户端程序..)就是一个进程
进程来说:进程是系统分配资源调用的一个独立单位.

2:多进程有什么意义呢?
1)现在目前计算机是一种多进程计算机,在做一件事情的同时还可以做另一件事情.
2)多进程是为了提高计算机CPU的使用率
3:多进程:在一边打游戏,一边听音乐,这两个进程是同时进行的吗?
打游戏--->开启一个游戏进程,听音乐--->开启音乐进程,这里面并不是同时进行的,由于CPU的一点点时间片在两者之间进行高效的
切换,感觉同时进行的.
4:线程?
线程是依赖于进程存在,一个线程相当于进程中的某个任务,
多线程的意义:
一个进程中开启了多个任务,每个一任务(线程),他们在互相抢占CPU的执行权,(提高程序的执行效率)
多线程在抢占CPU的执行权:特点:具有随机性!
举例:
打篮球:
1v1,两个人抢占到篮球的几率同样大,那么1v5的时候,如果用线程中考虑(线程的执行具有随机性),只能是5个人抢占到篮球

的几率大,但是不一定能抢占到.


 一.

  1.多线程:如果一个程序的执行路径有多条.
  单线程:程序的执行路径只有一条
  
  
 面试题:Jvm,java虚拟机是多线程程序吗?
 
  是多线程程序,由于java虚拟机中自带一个垃圾回收器,来确保程序不会轻易的造成内存溢出!
  至少开启两条子线程:
  当前程序在执行代码的时候,会开启main:主线程
  垃圾回收器会开启一个垃圾回收线程,来确保程序不会内存异常,将不用的变量或者没有更多引用的对象来回收掉!

例子:

public class ThreadDemo {public static void main(String[] args) {System.out.println(new Object()) ;System.out.println(new Object()) ;System.out.println(new Object()) ;System.out.println(new Object()) ;//....中间创建了很多对象System.out.println(new Object()) ;}}

2.

重点:如何实现多线程程序?

要实现多线程程序,必须创建一个进程,
  创建进程需要调用系统资源进行创建,但是Java语言是不能直接调用系统资源
  C/C++语言是可以创建系统资源,然后使用Java语言掉C/C++已经封装好的东西,
  Java---->类:Thread类
  
 并发和并行
   并发:指的是同一个时间点
   并行:指的的是一个时间段
  
  多线程程序实现方式1:
   1)自定一个类:MyThread 继承自Thread类
   2)在MyThread类中重写Thread类中的run()  :为什么重写run()
   3)在主线程中,创建该类的实例对象,启动线程

例子:

public class ThreadDemo {public static void main(String[] args) {//创建该线程的实例对象//MyThread my = new MyThread() ;//////启动线程///**// * 启动线程不是调用run()方法,// * strat()是线程开始执行的方法//run()方法调用相当于调用了一个普通方法,并不会出现线程随机性;而start()方法调用,//其实是通过Jvm调用线程中的run()来进行多个线程抢占CPU执行权// *///my.start() ;////0java.lang.IllegalThreadStateException///**// * 第二次启动线程,会出现:非法线程状态异常// * 当前my线程已经启动了,不能重新启动// *///my.start() ;//my.run() ;//my.run() ;MyThread my1 = new MyThread() ;MyThread my2 = new MyThread() ;//分别启动线程my1.start() ;my2.start() ;}}

public class MyThread extends Thread {/* * 重写Thread类中的run()方法 * */@Overridepublic void run() {//System.out.println("helloworld");//耗时的操作,线程睡眠,线程等待,循环语句for(int x = 0 ; x <100 ; x ++){System.out.println(x);}}}

3.如何获取线程的名称?
   public final String getName()返回该线程的名称。 
  设置线程名称
  public final void setName(String name)改变线程名称,使之与参数 name 相同。 

例子:

public class ThreadDemo {public static void main(String[] args) {//创建线程类的实例//无参构造的形式//MyThread my1 = new MyThread() ;//MyThread my2 = new MyThread() ;//有参构造形式设置线程名称//MyThread my1 = new MyThread("高圆圆") ;//MyThread my2 = new MyThread("张杨") ;/* * 设置线程名称方式:无参+setName(String name)方法 * */MyThread my1 = new MyThread() ;MyThread my2 = new MyThread() ;// public final void setName(String name)改变线程名称,使之与参数 name 相同。my1.setName("高圆圆") ;my2.setName("张杨") ;//public static Thread currentThread():表示正在运行的线程//默认所有的子线程的都在主线程中System.out.println(Thread.currentThread().getName());//main//启动线程my1.start() ;my2.start() ;}}/* *  * public class Thread implements Runnable{ *  private char        name[]; *   * public Thread() { *初始化        init(null, null, "Thread-" + nextThreadNum(), 0);//"Thread-" + nextThreadNum()    }        private void init(ThreadGroup g, Runnable target, String name,                      long stackSize) {        init(g, target, name, stackSize, null);    }         private void init(ThreadGroup g, Runnable target, String name,                      long stackSize, AccessControlContext acc) {        if (name == null) {            throw new NullPointerException("name cannot be null");        }        this.name = name.toCharArray();//字符数组//此处省略了代码    }        rivate static int threadInitNumber;     private static synchronized(同步机制) int nextThreadNum() {        return threadInitNumber++;//0,1,2..    }        public final String getName() {        return String.valueOf(name);//将当前name字符数组转换成字符串了    }    } *  * */
public class MyThread extends Thread {//无参构造public MyThread(){}//有参构造public MyThread(String name){super(name) ;}@Override//my1和my2子线程都会执行这段代码,两个子线程在互相抢占CPU的执行权public void run() {for(int x = 0 ; x <200; x ++){//System.out.println(x);System.out.println(getName() +":"+x);}}}


4.public final int getPriority()返回线程的优先级。 
  线程的优先级有哪些?
   默认优先级:5
  java.lang.Thread 
  public static final int MAX_PRIORITY 10  :最大优先级优先级大的抢占到CPU的执行权大,并不代表就一定能抢到,因为线程的执行具有随机性!
  public static final int MIN_PRIORITY 1   :最小优先级
  public static final int NORM_PRIORITY 5  :默认优先级

例子:

public class ThreadProrityDemo {public static void main(String[] args) {//创建该线程的实例//并且设置线程名称MyPriority mp1 = new MyPriority() ;MyPriority mp2 = new MyPriority() ;MyPriority mp3 = new MyPriority() ;//设置线程名称:无参构造+setName()方法////获取线程的优先级//System.out.println(mp1.getPriority());//5//System.out.println(mp2.getPriority());//5//System.out.println(mp3.getPriority());//5mp1.setName("张飞") ;mp2.setName("刘备") ;mp3.setName("关羽") ;//给线程设置优先级mp1.setPriority(10) ;mp2.setPriority(1) ;//启动线程mp1.start() ;mp2.start() ;mp3.start() ;}}

public class MyPriority extends Thread {@Overridepublic void run() {for(int x = 0 ; x <100 ; x ++){System.out.println(getName()+":"+x);}}}

5.public static void yield()暂停当前正在执行的线程对象,并执行其他线程。
暂停当前线程执行其他线程,并不保证另一个线程就一定能抢占到CPU的执行权

例子:

public class YieldThreadDemo {public static void main(String[] args) {//创建线程类对象YieldThread yt1 = new YieldThread() ;YieldThread yt2 = new YieldThread() ;//设置线程名称yt1.setName("高圆圆") ;yt2.setName("赵又廷") ;//启动线程yt1.start() ;yt2.start() ;}}

public class YieldThread extends Thread {@Overridepublic void run() {//yt1,yt2for(int x =0 ; x <100 ; x ++){System.out.println(getName()+":"+x);Thread.yield() ;}}}

6.public final void join()
                throws InterruptedException等待该线程终止。

例子:

public class JoinDemo {public static void main(String[] args) {/* * 创建该线程的实例对象 * */JoinThread jt1 = new JoinThread() ;JoinThread jt2 = new JoinThread() ;JoinThread jt3 = new JoinThread() ;//设置线程名称jt1.setName("李世民") ;jt2.setName("李元霸") ;jt3.setName("李渊") ;//启动线程jt1.start() ;//设置线程等待该线程终止该方法必须要启动线程try {jt1.join() ;} catch (InterruptedException e) {//InterruptedException:中断异常e.printStackTrace();}jt2.start() ;jt3.start() ;}}

public class JoinThread extends Thread {@Overridepublic void run() {//jt1,jt2,jt3for(int x = 0 ; x <100 ; x ++){System.out.println(getName()+":"+x);//李世民:0,,,,50,jt2进来了}}}

7.public final void setDaemon(boolean on)  on指定true,就是设置守护线程...
  将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。 
该方法必须在启动线程前调用。

jvm自动退出,对于主线程的数据如果直接输出完毕,对于两个守护线程来说不会立即消失,Jvm等会就自动退出.

比如:坦克大战

例子:

public class ThreadDemaonDemo {public static void main(String[] args) {//创建线程类对象ThreadDeamon td1 = new ThreadDeamon() ;ThreadDeamon td2 = new ThreadDeamon() ;//设置线程名称td1.setName("张飞") ;td2.setName("关羽") ;//setDaemon(boolean on)td1.setDaemon(true) ;td2.setDaemon(true) ;//启动线程td1.start() ;td2.start() ;Thread.currentThread().setName("刘备") ;for(int x = 0 ; x <5 ; x++){System.out.println(Thread.currentThread().getName()+":"+x); //设置主线程}}}
public class ThreadDeamon extends Thread {@Overridepublic void run() {for(int x = 0 ; x <100 ; x ++){System.out.println(getName()+":"+x);}}}

8.线程睡眠:
    public static void sleep(long millis)
                  throws InterruptedException在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)

例子:

public class ThreadSleepDemo {public static void main(String[] args) {//创建线程类对象ThreadSleep ts1 = new ThreadSleep() ;ThreadSleep ts2 = new ThreadSleep() ;ThreadSleep ts3 = new ThreadSleep() ;//设置线程名称ts1.setName("线程1") ;ts2.setName("线程2") ;ts3.setName("线程3") ;//启动线程ts1.start() ;ts2.start() ;ts3.start() ;}}

public class ThreadSleep extends Thread {@Overridepublic void run() {//ts1,ts2,ts3for(int x = 0 ; x <100 ; x ++){System.out.println(getName()+":"+x+",日期:"+new Date());//睡眠1秒中:1000毫秒try {Thread.sleep(1000) ;//此方法本身就抛出一个异常!} catch (InterruptedException e) {e.printStackTrace();}}}}

9.线程停止:
   public final void stop():强迫线程停止执行
   public void interrupt()中断线程。 表示中断线程一种状态
  

例子:

public class ThreadStopDemo {public static void main(String[] args) {//创建线程类对象ThreadStop ts = new ThreadStop() ;ts.start() ;//如果3秒中不醒来,干掉它try {Thread.sleep(3000) ;//ts.stop() ;//该方法表示过时,但是可以用它//强迫线程停止执行ts.interrupt() ;//中断一种状态} catch (InterruptedException e) {e.printStackTrace();}}}

public class ThreadStop extends Thread {@Overridepublic void run() {System.out.println("开始执行了.."+new Date());//子线程进来之后,让它睡眠10秒中try {Thread.sleep(10000) ;} catch (InterruptedException e) {//e.printStackTrace();System.out.println("线程终止了...");}System.out.println("结束执行"+new Date());}}

10.多线程实现的第二种方式:(实际开发中第二种比第一种应用更广泛)
   开发步骤:
  1)自定义一个类MyRunnable,该类实现Runnable接口
   2)实现该接口中的run()方法
   3)在主线程中创建该类的实例对象,
  4)创建Thread类对象,将3)创建的这个对象作为参数进行传递
   5)分别启动线程
  
  
  面试题:多线程的实现方式有几种,分别是什么?
   答三个方式,分别阐述
   第三种:和线程池有关系(并且和Callable接口有关系)
 

  如果这个题口头问你,那么直说两种可以:继承Thread类,实现Runnable接口

public class ThreadDemo {public static void main(String[] args) {//1)创建MyRunnable实例对象MyRunnable my = new MyRunnable() ;//2)创建线程类对象//Thread线程类中的构造方法//public Thread(Runnable target)//Thread t1 = new Thread(my) ;//t1.setName("线程1") ;//public Thread(Runnable target,String name)Thread t1 = new Thread(my, "线程1") ;Thread t2 = new Thread(my, "线程2") ;//启动线程t1.start() ;t2.start() ;}}
public class MyRunnable implements Runnable {@Override//run()中的方法也是需要一些耗时的操作public void run() {for(int x = 0 ; x < 100 ; x ++){//在控制台,打印每一个子线程的线程名称//System.out.println(getName()+":"+x);//getName()是Thread类中的方法//间接的使用Thread类的静态功能得到线程名称System.out.println(Thread.currentThread().getName()+":"+x);}}}



课堂练习:

 某电影院目前正在上映贺岁大片(红高粱,少林寺传奇藏经阁),共有100张票,而它有3个售票窗口售票,请设计一个程序
模拟该电影院售票。

public class SellTicketDemo {public static void main(String[] args) {//创建线程类对象SellTicket st1 = new SellTicket() ;SellTicket st2 = new SellTicket() ;SellTicket st3 = new SellTicket() ;//分别命名三个窗口st1.setName("窗口1") ;st2.setName("窗口2") ;st3.setName("窗口3") ;//启动线程st1.start() ;st2.start() ;st3.start() ;}}

public class SellTicket extends Thread {//定义100张票//private int tickets = 100 ;//这100张票应该被三个线程共用,所以用static修饰private static int tickets = 100 ;@Overridepublic void run() {//st1,st2,st3都要执行run()方法中代码//有100张票//int tickets = 100 ;(将这个代码放到run()方法的外面)//模拟该电影院售票:电影院一直有票while(true){if(tickets>0){System.out.println(getName() + "正在出售第" + (tickets--) + "张票");}}}}


接口:自定义类实现Runnable接口(研究主要接口的形式)

  为了模拟电影院卖票更真实的场景,每一个窗口卖票应该延迟操作
   在接口自实现类中,在run()方法中让每一个线程执行睡眠0.1秒
  
 加入延迟操作:
  1)一张票可能被卖多次
  100张票会出现多次
  CPU的执行具有原子性操作
  2)可能出现负票
  1,0,-1
  延迟操作和线程随机性导致
 
 
 这两种情况都属于线程安全问题,现在写的这个代码是一种有问题的代码?
  如何解决多线程的安全问题呢?
  检验多线程安全问题的标准(以后在判断一个多线程序是否有安全问题的标准)
  1)当前是否是一个多线程环境
  2)多线程环境中是否有共享数据
  3)是否有多条语句对共享数据进行操作
 
 就目前这个电影院卖票这个案例,
  1)是多线程环境,因为要实现3个窗口同时卖票
  2)有共享数据,比如:tickets
  3)有多条语句对当前这个共享数据进行操作
 
 优化改进:
  1)多线程环境和共享数据改变不了,突破口就是3)条件:将多条语句对共享数据的操作进行更改
 
  将多条语句对共享数据进行操作的代码用代码块包起来
  使用同步代码块:synchronized(同步锁对象){
  多条语句对共享数据的操作;
  }


public class SellTicketDemo {public static void main(String[] args) {//创建SellTicket对象SellTicket st = new SellTicket() ;//创建线程类的对象Thread t1 = new Thread(st, "窗口1") ;Thread t2 = new Thread(st, "窗口2") ;Thread t3 = new Thread(st, "窗口3") ;//启动线程t1.start() ;t2.start() ;t3.start() ;}}

public class SellTicket implements Runnable {//100张票被共用,不让外界修改数据private  static int tickets = 100 ;//@Override//public void run() {////// 模拟电影院一直有票//while (true) {//////设置线程睡眠0.1m////if (tickets > 0) {////ts1,ts2,ts3//try {//Thread.sleep(100) ;//} catch (InterruptedException e) {//e.printStackTrace();//}//System.out.println(Thread.currentThread().getName() + "正在出售第"//+ (tickets--) + "张票");//}//}//}//一张票可能会被卖多次,(加入延迟操作之后)//@Override//public void run() {////// 模拟电影院一直有票//while (true) {//////设置线程睡眠0.1m////if (tickets > 0) {////ts1,ts2,ts3//try {//Thread.sleep(100) ;//} catch (InterruptedException e) {//e.printStackTrace();//}//System.out.println(Thread.currentThread().getName() + "正在出售第"//+ (tickets--) + "张票");//}/** * 理想状态: * 窗口1正在出售第100张票 *  窗口2正在出售第99张票 *  ... *   *  多个线程在抢占CPU的执行权,CPU的一点点时间片具有特性:原子性操作(最简单最基本):比如变量++或者-- *  窗口1正在出售100张票 *  ticketss--,之前的动作:记录之前的值100(窗口2) *  窗口2在记录值的时候抢占cPU的执行权,打印-->窗口也正在出售第100张票 *  -->99 *///}@Overridepublic void run() {// 模拟电影院一直有票while (true) {//设置线程睡眠0.1m//ts1,ts2,ts3try {Thread.sleep(100) ;} catch (InterruptedException e) {e.printStackTrace();}if (tickets > 0) {System.out.println(Thread.currentThread().getName() + "正在出售第"+ (tickets--) + "张票");}/** * 当前代码出现了负票 * 理想状态: * 窗口1正在出售第3张票,tickets-- * 窗口3正在出售第2张票,tickets-- * 窗口2正在出售第1张票.... *  * 加入延迟操作后,线程执行具有随机性(延迟操作和随机性) * 窗口2正在出售第0张票..tickets-- * 窗口3正在出售第-1张票... */}}}

同步锁定对象:
   1)可以Object类型以及任意的Java类型对象
   2)如果一个方法进来之后是一个同步代码块,那么同步代码块可以演变成一个同步方法
   3)如果是一个静态的同步方法,锁对象是当前类名class属性:类名.class (反射机制:获取一些类的字节码文件对象Class类对象)
  
  
   面试题:思考:
   wait()线程等待,notify(),唤醒单个线程,notifyAll():唤醒所有线程这三个方法为什么不定义到Thread类中呢
  而是定义在Object类中?
线程中会存在安全问题,并且解决线程安全问题使用的同步代码块或者同步方法来解决,同步代码块来解决线程安全问题,就存在
  同步锁对象,谁能代表同步锁对象(Object以及任意的Java类),把它定义到Object类中;
  

  等待唤醒机制(生产者和消费者线程)

public class SellTicketDemo {public static void main(String[] args) {//创建SellTicket对象SellTicket st = new SellTicket() ;//创建线程类的对象Thread t1 = new Thread(st, "窗口1") ;Thread t2 = new Thread(st, "窗口2") ;Thread t3 = new Thread(st, "窗口3") ;//启动线程t1.start() ;t2.start() ;t3.start() ;}}
public class SellTicket implements Runnable {//100张票被共用,不让外界修改数据private  static int tickets = 100 ;//同步锁对象private Object obj = new Object() ;//任意的类的对象private Demo d = new Demo() ;private int x ;//设计模式:单例模式:懒汉式就和线程安全问题有关系//Object类型对象//@Override//public void run() {////// 模拟电影院一直有票//////t1,t2,t3子线程要执行循环语句中的内容//while (true) {//////t1线程先抢占CPU的执行权////理解为门的开和关////t1,t2,t3继续抢占CPU的执行权////t2抢占到了,门一关,t1,t3进不来了//synchronized(obj){//t1进来,门关了,t2和t3线程都不会进来////if (tickets > 0) {//try {//Thread.sleep(100) ;//} catch (InterruptedException e) {//e.printStackTrace();//}//System.out.println(Thread.currentThread().getName() + "正在出售第"//+ (tickets--) + "张票");////窗口1正在出售第100张////窗口正在出售第99张//}//}//代码执行完毕,t1线程出来,意味着门开了...  //////代码执行完毕,t1线程出来,意味着门开了...  //////}//}//同步锁对象:任意的自定义类的对象//@Override//public void run() {////// 模拟电影院一直有票//////t1,t2,t3子线程要执行循环语句中的内容//while (true) {//////t1线程先抢占CPU的执行权////理解为门的开和关////t1,t2,t3继续抢占CPU的执行权////t2抢占到了,门一关,t1,t3进不来了//synchronized(d){//t1进来,门关了,t2和t3线程都不会进来////if (tickets > 0) {//try {//Thread.sleep(100) ;//} catch (InterruptedException e) {//e.printStackTrace();//}//System.out.println(Thread.currentThread().getName() + "正在出售第"//+ (tickets--) + "张票");////窗口1正在出售第100张////窗口正在出售第99张//}//}//代码执行完毕,t1线程出来,意味着门开了...  //////代码执行完毕,t1线程出来,意味着门开了...  //////}//}@Overridepublic void run() {// 模拟电影院一直有票//t1,t2,t3子线程要执行循环语句中的内容while (true) {if(x%2==0){synchronized(SellTicket.class){//静态同步方法:要通过反射获取Class类对象(当前类的字节码文件对象)if (tickets > 0) {try {Thread.sleep(100) ;} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "正在出售第"+ (tickets--) + "张票");//窗口1正在出售第100张//窗口正在出售第99张}}}else{sellTicket() ;}x ++ ;}}//如果一个方法(非静态的方法)进来是一个同步代码块,把这个方法可不可以定义为一个同步方法呢?//private void sellTicket() {//synchronized (d) {////if (tickets > 0) {//try {//Thread.sleep(100);//} catch (InterruptedException e) {//e.printStackTrace();//}//System.out.println(Thread.currentThread().getName()//+ "正在出售第" + (tickets--) + "张票");////}//}//}//同步方法 (非静态的同步方法)//对于同步方法来说,它的同步锁对象是this//public synchronized void sellTicket(){//if (tickets > 0) {//try {//Thread.sleep(100);//} catch (InterruptedException e) {//e.printStackTrace();//}//System.out.println(Thread.currentThread().getName()//+ "正在出售第" + (tickets--) + "张票");////}//}//如果是静态同步方法,那么同步锁对象是谁呢?public static synchronized void sellTicket(){if (tickets > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+ "正在出售第" + (tickets--) + "张票");}}}class Demo{}



原创粉丝点击