package com.momo.demo;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
public class Demo1 {
public static void main(String[] args) throws IOException {
Properties ps = new Properties();
System.out.println(ps);
ps.load(new FileInputStream("a.txt"));
System.out.println(ps);
String value = ps.getProperty("diaosi"); System.out.println(value); }
}
一,多线程
1,概述
-看图
-如果一个程序有多条执行路径那么它就是一个多线程程序
-要真正的了解多线程,必须先了解进程。
因为线程是依赖进程存在的。
2,进程
-正在运行的程序,是系统进行资源分配和调度的独立单位。
-每一个进程都有自己的内存空间和系统资源。
通过任务管理器可以看到进程的存在
发现计算机可以同时运行多个程序,意思就是计算机支持多进程。
-多进程有什么意义?
可以在一个时间段内同时执行多个任务。提高cpu和内存的使用率。
计算机可以一边玩游戏,一边看电影,在听音乐。。。
-问:一边玩游戏,一边看电影。。。真的是同时进行的吗?
不是,因为一个cpu在一个时间点上只能做一件事情
而是cpu在多个程序之间进行快速的切换。
3,什么是线程?
-在一个进程内如果有多个执行路径就是多线程。
如果只有一个执行路径就是单线程。
-在一个进程内可以同时执行多个任务,这每一个任务就可以看成是一个线程。
-线程是进程中单个执行单元,是使用cpu的基本单位。
-我们发现现在的程序大部分都是多线程程序。
-问:多线程有什么意义?
多线程不是为了提高程序的执行速度。是提高了程序的使用率。
程序的执行都是在抢cpu的执行权。
多个继承在抢的时候,如果其中某一个进程的线程数很多,那么它抢到cpu
的概率就大很多。
但是我们并不知道哪一个线程在什么时候可以抢到。所以
线程的执行具有随机性。
-问:并发和并行?
-问:使用java命令运行程序的时候,启动jvm的时候,相当于启动了一个进程。
这个进程是单线程的还是多线程的?
多线程。
该进程中有一个主线程调用了main方法。
同时垃圾回收线程也启动了。否则肯定很容器导致内存溢出。。
4,我要如何实现一个多线程程序?
-因为我们上面说了,线程是依赖进程存在。所以我们先要创建一个进程。
而进程是由系统创建的,所以我们需要找一个方法调用系统功能创建进程。
但是java又不能直接调用系统功能,也就是说我们没有办法直接创建进程,
实现多线程。怎么办?
但是java可以调用C或者C++写好的程序来实现。然后由C或者C++调用系统功能
为了实现这样的效果,java就给我们提供了一些类供我们使用。
我们使用这些类就可以实现多线程了。。。
这个类就是Thread
二,多线程的实现方式一
1,看APIThread是一个类
-继承Thread类
2,构造方法
Thread()
分配一个新的 Thread对象。
Thread(String name)
分配一个新的 Thread对象。
3,步骤
-自定义类继承Thread类
-重写run方法
为什么要重写run方法?
因为不是说类中的所有代码都需要被多线程执行
为了区分哪些代码将来要被多线程执行,java在Thread类中就提供了
run方法用来包含哪些需要被多线程执行的代码
哪些代码需要被多线程执行?
执行起来比较费时的代码。比如循环。。。。
package com.momo.demo;
import com.momo.thread.MyThread;
/*
- 多线程实现方式一:继承Thread类
- / public class Demo4 { public static void main(String[] args) { // MyThread mt = new MyThread(); /
mt.run();/ /
* 注意:调用run方法为什么看不到多线程效果?
* 因为调用run方法相当于是普通方法的调用。
* 启动线程的方法实际上是 start 方法
* / /
* start方法启动了线程,然后由jvm调用了该线程的run方法
* */
// mt.start();
//java.lang.IllegalThreadStateException 非法线程状态异常
//因为一个线程被启动了2次导致。
// mt.start();
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
mt1.start();
mt2.start();
}
}
package com.momo.thread;
public class MyThread extends Thread{
@Override
public void run() {
//这里写我们将来想要被多线程执行的代码
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
}
三,如何获取和设置线程名称
String getName()
返回此线程的名称。
void setName(String name)
将此线程的名称更改为等于参数 name 。
Thread(String name)
分配一个新的 Thread对象。
package com.momo.demo;
import com.momo.thread.MyThread;
import java.security.AccessController;
/*
- 获取和设置线程名称
- 问:我想知道执行main方法的线程的名字,怎么办?
- static Thread currentThread()
返回对当前正在执行的线程对象的引用。
- /
public class Demo5 {public static void main(String[] args) {Thread t = Thread.currentThread();System.out.println(t.getName());//无参+set/
mt1.setName("宝强");
MyThread mt2 = new MyThread();
mt2.setName("宋吉吉");
mt1.start(); mt2.start();*/ //带参 /* MyThread mt1 = new MyThread("吴亦凡"); MyThread mt2 = new MyThread("都美竹"); mt1.start(); mt2.start();*/
/*
- 为什么默认的名字是Thread-编号?
- class Thread{
- public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
private static int threadInitNumber;0
private static synchronized int nextThreadNum() {
return threadInitNumber++;
}
- private void init(ThreadGroup g, Runnable target, String name,
long stackSize) { init(g, target, name, stackSize, null, true);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name; Thread parent = currentThread(); SecurityManager security = System.getSecurityManager(); if (g == null) { if (security != null) { g = security.getThreadGroup(); } if (g == null) { g = parent.getThreadGroup(); } } g.checkAccess(); if (security != null) { if (isCCLOverridden(getClass())) { security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); } } g.addUnstarted(); this.group = g; this.daemon = parent.isDaemon(); this.priority = parent.getPriority(); if (security == null || isCCLOverridden(parent.getClass())) this.contextClassLoader = parent.getContextClassLoader(); else this.contextClassLoader = parent.contextClassLoader; this.inheritedAccessControlContext = acc != null ? acc : AccessController.getContext(); this.target = target; setPriority(priority); if (inheritThreadLocals && parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); this.stackSize = stackSize; tid = nextThreadID(); }
}
- / /
MyThread mt2 = new MyThread();
mt1.start();
mt2.start();*/
}
}
四,线程调度
1,我们普通计算都只有一个cpu,一个cpu在某一个时间点上只能执行一个命令。
线程只有获取到cpu的执行权才能狗执行。
那么java是如何对线程进行调用的?
2,线程调用方式有2种模式:
-分时调用模式:
所有的线程轮流使用cpu的执行权,平均分配每个线程占用cpu的时间。
-抢占式调用模式:
优先让优先级高的线程使用,如果线程的优先级一样,那么就随机选择一个
注意:优先级高表示获取cpu的执行权的概率大。
-java使用的是抢占式
3,如何获取和设置线程优先级
int getPriority()
返回此线程的优先级。
void setPriority(int newPriority)
更改此线程的优先级。
package com.momo.demo;
import com.momo.thread.MyThread2;
/*
- 默认优先级:5,最小是1,最大是10
- */
public class Demo6 {
public static void main(String[] args) {
MyThread2 mt1 = new MyThread2("王祖贤");
MyThread2 mt2 = new MyThread2("关之琳");
MyThread2 mt3 = new MyThread2("中山忍");
/* System.out.println(mt1.getPriority()); System.out.println(mt2.getPriority()); System.out.println(mt3.getPriority()); System.out.println(Thread.NORM_PRIORITY); System.out.println(Thread.MIN_PRIORITY); System.out.println(Thread.NORM_PRIORITY); System.out.println(Thread.MAX_PRIORITY);*/ //java.lang.IllegalArgumentException // mt2.setPriority(11); mt2.setPriority(10); mt1.setPriority(1); mt1.start(); mt2.start(); mt3.start();
}
}
4,线程休眠
static void sleep(long millis)
package com.momo.thread;
public class MyThread2 extends Thread{
public MyThread2(){}
public MyThread2(String name){
super(name);
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName()+"---"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package com.momo.demo;
import com.momo.thread.MyThread2;
/*
- 线程休眠
- */
public class Demo7 {
public static void main(String[] args) {
MyThread2 mt1 = new MyThread2("王祖贤");
MyThread2 mt2 = new MyThread2("关之琳");
MyThread2 mt3 = new MyThread2("中山忍");
mt1.start(); mt2.start(); mt3.start();
}
}
5,线程加入
void join()
package com.momo.demo;
import com.momo.thread.MyThread2;
import com.momo.thread.MyThread3;
//线程加入
public class Demo8 {
public static void main(String[] args) throws InterruptedException {
MyThread3 mt1 = new MyThread3("王祖贤");
MyThread3 mt2 = new MyThread3("关之琳");
MyThread3 mt3 = new MyThread3("中山忍");
mt2.start(); mt2.join(); mt1.start(); mt3.start(); }
}
package com.momo.thread;
public class MyThread3 extends Thread{
public MyThread3(){}
public MyThread3(String name){
super(name);
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName()+"---"+i);
}
}
}
6,线程礼让
static void yield()
package com.momo.demo;
import com.momo.thread.MyThread3;
import com.momo.thread.MyThread4;
//线程礼让:尽可能的让线程的执行变得和谐一点。但是并不能保证可以一人一次
public class Demo9 {
public static void main(String[] args) {
MyThread4 mt1 = new MyThread4("王祖贤");
MyThread4 mt2 = new MyThread4("关之琳");
MyThread4 mt3 = new MyThread4("中山忍");
mt2.start(); mt1.start(); mt3.start(); }
}
package com.momo.thread;
public class MyThread4 extends Thread{
public MyThread4(){}
public MyThread4(String name){
super(name);
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName()+"---"+i);
Thread.yield();
}
}
}
7,守护线程
void setDaemon(boolean on)
package com.momo.demo;
import com.momo.thread.MyThread4;
import com.momo.thread.MyThread5;
//守护线程 lol
//注意:在启动前设置
//如果都是守护线程,jvm直接退出
public class Demo10 {
public static void main(String[] args) {
MyThread5 mt1 = new MyThread5("刘备");
MyThread5 mt2 = new MyThread5("关羽");
MyThread5 mt3 = new MyThread5("张飞");
//mt1.setDaemon(true); // mt2.setDaemon(true); mt3.setDaemon(true); mt1.start(); mt2.start(); mt3.start(); }
}
package com.momo.thread;
public class MyThread5 extends Thread{
public MyThread5(){}
public MyThread5(String name){
super(name);
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName()+"---"+i);
}
}
}
8,中断线程
void interrupt()
中断这个线程。 把线程状态终止,并且抛出异常InterruptedException
package com.momo.thread;
public class MyThread6 extends Thread{
public MyThread6(){}
public MyThread6(String name){
super(name);
}
@Override
public void run() {
System.out.println("开始上班。。。。");
try {
Thread.sleep(100000);
for (int i = 0; i < 100; i++) {
System.out.println(getName()+"---"+i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("工作结束。。。。");
}
}
package com.momo.demo;
import com.momo.thread.MyThread6;
//线程终止 public class Demo11 { public static void main(String[] args) { MyThread6 mt = new MyThread6("员工"); mt.start(); try { Thread.sleep(5000); mt.interrupt(); } catch (InterruptedException e) { e.printStackTrace(); } } }
五,线程的生命周期
-看图
六,多线程实现方式二
1,实现Runnable接口
2,方式一和方式二的区别?优缺点?
-方式二可以避免java单继承带来的局限性
-方式二更加合适多个线程处理同一个资源的情况
把线程和数据分离开了,较好的体现了面向对象思想
3,步骤
-自定义类实现接口
-重写run方法
-创建自定义类对象
-创建Thread对象,把自定义对象作为参数传递进去
Thread(Runnable target)
分配一个新的 Thread对象。
Thread(Runnable target, String name)
分配一个新的 Thread对象。
package com.momo.demo;
import com.momo.thread.MyRunnable;
public class Demo12 {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
/* Thread t1 = new Thread(mr); Thread t2 = new Thread(mr); t1.setName("默默"); t2.setName("小宝");*/ Thread t1 = new Thread(mr,"默默"); Thread t2 = new Thread(mr,"小宝"); t1.start(); t2.start(); }
}
package com.momo.thread;
public class MyRunnable implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+"--"+i); } } }
4,练习
-需求:假设某个电影院正在上映一个电影。一共有100张票
这个影院有3个售票窗口。设计一个程序模拟售票。
-2种方式:
继承Thread类
package com.momo.thread;
public class MyMovie extends Thread{
/*
* 应该让多个对象共享这100张,加上static
* 为了不让外部访问它,加上private
* /private static int tickets = 100;@Overridepublic void run() {//100张票// int tickets = 100;/
* 每个线程进来都走这里,这样的话,每个线程对象卖的都是自己的100张
* 这里有问题,应该3个窗口卖同一个100张票,应该定义在外面。
* */
//为了模拟
while (true){
if(tickets>0){
System.out.println(getName()+"卖出了第:"+tickets+"张票");
tickets--;
}
}
}
}
package com.momo.demo;
import com.momo.thread.MyMovie;
//方式一:继承Thread类
public class Demo13 {
public static void main(String[] args) {
// MyMovie.tickets = 1000;
MyMovie m1 = new MyMovie(); MyMovie m2 = new MyMovie(); MyMovie m3 = new MyMovie(); m1.setName("窗口一"); m2.setName("窗口二"); m3.setName("窗口三"); m1.start(); m2.start(); m3.start(); }
}
实现Runnable接口
package com.momo.demo;
import com.momo.thread.MyMv;
//方式二:
public class Demo14 {
public static void main(String[] args) {
MyMv m = new MyMv();
Thread t1 = new Thread(m,"窗口一"); Thread t2 = new Thread(m,"窗口二"); Thread t3 = new Thread(m,"窗口三"); t1.start(); t2.start(); t3.start(); }
} package com.momo.thread; //方式二:实现Runnable接口 public class MyMv implements Runnable{ private int tickets = 100; @Override public void run() { while (true){ if(tickets>0){ System.out.println(Thread.currentThread().getName()+"卖出了第:"+tickets+"张票"); tickets--; } } } }
5,上面案例,大致上完成了需求,但是还存在一些问题。
-在现实生活种,买票需要联网去买,网络总是存在一定的延时。
所以为了更加现实,改进程序,加入一点延时效果。
-有个别票被卖了多次
cpu的每一次操作都只能是原子性的
-出现了0票和负票
线程的随机性和延时导致
package com.momo.thread;
//方式二:实现Runnable接口
public class MyMv implements Runnable{
private int tickets = 100;
@Override
public void run() {
while (true){
if(tickets>0){
//t1 t2 t3
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖出了第:"+(tickets--)+"张票");
// tickets--; } } }
}
package com.momo.demo;
import com.momo.thread.MyMv;
//方式二:
public class Demo14 {
public static void main(String[] args) {
MyMv m = new MyMv();
Thread t1 = new Thread(m,"窗口一"); Thread t2 = new Thread(m,"窗口二"); Thread t3 = new Thread(m,"窗口三"); t1.start(); t2.start(); t3.start(); }
}
6,上面代码出现的问题我们就叫做线程安全问题。
一旦出现对软件的影响很大。所以我们要想办法解决问题。
-以后我们判断代码会不会出现线程安全问题的标准:
是否是多线程
是否有共享数据
是否有多条语句操作共享数据
-解决线程安全问题的基本思想
出现问题的主要原因,就是因为一个线程还没有执行完就被另一个
线程抢到了执行权。
所以我就想怎么样能够实现一次只让一个人进去,直到它出来其他人才能进去
这样就可以解决这个问题了。
-怎么实现?
不用担心,为了解决这个问题,java给我们提供了: 同步机制
同步代码块:
synchronized (对象){
需要同步的代码(需要被包成一个整体的代码,一次只让一个人进去)
多条语句操作共享数据的代码
}
同步代码块中的对象是?
可以是任意对象,多个线程必须使用的是同一个对象
对象相当于是一个锁的作用
同步代码块能解决线程安全问题的根本就在于对象上。