实训笔记6.25

6.25

一、座右铭

我的故事你说,我的文字我落,我值几两你定,我去何方我挑。

二、知识回顾

2.1 JavaSE

2.1.1 基本语法

  1. 标识符、关键字、保留字
  2. 数据类型
  3. 变量、常量、字面量
  4. 运算符
  5. 控制流程

2.1.2 数组

2.1.3 JVM内存

2.1.4 面向对象

  1. 类和对象
  2. 三大特征
  3. 抽象类、抽象方法、接口
  4. 常见关键字:package、import、this、super、static、final、instanceof、enum

2.1.5 Java常用类

  1. 枚举类
  2. JavaBean类、lombok
  3. Object类
  4. 包装类
  5. String、StringBuffer、StringBuilder
  6. System类
  7. Arrays类
  8. 时间日期类
    1. Date
    2. SimpleDateFormat
    3. Calendar
  9. Math、Scanner、Random
  10. Java比较器

2.1.6 Java异常机制

2.1.7 Java泛型

2.1.8 Java集合

  1. Collection
    1. List
    2. Set
  2. Map

2.1.9 JavaIO流

  1. 四大基类

  2. 常用IO流

    1. 节点流
      1. 数组流
      2. 文件流
    2. 功能流
      1. 缓冲流
      2. 转换流
      3. 对象流
      4. 打印流
      5. 标准输入和输出流
  3. IO流使用完毕要注意关闭、IO流要配套使用

2.1.10 Java注解

注解的四大元注解

2.1.11 Java反射机制

  1. Class类
  2. 通过Class类去获取类中的组成成分
  3. 通过获取的类中成分去调用相对应的内容

2.1.12 Java多线程

2.1.13 Java网络编程

三、Java多线程

3.1 多线程的基本概念

3.1.1 程序、进程和线程

程序:没有运行起来的代码块

进程:运行起来的程序叫做进程

线程:线程是进程的组成单位,是资源分配的最小单位

3.1.2 单核CPU和多核CPU问题

CPU内核是用来运行线程的最小单位,CPU的一个内核在某一时刻只能运行一个线程。

但是我们电脑上线程的数量是远大于内核数的,也就意味着一个CPU内核同时运行几百个线程。 CPU的调度机制:时间片调度机制

3.1.3 并行和并发

并行:同一时刻多个线程同时运行

并发:同一时刻只能运行一个线程,多个线程抢占时间片运行,给人的感觉就好像是同时运行

3.2 Java多线程的问题

Java的JVM内存本身就是支持多线程运行的,也就意味着Java可以开发多线程程序的。JVM内存分为五部分,Java当中每启动一个线程,会给这个线程启动一个独立的虚拟机栈区和程序计数器,而堆区和元数据区对于多个线程是共享的。

3.3 Java当中如何创建和启动多线程类

1、借助Java中Thread类–Thread类是java.lang包的,是Java中所有多线程类的顶尖父类

2、借助Java中Runnable接口创建多线程

3、借助Java中的Callable接口创建多线程

4、借助Java中的线程池创建多线程

启动多线程只有一种方式:借助Thead线程类中的start方法来启动

启动多线程只有一种方式:
借助Thead线程类中的start方法来启动

代码示例:

/**
 * 线程的第一种创建和使用方式:
 *   1、继承Thread线程类,并且重写run方法---run方法中就是线程启动起来以后需要执行的业务逻辑
 *   2、创建一个这个类的实例对象,然后调用start方法启动多线程
 *   
 * 想要通过Java一个线程输出0-100以内的所有偶数,另外一个线程输出0-100以内的所有奇数
 * 
 * 一个Java程序,没有创建多线程的情况下,有几个线程?
 *     1、main线程
 *     2、垃圾回收线程
 *     3、异常处理线程
 *   如果我们要开启多线程,需要在main线程执行过程中开启另外一个线程
 * @author 11018
 *
 */
public class Demo01 {
    
    
	public static void main(String[] args) {
    
    
//		T t = new T();
//		t.start();
		Thread t = new Thread() {
    
    
			@Override
			public void run() {
    
    
				for (int i = 0; i <= 100; i++) {
    
    
					if(i % 2 == 0) {
    
    
						System.out.println("有一个偶数为:"+i);
					}
				}
			}
		};
		t.start();
		
		for (int i = 0; i <= 100; i++) {
    
    
			if(i % 2 == 1) {
    
    
				System.out.println("有一个奇数为:"+i);
			}
		}
	}

}
class T extends Thread{
    
    
	@Override
	public void run() {
    
    
		for (int i = 0; i <= 100; i++) {
    
    
			if(i % 2 == 0) {
    
    
				System.out.println("有一个偶数为:"+i);
			}
		}
	}
}
/**
 * 创建多线程的第二种方式:实现Runnable接口
 *   1、实现Runnable接口,然后重写run方法
 *   2、创建一个实现类的对象,然后创建一个Thread类对象,把实现类对象当中参数传入到Thread类的构建中
 *   3、启动thead类的start方法
 * @author 11018
 */
public class Demo02 {
    
    
	public static void main(String[] args) {
    
    
		A a = new A();
		Thread t = new Thread(a);
		t.start();
		
		for (int i = 0; i <= 100; i++) {
    
    
			if(i % 2 == 1) {
    
    
				System.out.println("有一个奇数为:"+i);
			}
		}
	}
}
class A implements Runnable{
    
    
	@Override
	public void run() {
    
    
		for (int i = 0; i <= 100; i++) {
    
    
			if(i % 2 == 0) {
    
    
				System.out.println("有一个偶数为:"+i);
			}
		}
	}
}
/**
 * 线程的第三种创建方式:Callable接口
 *   1、实现类实现callable接口,然后重写call()方法
 *   2、创建一个实现类的对象,然后创建FutureTask类包含实现类对象
 *   3、创建一个Thread类包含Fuxxxxx 然后调用thread类的start方法开启多线程
 * @author 11018
 *
 */
public class Demo03 {
    
    
	public static void main(String[] args) throws InterruptedException, ExecutionException {
    
    
		B b = new B();
		FutureTask<Integer> ft = new FutureTask<>(b);
		Thread t = new Thread(ft);
		t.start();
		
		for (int i = 0; i <= 100; i++) {
    
    
			if(i % 2 == 1) {
    
    
				System.out.println("有一个奇数为:"+i);
			}
		}
		System.out.println(ft.get());
	}

}
class B implements Callable<Integer>{
    
    
	@Override
	public Integer call() throws Exception {
    
    
		for (int i = 0; i <= 100; i++) {
    
    
			if(i % 2 == 0) {
    
    
				System.out.println("有一个偶数为:"+i);
			}
		}
		return 0;
	}
}

		Runnable r = new Runnable() {
    
    
			@Override
			public void run() {
    
    
				int sum = 0;
				for (int i = 1; i <= 5; i++) {
    
    
					sum += i;
				}
				System.out.println(sum);
			}
		};
		Thread t= new Thread(r);
		t.start();
		
		int sum = 1;
		for (int i = 1; i <= 5; i++) {
    
    
			sum *= i;
		}
		System.out.println(sum);

3.4 Thread类常用方法

Thread类是线程启动运行的核心类,内部提供了很多和线程有关的方法

方法名 作用
run()方法 Thread类继承的Runnable接口,多线程启动成功以后自动执行的代码方法
start()方法 开启一个线程,线程一旦开启以后,会自动调用run方法执行其中的业务逻辑
stop()方法 强制停止一个线程
setName(String name) 给当前线程设置一个别名,如果没有设置,会有一个默认线程名
getName() 获取设置的别名----设置的别名不一定是一个独立的线程
static currentThread():Thread 获取当前程序运行所属的线程
static sleep(long timestrap) 让当前线程睡上一定的毫秒数
setPriorit(int num) 设置线程的优先级,优先级高的更容易获取CPU的执行权 1-10 优先级设置必须在线程开启之前设置
yield():void 让当前线程丧失CPU的执行权,让优先级高的去获取CPU的执行权
join():void 线程加入,将另外一个线程加入到当前线程中,只有当加入的线程执行完成,当前线程才能继续向后执行
setDaemon(boolean flag) 设置 true线程为守护线程
isDaemon():boolean 测试线程是否为守护线程

3.5 线程的生命周期

五个阶段:创建、就绪、执行、阻塞、死亡

在这里插入图片描述

3.6 线程的数据同步问题–共享数据问题

3.6.1 JVM堆区和元数据的数据对于多线程是共享的,堆区如果共享必须满足数据在堆区是唯一的

3.6.2 继承Thread类实现的线程和Runnable、Callable实现的线程有什么区别

  1. 继承Thread类线程共享数据很难操作
  2. 实现方式做的线程本身就是线程共享的

3.6.3 如何解决共享数据的安全性问题

同步代码块

synchronized(同步监视器--锁的钥匙){ 操作共享数据的代码块; }

同步方法

案例:

/**
 * 做一个买火车票的程序,三个窗口同时卖100张票
 * @author 11018
 *
 */
public class Demo01 {
    
    
	public static void main(String[] args) {
    
    
//		TrainTicket tt = new TrainTicket();
		Window one = new Window();
		one.setName("window1");
		Window two = new Window();
		two.setName("window2");
		Window three = new Window();
		three.setName("window3");
		one.start();
		two.start();
		three.start();
	}
}
  1. 将操作共享数据的代码块抽取到一个独立的方法中,然后再方法上加上synchronized关键字

    代码示例:

    public class Demo02 {
          
          
    	public static void main(String[] args) {
          
          
    		NewWindow nw = new NewWindow();
    		Thread one = new Thread(nw);
    		Thread two = new Thread(nw);
    		Thread three = new Thread(nw);
    		one.start();
    		two.start();
    		three.start();
    	}
    
    }
    class NewWindow implements Runnable{
          
          
    	int num = 100;
    	@Override
    	public void run() {
          
          
    		while(true) {
          
          
    			int saleTicket = saleTicket();
    			if (saleTicket == 0) {
          
          
    				break;
    			}
    		}	
    	}
    	
    	public synchronized int saleTicket() {
          
          
    		if(num > 0) {
          
          
    			System.out.println(Thread.currentThread().getName()+"窗口卖出了第"+num+"张票,还剩余"+(--num)+"张票");
    			return 1;
    		}else {
          
          
    			return 0;
    		}
    	}
    }
    
    
  2. 静态方法 该类的.class实列

  3. 普通方法 this

  4. 同步方法再实现接口的线程类中可以实现安全性问题解决,继承体系下实现解决安全性问题比较复杂的。

上锁lock显示的上锁、释放锁:把操作共享数据的代码块放到一个try catch代码块当中,然后再try块显示的上锁 在finally块显示的释放锁

代码示例:

public class Demo03 {
    
    
	public static void main(String[] args) {
    
    
		WD w = new WD();
		FutureTask<String> ft = new FutureTask<>(w);
		FutureTask<String> ft1 = new FutureTask<>(w);
		FutureTask<String> ft2 = new FutureTask<>(w);
		Thread t = new Thread(ft);
		Thread t1 = new Thread(ft1);
		Thread t2 = new Thread(ft2);
		t.start();
		t1.start();
		t2.start();
		
	}
}
class WD implements Callable<String>{
    
    
	ReentrantLock lock = new ReentrantLock();
	int num = 100;
	@Override
	public String call() throws Exception {
    
    
		while (true) {
    
    
			try {
    
    
				lock.lock();
				if(num > 0 ) {
    
    
					System.out.println(Thread.currentThread().getName()+"窗口卖出了第"+num+"张票,还剩余"+(--num)+"张票");
				}else {
    
    
					break;
				}
			} catch (Exception e) {
    
    
				// TODO: handle exception
			}finally {
    
    
				lock.unlock();
			}
			
		}
		return null;
	}
}

**死锁:**我要的钥匙被别人用了,别人用的要是我用了

代码示例:

Thread t = new Thread() {
    
    
			@Override
			public void run() {
    
    
				synchronized (String.class) {
    
    
					System.out.println("线程正在执行中");
					try {
    
    
						Thread.sleep(100);
					} catch (InterruptedException e) {
    
    
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					synchronized (StringBuffer.class) {
    
    
						System.out.println("线程的第二个逻辑正在执行");
					}
				}
			}
		};
		Thread t1 = new Thread() {
    
    
			@Override
			public void run() {
    
    
				synchronized (StringBuffer.class) {
    
    
					System.out.println("线程正在执行中");
					try {
    
    
						Thread.sleep(100);
					} catch (InterruptedException e) {
    
    
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					synchronized (String.class) {
    
    
						System.out.println("线程的第二个逻辑正在执行");
					}
				}
			}
		};
		t.start();
		t1.start();

3.7 线程的通信问题–线程之间如何交流

多个线程之间互相“交流”,多个线程去通过交流去决定谁获得CPU执行,谁阻塞暂停执行

通信是通过Object类的三个方法来完成的:

方法名 作用
wait 让当前线程失去CPU执行权,进入阻塞状态
notify 唤醒整个进程中通过wait方法进入阻塞状态的线程—优先级最高的被唤醒的概率高一点
notifyAll 唤醒整个进程中所有通过wait方法进入阻塞状态的线程

线程通信的方法只能使用在同步代码块或者同步方法中,而且线程通信的三个方法调用者是同步监视器。

线程通信在同步代码和同步方法中,先使用notify/notifyall唤醒wait的线程,然后在代码块的最后再wait让当前线程进入阻塞状态

多线程通信时,多线程使用的同步监视器必须是同一个

代码示例:

package com.sxuek.thread.communication;
/**
 * 让两个线程交替打印0-100
 * @author 11018
 *
 */
public class Demo {
    
    
	public static void main(String[] args) {
    
    
		D d = new D();
		Thread t1 = new Thread(d);
		t1.setPriority(1);
		Thread t2 = new Thread(d);
		t2.setPriority(5);
		Thread t3 = new Thread(d);
		t3.setPriority(10);
		t1.start();
		t2.start();
		t3.start();
	}

}
class D implements Runnable{
    
    
	int num = 0;
	@Override
	public void run() {
    
    
		while(true) {
    
    
			synchronized (String.class) {
    
    
				String.class.notify();
				if(num > 100) {
    
    
					break;
				}else {
    
    
					System.out.printf("%s线程打印了数字%d\n", Thread.currentThread().getName(),num);
					num++;
				}
				try {
    
    
					String.class.wait();
				} catch (InterruptedException e) {
    
    
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
		}
		
	}
}

猜你喜欢

转载自blog.csdn.net/cai_4/article/details/131385943
今日推荐