0
点赞
收藏
分享

微信扫一扫

多线程窗口买票

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 (对象){

需要同步的代码(需要被包成一个整体的代码,一次只让一个人进去)

多条语句操作共享数据的代码

}

同步代码块中的对象是?

可以是任意对象,多个线程必须使用的是同一个对象

对象相当于是一个锁的作用

同步代码块能解决线程安全问题的根本就在于对象上。

举报

相关推荐

0 条评论