java基础学习(十)


一、进程和线程

1.进程:

  • 正在运行的程序
  • 是系统进行资源分配和调用的独立单位
  • 每一个进程都有他自己的内存空间和系统资源
  • 进程指当前正在执行的程序,代表一个应用程序在内存中的执行区域

2.线程:

  • 是进程中单个顺序控制流,是一条执行路径
  • 是进程中的一个执行控制单元,执行路径。
  • 单线程:一个进程如果只有一条执行路径,则称为单线程
  • 多线程:一个进程如果有多个执行路径,则称为多线程

注意::进程只是负责开辟内存空间的,线程才是负责执行代码逻辑的执行单元。

二、Thread类

  • 线程 是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。

1.方式一实现多线程

  • 步骤:继承Thread类
               定义一个类继承Thread类
               在类中重写run方法
               创建类的对象
               启动线程

代码演示

public class My_Thread extends Thread{
    
    
    @Override
    public void run() {
    
    
        System.out.println("这是一个线程");
    }
}
public class ThreadTest {
    
    
    public static void main(String[] args) {
    
    
        My_Thread my_thread=new My_Thread();
        my_thread.start();
    }
}

运行结果

  • 为什么要重写run方法?
    因为run方法用来封装被线程执行的代码
  • run和start的区别?
    run():封装线程执行的代码,直接调用,相当于普通方法
    start():启动线程,然后由JVM调用此线程的run()方法
  • 线程的随机性:
    在某一个时刻,CPU只能执行一个程序,所以多个程序同时执行并不是真正的同时执行。其实就是CPU在做着快速的切换完成的。只是你感觉上同时而已。这样就造成打印的数据不规律。

2.设置和获取线程的名称

  • Thread中设置和获取线程名称的方法
    • void SetName(String name):将线程的名称设置为name
    • String getName():返回次线程的名称
    • Thread(String name):构造函数,线程对象一建立就可以指定名称
    • static Thread currentThread():获取当前线程对象

代码演示

public class MyThread extends Thread {
    
    

    public MyThread(String name) {
    
    
        super(name);
    }

    public MyThread() {
    
    
    }

    @Override
    public void run() {
    
    
        for (int i = 1; i < 100; i++) {
    
    
            System.out.println(Thread.currentThread().getName()+"======="+i);
        }
    }
}
public class MyThreadTest {
    
    
    public static void main(String[] args) {
    
    
        MyThread myThread1=new MyThread("ls");
        MyThread myThread2=new MyThread();
        MyThread mythread3=new MyThread();
        System.out.println(myThread1.getName());
        myThread2.setName("zs");
        System.out.println(myThread2.getName());
        System.out.println(mythread3.getName());
        myThread1.start();
        myThread2.start();
        mythread3.start();

    }
}

交叉输出直到99(后续输出未截取)
在这里插入图片描述

3.线程调度

1)分时调度

  • 分时调度模型:
    所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间

2)抢占调度

  • 抢占调度模型:优先让优先级高的线程使用CPU,如果线程优先级相同,会进行随机,优先级高的线程获取CPU的时间相对较多

3)优先级

  • Java使用的是抢占模式
  • Thread设置获取线程优先级的方法
  • int getPriority():返回此线程的优先级
  • void setPriority():更改此线程的优先级
  • 线程默认优先级为5,最大优先级为10,最小优先级为1
    优先级高仅仅表示了抢占CPU的概率大,在次数多的时候效果明显,并不是绝对

代码演示

public class Threadpriority extends Thread {
    
    
    @Override
    public void run() {
    
    
        for(int i=0;i<20;i++) {
    
    
            System.out.println(Thread.currentThread().getName()+"========"+i);
        }
    }
}
public class Test {
    
    
    public static void main(String[] args) {
    
    
        Threadpriority th1=new Threadpriority();
        Threadpriority th2=new Threadpriority();
        th1.setName("张三");
        th2.setName("李四");
        //只能给1-10,否者报错
        th1.setPriority(2);
        th2.setPriority(10);
        th1.start();
        th2.start();
    }
}

在这里插入图片描述
注意:优先级只是说抢占的可能性大点,并不是绝对的

4.线程控制

在这里插入图片描述

1)sleep(long millis)

代码演示

public class MyThread extends Thread {
    
    
    @Override
    public void run() {
    
    
        //乌龟跑完需要100秒
        for(int i=0;i<100;i++){
    
    
            System.out.println(Thread.currentThread().getName()+"--------"+i);
        }
    }
}
public class YourThread extends Thread {
    
    
    @Override
    public void run() {
    
    
        //兔子跑完需要50秒
        for(int i=0;i<50;i++){
    
    
            System.out.println(Thread.currentThread().getName()+"-------"+i);
            if(i==20){
    
    
                try {
    
    
                    sleep(200);//单位为毫秒
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
        }
    }
}
//需求:让乌龟赢
public class Test {
    
    
    public static void main(String[] args) {
    
    
        MyThread myThread=new MyThread();
        myThread.setName("乌龟");
        YourThread yourThread=new YourThread();
        yourThread.setName("兔子");
        myThread.start();
        yourThread.start();
    }
}

2)join()

public class Mythread extends Thread{
    
    
    @Override
    public void run() {
    
    
        for(int i=0;i<20;i++){
    
    
            System.out.println(Thread.currentThread().getName()+"===="+i);
        }
    }
}
public class Test {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        Mythread t1=new Mythread();
        Mythread t2=new Mythread();
        Mythread t3=new Mythread();
        Mythread t4=new Mythread();
        //t1和t2是串行,先执行t1,等t1死亡后再执行t2
        t1.start();
        try {
    
    
            t1.join();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        t2.start();
        t2.join();
        //t2执行完后,开始并行执行t3和t4,交互抢占
        t3.start();
        t4.start();

    }
}

结果(截图太长,画图展示)
在这里插入图片描述

public class MyThread extends Thread {
    
    
    @Override
    public void run() {
    
    
        for (int i = 0; i < 20; i++) {
    
    
            System.out.println(Thread.currentThread().getName() +"=========="+ i);
            if(i==14) {
    
    
                try {
    
    
                    join();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
        }
    }
}
public class YourThread extends Thread{
    
    
    @Override
    public void run() {
    
    
        for(int i=0;i<20;i++){
    
    
            System.out.println(Thread.currentThread().getName()+"===="+i);
        }
    }
}
public class Test {
    
    
    public static void main(String[] args) {
    
    
        MyThread myThread=new MyThread();
        YourThread yourThread=new YourThread();
        myThread.setName("zs");
        yourThread.setName("ls");
        myThread.start();
        yourThread.start();
    }
}

zs在14的时候,调用join,等待线程结束,后续zs线程就结束了,故不会再输出
在这里插入图片描述

3)setDaemon(boolean on)

public class Thread1  extends Thread{
    
    
    @Override
    public void run() {
    
    
        for(int i=0;i<51;i++){
    
    
            System.out.println(Thread.currentThread().getName()+"======"+i);
            if(i==30){
    
    
                stop();
            }
        }
    }
}
public class Thread2  extends Thread{
    
    
    @Override
    public void run() {
    
    
        for(int i=0;i<51;i++){
    
    
            System.out.println(Thread.currentThread().getName()+"======="+i);
        }
    }
}
public class Thread3 extends Thread {
    
    
    @Override
    public void run() {
    
    
        for(int i=0;i<51;i++){
    
    
            System.out.println(Thread.currentThread().getName()+"========"+i);
        }
    }
}
public class Test {
    
    
    public static void main(String[] args) {
    
    
        Thread1 thread1=new Thread1();
        Thread2 thread2=new Thread2();
        Thread3 thread3=new Thread3();

//将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出
        thread2.setDaemon(true);//将2和3设为守护线程
        thread3.setDaemon(true);

        thread1.start();//使用默认线程名称thread_0
        thread2.start();//thread_1
        thread3.start();//thread_2
        //三个线程一起运行,当thread1运行到30时,线程停止,运行的线程都是守护线程,
        // java虚拟机退出,线程不再运行
    }
}

在这里插入图片描述
Thread_0到30时,守护线程Thread_1已经跑完,而Thread_2还没结束,但由于线程Thread_0停止,虚拟机将退出,thread_2尽可能的跑,但最终还是没跑完。

4)线程的生命周期

在这里插入图片描述

5)老人和小孩爬山案例

老人和小孩爬山,老人爬一百米,要休息1000ms,小孩爬一百米,要休息500ms,一共要爬1km,谁先爬到山顶,谁先爬完。

public class Mythread extends Thread{
    
    
    private int time;
    private int sum;
    private int m=0;

    public Mythread(String name,int time,int k){
    
    
        super(name);
        this.time=time;
        this.sum=(k*1000)/100;
    }

    @Override
    public void run() {
    
    
        while(sum>0) {
    
    
            m = m + 100;
            System.out.println(Thread.currentThread().getName() + "爬了" + m + "米,还剩" + (--sum) + "个100米");
            if(sum==0){
    
    
                System.out.println(Thread.currentThread().getName()+"爬完了");
            }
            try {
    
    
                sleep(time);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }
    }
}
public class Test {
    
    
    public static void main(String[] args) {
    
    
        Mythread th1=new Mythread("老人",1000,1);
        Mythread th2=new Mythread("小孩",500,1);
        th1.start();
        th2.start();

    }
}

在这里插入图片描述

5.方式二实现多线程

  • 步骤:实现Runnable接口
               定义一个类实现Runnable类
               在类中重写run方法
               创建类的对象
               创建Thread对象,把类的对象作为构造方法的参数
               启动线程
  • 多线程的实现方式:
               继承Thread类
               实现Runnable接口
  • 实现Runnable的好处
               避免了Java的单继承的局限性
               适合多个线程去同时处理同一个资源(继承Thread的方式不能实现资源共享)

代码演示

public class MyRunnable implements Runnable {
    
    
    int i=0;//为共享资源
    @Override
    public void run() {
    
    
        for(i=i;i<100;i++){
    
    
            System.out.println(Thread.currentThread().getName()+"======="+i);
        }
    }
}
public class Test {
    
    
    public static void main(String[] args) {
    
    
        MyRunnable my=new MyRunnable();
        Thread thread1=new Thread(my,"张三");
        Thread thread2=new Thread(my,"李四");
        thread1.start();
        thread2.start();
    }
}

在这里插入图片描述
两个线程共享一个变量,随机调用,当条件不满足时,不在输出

6.资源共享

  • 使用方式二实现线程共享资源时,会出现共享资源不同步的问题

    • 原因: 多线程的随机性导致(多线程每次多个语句操作共享数据时,因为多线程的随机性导致操作执行不完整(同一个操作,多个线程交互执行))
    • 如何判断出现数据安全问题
      是否是多线程
      是否有共享数据
      是否有多个语句操作共享数据
  • 如何解决数据安全问题

    • 每个线程多个语句操作共享数据时,必须执行完整
    • 多个语句操作共享数据给锁起来,让任意时刻只能有一个线程执行
    • 为了保护数据安全使用synchronized 关键字

7.synchronized

  • 如果直接实现Runnable,不加锁,会导致数据不安全,无法同步,案例参考方式二实现多线程代码,这里只给解决安全问题的例子

1)同步代码块解决数据安全问题

  • 格式:
    synchronized(任意对象){
    多条语句操作共享数据的代码
    }
  • synchronize(任意对象):给代码加上锁,任意对象就可以看做锁,一般对象给this,即当前对象。
  • 好处和弊端:
    好处:解决了数据安全问题
    弊端:线程很多时,每个线程都要执行synchronize,消耗资源,会降低执行效率

代码演示

public class MyRunnable implements Runnable{
    
    
    private int num=100;

    @Override
   public void run() {
    
    
        while(true) {
    
    
            synchronized (this) {
    
    
                if (num > 0) {
    
    
                    System.out.println(Thread.currentThread().getName() + "卖出一张票,还剩" + (num - 1) + "张票");
                    try {
    
    
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                    num--;
                }
                else {
    
    
                    break;
                }
            }


        }
    }
}
public class Test {
    
    
    public static void main(String[] args) {
    
    
        MyRunnable my=new MyRunnable();
        Thread thread1=new Thread(my,"窗口一");
        Thread thread2=new Thread(my,"窗口二");
        Thread thread3=new Thread(my,"窗口三");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

在这里插入图片描述

2)把synchronized关键字加在方法上

  • 把synchronized关键字加在方法上
  • 格式:
    修饰符 synchronized 返回值类型 方法名(参数){}
  • 同步方法的锁对象是什么?
    this

代码演示

public class MyTread implements Runnable{
    
    
    private int ticket=100;
    @Override
    public void run() {
    
    
        while(true){
    
    
            show();
        }
    }

    public synchronized void show(){
    
    
        if(ticket>0) {
    
    
            System.out.println(Thread.currentThread().getName() + "卖出第" + ticket + "张票" + "," + "还剩" + (--ticket) + "张");
        }
    }
}
public class Test {
    
    
    public static void main(String[] args) {
    
    
        MyTread my=new MyTread();
        Thread thread1=new Thread(my,"窗口一");
        Thread thread2=new Thread(my,"窗口二");
        Thread thread3=new Thread(my,"窗口三");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

在这里插入图片描述

3)同步静态方法

  • 同步静态方法

  • 格式:
    修饰符 static synchronized 返回值类型 方法名(参数){}

  • 同步静态方法锁对象是什么?
    类名.class

public class MyThread implements Runnable {
    
    
    private static int ticket=100;
    @Override
    public void run() {
    
    
        while(true){
    
    
            show();
        }
    }

    public static synchronized void show(){
    
    
        if(ticket>0){
    
    
            System.out.println(Thread.currentThread().getName()+"卖出第"+ticket+"张");
            ticket--;
        }
    }
}
public class Test {
    
    
    public static void main(String[] args) {
    
    
        MyThread my=new MyThread();
        Thread thread1=new Thread(my,"窗口一");
        Thread thread2=new Thread(my,"窗口二");
        Thread thread3=new Thread(my,"窗口三");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

在这里插入图片描述

4)混合锁

  • 注意:上的要是同一个锁,以下代码运行结果和上面相同都是100到1,结果不在演示

代码块锁和方法锁代码演示

public class MyRunnable implements Runnable{
    
    
    private int ticket=100;
    @Override
    public void run() {
    
    
        while (true) {
    
    
            synchronized (this) {
    
    
                if (ticket > 0) {
    
    
                    if ((ticket % 2) == 0) {
    
    
                        //代码块中锁的也是this,两个锁锁的对象要一致,否则不能保证数据安全

                        System.out.println(Thread.currentThread().getName() + "卖出第" + ticket + "张");
                        ticket--;
                    } else {
    
    
                        show();
                    }
                }
            }
            }
        }
    public synchronized void show(){
    
    //方法上上锁的是this
        System.out.println(Thread.currentThread().getName()+"卖出第"+ticket+"张");
        ticket--;
    }
}
public class Test {
    
    
    public static void main(String[] args) {
    
    
        MyRunnable my=new MyRunnable();
        Thread thread1=new Thread(my,"窗口一");
        Thread thread2=new Thread(my,"窗口二");
        Thread thread3=new Thread(my,"窗口三");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

代码块锁和静态synchronized演示

public class MyRunnable implements Runnable{
    
    
    private static int ticket=100;
    @Override
    public void run() {
    
    
        while (true) {
    
    
            synchronized (MyRunnable.class) {
    
    
                if (ticket > 0) {
    
    
                    if ((ticket % 2) == 0) {
    
    
                        //代码块中锁的也是this,两个锁锁的对象要一致,否则不能保证数据安全

                        System.out.println(Thread.currentThread().getName() + "卖出第" + ticket + "张");
                        ticket--;
                    } else {
    
    
                        show();
                    }
                }
            }
            }
        }
    public static synchronized void show(){
    
    //方法上上锁的是this
        System.out.println(Thread.currentThread().getName()+"卖出第"+ticket+"张");
        ticket--;
    }
}
public class Test {
    
    
    public static void main(String[] args) {
    
    
        MyRunnable my=new MyRunnable();
        Thread thread1=new Thread(my,"窗口一");
        Thread thread2=new Thread(my,"窗口二");
        Thread thread3=new Thread(my,"窗口三");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

三、总结

进程:正在运行的程序,只负责申请资源
线程:是进程最小执行单位,流程控制

继承Thread来实现多线程
	1.创建类继承Thread
	2.重写run()方法
	3.new实例
	4.调用start启动线程
	run()的作用:封装线程要执行的代码,由jvm去调用
	run和start的区别:
		run:直接调用,就是普通方法,按顺序执行
		start:启动线程,由jvm去调用run
		注意:如果是单线程使用结果和run没有区别,如果是多线程,执行结果不能保证(因为线程的随机性)
	线程的随机性:在某一时刻(时间非常非常短),一个CPU执行线程时,只会执行一个,那么运行多个程序时,CPU会交叉执行每个程序中的线程
		因为切换线程的时候时间非常短,看做了同时执行
	线程的优先级:默认为5 最大为10 最小为1
		int getPriority():返回此线程的优先级
		void setPriority():更改此线程的优先级
		注意:优先级高的占用CPU资源的可能性大,但是不是绝对的,只是提供了一个可能性
	线程的名称:默认是Thread-0从0开始依次加1
		void SetName(String name):将线程的名称设置为name
		String getName():返回次线程的名称
		Thread(String name):构造函数,线程对象一建立就可以指定名称
		static Thread currentThread():获取当前线程对象
	线程的控制:
		void sleep(long millis)	使当前正在执行的线程暂停指定的毫秒数
		void join()	等待这个线程的死亡
		void setDaemon(boolean on)	将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出
		注意:join()通常作为,把并行改为串行的操作,如果让一个线程进入死亡状态可以选择stop()
实现多线程的方式2:实现Runnable
	1.创建类实现Runnable接口
	2.重写run()
	3.创建类的对象
	4.创建线程对象,构造中参数为类的对象
	5.start
实现Runnable的好处:
	1.避免了单继承
	2.可以进行资源共享
为什么可以进行资源共享?
	方式2实现多线程的时候,多个线程共同使用同一个类对象,同一个类对象中的资源只会定义一次
会有数据安全问题
	1.多个线程操作同一个数据多次(相同的票数)
	2.共享数据超出范围(负数票)
	原因:因为线程的随机性,导致每个线程操作数据时,操作执行不完整
通过锁synchronized解决数据安全问题
	1.同步代码块:
		格式:synchronized(任意对象){
				操作数据的代码
			}
	2.同步方法:
		格式:修饰符 synchronized 返回值数据类型 方法名(参数){}
	3.同步静态方法:
		格式:修饰符 static synchronized 返回值数据类型 方法名(参数){}
	注意:
		三种锁的方式,分别锁的对象
			同步代码块可以给任意对象,通常使用this
			 同步方法默认是this
			 同步静态方法默认是类名.class	

四、习题

(一)填空题

1.处于运行状态的线程在某些情况下,如执行了sleep(睡眠)方法,或等待
I/O设备等资源,将让出CPU并暂时停止自己的运行,进入___阻塞______状
态。 

2.处于新建状态的线程被启动后,将进入线程队列排队等待CPU,此时它已
具备了运行条件,一旦轮到享用CPU资源就可以获得执行机会。上述线程是
处于     就绪         状态。

3.一个正在执行的线程可能被人为地中断,让出CPU的使用权,暂时中止自
己的执行,进入     阻塞     状态。

4.在Java中编写实现多线程应用有两种途径:一种是继承Thread类创建线
程,另一种是实现    Runnable        接口创建线程。

5.在线程控制中,可以调用_____join()_____方法,阻塞当前正在执行的线
程,等插队线程执行完后后再执行阻塞线程。

6.多线程访问某个共享资源可能出现线程安全问题,此时可以使用__ 
synchronized_____________关键字来实现线程同步,从而避免安全问题出
现,但会影响性能,甚至出现死锁。

7. 在线程通信中,调用wait( )可以是当前线程处于等待状态,而为了唤醒一
个等待的线程,需要调用的方法是____notofy()________。

8.在线程通信中,可以调用wait()、notify()、notifyAll()三个方法实现线程通
信,这三个方法都是______Object______类提供的public方法,所以任何类
都具有这三个方法。 

(二)选择题

1.	下列关于Java线程的说法正确的是(  A )。(选择一项)
		
	A	每一个Java线程可以看成由代码、一个真实的CPU以及数据三部分组成
	B.	创建线程的两种方法中,从Thread类中继承方式可以防止出现多父类的问题
	C.	Thread类属于java.util程序包
	D.	使用new Thread(new X()).run();方法启动一个线程

2.	以下选项中可以填写到横线处,让代码正确编译和运行的是(   A)。(选择一项)
	public class Test implements Runnable {
	public static void main(String[] args) {
		___________________________________
		t.start();
		System.out.println("main");
	}
	public void run() {
		System.out.println("thread1!");
	}
}
		
	A.	Thread t = new Thread(new Test());
	B.	Test t = new Test();
	C.	Thread t = new Test();		
	D.	Thread t = new Thread();

3.	如下代码创建一个新线程并启动线程,问:四个选项中可以保证正确代码创建target对象,并能编译正确的是( C     )?(选择一项)
	public static void main(String[] args) {
	 Runnable target=new MyRunnable( );  
	 Thread myThread=new Thread(target); 
}
		
	A	public class MyRunnable extends Runnable {
	public void run( ) {

	}
}
	B.	public class MyRunnable extends Runnable {
	void run( ) {

	}
}
	C.	public class MyRunnable  implements Runnable  {
	public void run( ) {

	}
}
	D.	public class MyRunnable  implements Runnable  {
	void run( ) {

}
}

4.	当线程调用start( )后,其所处状态为(  C  )。(选择一项)
		
	A	阻塞状态
	B.	运行状态
	C.	就绪状态
	D.	新建状态

5.	下列关于Thread类提供的线程控制方法的说法中,错误的是( c  )。(选择一项)
		
	A	线程A中执行线程B的join()方法,则线程A等待直到B执行完成
	B.	线程A通过调用interrupt()方法来中断其阻塞状态
	C.	若线程A调用方法isAlive()返回值为false,则说明A正在执行中,也可能是可运行状态
	D.	currentThread()方法返回当前线程的引用

6.	下列关于线程的优先级说法中,正确的是( BC )。(选择两项)
		
	A	线程的优先级是不能改变的
	B.	线程的优先级是在创建线程时设置的
	C.	在创建线程后的任何时候都可以重新设置
	D.	线程的优先级的范围在1-100之间

7.	以下选项中关于Java中线程控制方法的说法正确的是(  AD )。(选择二项)
		
	A.	join ( ) 的作用是阻塞指定线程等到另一个线程完成以后再继续执行
	B.	sleep ( ) 的作用是让当前正在执行线程暂停,线程将转入就绪状态
	C.	yield ( ) 的作用是使线程停止运行一段时间,将处于阻塞状态
	D.	setDaemon( )的作用是将指定的线程设置成后台线程

8.	在多个线程访问同一个资源时,可以使用( A  )关键字来实现线程同步,保证对资源安全访问。(选择一项)
		
	A.	synchronized
	B.	transient
	C.	static
	D.	yield

9.	Java中线程安全问题是通过关键字( C  )解决的?。(选择一项)
		
	A.	finally
	B.	wait( )
	C.	synchronized
	D.	notify( )
10.	以下说法中关于线程通信的说法错误的是( D   )?。(选择一项)
		
	A.	可以调用wait()、notify()、notifyAll()三个方法实现线程通信
	B.	wait()、notify()、notifyAll()必须在synchronized方法或者代码块中使用
	C.	wait()有多个重载的方法,可以指定等待的时间
	D.	wait()、notify()、notifyAll()是Object类提供的final方法,子类可以重写

(三)判断题

1.进程是线程Thread内部的一个执行单元,它是程序中一个单一顺序控制流
程。( X    )

2.一个进程可以包括多个线程。两者的一个主要区别是:线程是资源分配的
单位,而进程CPU调度和执行的单位。(  X )

3.用new关键字建立一个线程对象后,该线程对象就处于新生状态。处于新生
状态的线程有自己的内存空间,通过调用start进入就绪状态。(   T  )

4.线程可以用yield使低优先级的线程运行。(   X )

5.Thread.sleep( )方法调用后,当等待时间未到,该线程所处状态为阻塞状
态。当等待时间已到,该线程所处状态为运行状态。(  X  )

6.当一个线程进入一个对象的一个synchronized方法后,其它线程不可以再
进入该对象同步的其它方法执行。(  X  )

(四)简答题

1.简述进程和线程的联系和区别。
进程是系统分配的最小单位,线程是系统调度的最小单位,且进程有独立的空间,而线程是共享堆栈,数据,代码段,而进程只是共享代码段。
进程:正在运行的程序,只负责申请资源
线程:是进程最小执行单位,用于流程控制

2.创建线程的两种方式分别是什么?各有什么优缺点。
方式1:继承Java.lang.Thread类,并覆盖run() 方法。
优势:编写简单;
劣势:单继承的限制----无法继承其它父类,同时不能实现资源共享。
方式2:实现Java.lang.Runnable接口,并实现run()方法。
优势:可继承其它类,多线程可共享同一个Thread对象;
劣势:编程方式稍微复杂,如需访问当前线程,需调用Thread.currentThread()方法

3.请你简述sleep( )和wait( )有什么区别
对于sleep()方法,我们首先要知道该方法是属于Thread类中的。
而wait()方法,则是属于Object类中的。
sleep()方法导致了程序暂停执行指定的时间,让出cpu给其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。在调用sleep()方法的过程中,线程不会释放对象锁。
而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态

4.Java中实现线程通信的三个方法及其作用
答:过对一个线程调用wait()函数后,线程就进入停滞状态,只有当两次对该线程调用notify或notifyAll后它才能两次回到可执行状态
wait():导致当前线程等待,直到其他线程调用该同步监视器的notify()方法或notifyAll()方法来唤醒该线程。
notify():唤醒在此同步监视器上等待的单个线程。
notifyAll():唤醒在此同步监视器上等待的所有线程。

(五)编程题

1. 设计一个多线程的程序如下:设计一个火车售票模拟程序。假如火车站要有100张火车票要卖出,现在有5个售票点同时售票,用5个线程模拟这5个售票点的售票情况。

public class Mythread  implements Runnable{
    
    
    private int ticket=100;
    @Override
    public void run() {
    
    
        while(true){
    
    
            synchronized (this){
    
    
                if(ticket>0){
    
    
                    System.out.println(Thread.currentThread().getName()+"===第"+ticket+"张票卖出");
                    ticket--;
                }
            }
        }

    }
}
public class Test {
    
    
    public static void main(String[] args) {
    
    
        Mythread my=new Mythread();
        Thread thread1=new Thread(my,"售票点1");
        Thread thread2=new Thread(my,"售票点2");
        Thread thread3=new Thread(my,"售票点3");
        Thread thread4=new Thread(my,"售票点4");
        Thread thread5=new Thread(my,"售票点5");
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
        thread5.start();
    }
}

在这里插入图片描述
2. 编写两个线程,一个线程打印1-52的整数,另一个线程打印字母A-Z。打印顺序为12A34B56C….5152Z。即按照整数和字母的顺序从小到大打印,并且每打印两个整数后,打印一个字母,交替循环打印,直到打印到整数52和字母Z结束

public class Printer{
    
    
    private int index=1;

   public synchronized void print(int i){
    
    
        if(index%3==0){
    
    
            try {
    
    
                wait();
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }

        }

            System.out.print(i);
            index++;
            notifyAll();
        }
        
    public synchronized void print(char c){
    
    
        if(index%3!=0){
    
    
            try {
    
    
                wait();
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }

        System.out.print(c);
        index++;
        notifyAll();
    }

}
public class NumberPrinter extends Thread {
    
    
    private Printer p;

    public NumberPrinter(){
    
    }

    public NumberPrinter(Printer p){
    
    
        this.p=p;
    }

    @Override
    public void run() {
    
    
        for(int i=1;i<=52;i++){
    
    
            p.print(i);
        }
    }
}

public class LetterPrinter extends Thread{
    
    
    private Printer p;

    public LetterPrinter(){
    
    }

    public LetterPrinter(Printer p){
    
    
        this.p=p;
    }

    @Override
    public void run() {
    
    
        for (char c = 'A'; c <='Z'; c++) {
    
    
            p.print(c);
        }

    }
}
public class Test {
    
    
    public static void main(String[] args) {
    
    
        Mycount mycount = new Mycount();
        for(int i=0;i<2;i++){
    
    
            Thread thread=new Thread(new Threaddec(mycount));
            Thread thread1=new Thread(new Threadsum(mycount));
            thread.start();
            thread1.start();
        }

    }
}

在这里插入图片描述
在这里插入图片描述
3.设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。
要求:使用内部类实现线程,对j增减的时候不考虑顺序问题。

public class Mycount {
    
    
    private int j=50;

    public synchronized   void add(){
    
    
        j++;
        System.out.println("当前j的值为:"+j);
    }
    public synchronized void dec(){
    
    
        j--;
        System.out.println("当前j的值为"+j);
    }
    public void show(){
    
    
        System.out.println(j);
    }

}
public class Test {
    
    
    public static void main(String[] args) {
    
    
        Mycount mycount = new Mycount();
        Runnable add=new Runnable() {
    
    
            @Override
            public void run() {
    
    
                for(int x=0;x<10;x++) {
    
    
                    mycount.add();
                }
            }
        };
        Runnable dec=new Runnable() {
    
    
            @Override
            public void run() {
    
    
                for(int i=0;i<10;i++) {
    
    
                    mycount.dec();
                }
            }
        };
        for(int i=1;i<=2;i++){
    
    
            new Thread(add).start();
            new Thread(dec).start();
        }
        mycount.show();

    }
}

在这里插入图片描述

4.编写多线程程序,模拟多个人通过一个山洞的模拟。这个山洞每次只能通过一个人,每个人通过山洞的时间为5秒,有10个人同时准备过此山洞,显示每次通过山洞人的姓名和顺序。

public class Sort implements Runnable {
    
    
    private int i=1;

    public synchronized void show(){
    
    
        System.out.println(Thread.currentThread().getName()+"===="+i);
        try {
    
    
            Thread.sleep(500);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        i++;
    }

    @Override
    public void run() {
    
    
        this.show();

    }
}
public class Test {
    
    
    public static void main(String[] args) {
    
    
        Sort sort=new Sort();
        ArrayList<Thread> a=new ArrayList<>();
        for(int i=0;i<10;i++){
    
    
            Thread thread=new Thread(sort,"路人"+i);
            a.add(thread);
        }
        for(Thread t:a){
    
    
            t.start();
        }
    }

}

在这里插入图片描述

java入门基础学习(一)
java入门基础学习(二)
java入门基础学习(三)
java入门基础学习(四)
java入门基础学习(五)
java入门基础学习(六)
java入门基础学习(七)
java入门基础学习(八)
java入门基础学习(九)
java进阶之常见对象(一)
java进阶之常见对象(二)
java进阶之冒泡排序
java进阶之选择排序
java进阶之面向对象(封装)
java进阶之面向对象(代码块、继承)
java进阶之面向对象(多态、抽象、接口)
java进阶之匿名内部类、访问修饰符、包

猜你喜欢

转载自blog.csdn.net/qq_45798550/article/details/108266991