成都网站建设 小兵cms,设计公司网站,wordpress取消ftp,厦门外贸建站目录1 多线程1.1 进程1.2 线程1.3 多线程的实现方式1.3.1 方式1#xff1a;继承Tread类1.3.2 方式2#xff1a;实现Runnable接口1.3.3 方式3#xff1a;实现Callable接口1.4 设置和获取线程名称1.5 线程调度1.6 线程控制1.7 线程生命周期1.8 数据安全问题之案例#xff1a;…
目录1 多线程1.1 进程1.2 线程1.3 多线程的实现方式1.3.1 方式1继承Tread类1.3.2 方式2实现Runnable接口1.3.3 方式3实现Callable接口1.4 设置和获取线程名称1.5 线程调度1.6 线程控制1.7 线程生命周期1.8 数据安全问题之案例买票1.9 线程同步_同步代码块1.10 线程同步_同步方法1.11 线程安全的类了解1.12 Lock锁1.13 线程通讯1.14 生产者消费者1.14.1 生产者消费者概述1.14.2 生产者消费者案例1 多线程
1.1 进程
进程是正在运行的程序 是系统进行资源分配和调用的独立单位每一个进行都有它自己的内存空间和系统资源 进程的三个特征 独立性进程与进程之间是相互独立的彼此有自己独立内存区域动态性进程是运行中的程序要动态的占用内存CPU和网络等资源并发性CPU会分时轮询切换依次为每个进程服务因为切换的速度非常快给我们的感觉像是在同时执行这就是并发性并发同一时刻同时有多个在执行
1.2 线程
线程是进程中的单个顺序控制流是一条执行路径 单线程一个进程只有一条执行路径多线程一个进程有多条执行路径
1.3 多线程的实现方式
1.3.1 方式1继承Tread类 流程 1、定义一个MyTread类继承Tread类2、在MyTread类中重写run()方法3、创建MyTread类的对象4、启动线程void start() 为什么要重写run()方法 因为run()是用来封装被线程执行的代码 run()方法和start()方法的区别 run()封装线程执行的代码直接调用相当于普通方法的的调用start()启动线程然后由JVM调用此线程中的run()方法 范例 MyTread类
package test;//1、定义一类MyTread继承Tread类
public class MyThread extends Thread{2、在MyTread类中重写run()方法Overridepublic void run() {for(int i0;i100;i) {System.out.println(i);}}
}测试类
package test;public class Demo {public static void main(String[] args) {//3、创建MyTread类的对象MyThread my1 new MyThread();MyThread my2 new MyThread();//4、启动线程void start()启动线程由Java虚拟机调用此线程的run()方法my1.start();my2.start();}
}1.3.2 方式2实现Runnable接口
流程 1、定义一个MyRunnable类实现Runnable接口2、在MyRunnable类中重写run()方法3、创建MyRunnable类的对象4、创建Tread类的对象把MyRunnable对象作为构造方法的参数5、启动线程 好处 避免了Java单继承的局限性适合多个相同程序的代码取处理同一个资源的情况把线程和程序的代码、数据有效分离较好地体现了面向对象的设计理论
package test;public class Demo {public static void main(String[] args) {//3、创建MyRunnable类的对象MyRunnable mr new MyRunnable();//4、创建Tread类的对象把MyRunnable对象作为构造方法的参数
// Thread t1 new Thread(mr);
// Thread t2 new Thread(mr);//Thread(Runnable target,String name)Thread t1 new Thread(mr,高铁);Thread t2 new Thread(mr,飞机);//5、启动线程t1.start();t2.start();}
}1.3.3 方式3实现Callable接口
1.4 设置和获取线程名称
Thread类中设置和获取线程名称的方法
方法名说明void setName(Stringname)将此线程的名称更改为等于参数nameString getName()返回此线程的名称public Thread(String name)通过构造方法也可以设置线程名称public static Thread currentThread()返回对当前正在执行的线程对象的引用可以返回main()方法中线程public static void sleep(long time)让当前线程休眠多少毫秒再继续执行
MyThread类
package test;public class MyThread extends Thread{//构造方法添加线程名称public MyThread(){}public MyThread(String name) {super(name);}Overridepublic void run() {for(int i0;i100;i) {//1,String getName() 返回此线程的名称System.out.println(getName():i);}}
}测试类
package test;public class Demo {public static void main(String[] args) {/* MyThread my1 new MyThread();MyThread my2 new MyThread();//2,void setName(Stringname) 将此线程的名称更改为等于参数namemy1.setName(高铁);my2.setName(飞机);*///3通过构造方法设置线程名称//需要自己定义的类中提供此带参构造方法并通过super访问父类带参构造方法/*MyThread my1 new MyThread(高铁);MyThread my2 new MyThread(飞机); my1.start();my2.start();*///4public static Thread currentThread() 返回对当前正在执行的线程对象的引用可以返回main()方法中线程System.out.println(Tread.currentThread().getName()); //main}
}1.5 线程调度 线程有两种调度模型 分时调度模型所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片抢占式调度模型优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的CPU时间片相对多一些 Java使用的是抢占式调度模型 假如计算机只有一个CPU, 那么CPU在某一个时刻只能执行条指令, 线程只有得到CPU时间片也就是使用权,才可以执行指令。所以说多线程程序的执行是有随机性因为谁抢到CPU的使用权是不一定的 Thread类中设置和获取线程优先级的方法
方法名说明public final int getPriority() [praɪˈɔːrəti]返回此线程的优先级public final void setPriority(int newPriority)更改此线程的优先级
线程默认优先级是5线程优先级范围是1-10线程优先级高仅仅表示线程获取的CPU时间的几率高但是要在次数比较多或者多次运行的时候才能看到你想要的效果
package test;public class Demo {public static void main(String[] args) {ThreadPriority tp1 new ThreadPriority();ThreadPriority tp2 new ThreadPriority();ThreadPriority tp3 new ThreadPriority();tp1.setName(高铁);tp2.setName(飞机);tp3.setName(汽车);//1,public final int getPriority() [praɪˈɔːrəti] 返回此线程的优先级
// System.out.println(tp1.getPriority()); //5
// System.out.println(tp2.getPriority()); //5
// System.out.println(tp3.getPriority()); //5//2,public final void setPriority(int newPriority) 更改此线程的优先级System.out.println(Thread.MAX_PRIORITY); //10System.out.println(Thread.MIN_PRIORITY); //1System.out.println(Thread.NORM_PRIORITY); //5//设置正确优先级tp1.setPriority(5);tp2.setPriority(10);tp3.setPriority(1);tp1.start();tp2.start();tp3.start();}
}1.6 线程控制
方法名说明static void sleep(long millis)使当前正在执行的线程停留暂停执行指定的毫秒数void join()等待这个线程死亡void setDaemon(boolean on) [ˈdiːmən]将此线程标记为守护线程当运行的线程都是守护线程时Java虚拟机很快将退出 并不是立刻退出
案例sleep()方法
线程类
package test;public class ThreadSleep extends Thread{Overridepublic void run() {for(int i0;i10;i) {System.out.println(getName():i);//1,static void sleep(long millis) 使当前正在执行的线程停留暂停执行指定的毫秒数try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}测试类
package test;public class Demo {public static void main(String[] args) {ThreadSleep ts1 new ThreadSleep();ThreadSleep ts2 new ThreadSleep();ThreadSleep ts3 new ThreadSleep();ts1.setName(曹操);ts2.setName(刘备);ts3.setName(孙权);ts1.start();ts2.start();ts3.start();// 曹操:0
// 孙权:0
// 刘备:0
// 孙权:1
// 曹操:1
// 刘备:1
// ...}
}案例join()方法
package test;public class Demo {public static void main(String[] args) {ThreadJoin tj1 new ThreadJoin();ThreadJoin tj2 new ThreadJoin();ThreadJoin tj3 new ThreadJoin();tj1.setName(康熙);tj2.setName(四阿哥);tj3.setName(八阿哥);tj1.start();//2,void join() 等待这个线程死亡try {tj1.join();} catch (InterruptedException e) {e.printStackTrace();}tj2.start();tj3.start();// 康熙:0
// 康熙:1
// 康熙:2
// 四阿哥:0
// 四阿哥:1
// 八阿哥:0
// 八阿哥:1
// 八阿哥:2
// 四阿哥:2
// ...}
}案例setDaemon()方法
package test;public class Demo {public static void main(String[] args) {ThreadJoin tj1 new ThreadJoin();ThreadJoin tj2 new ThreadJoin();ThreadJoin tj3 new ThreadJoin();tj2.setName(关羽);tj3.setName(张飞);//设置主线程为刘备Thread.currentThread().setName(刘备);//3,void setDaemon(boolean on) 将此线程标记为守护线程当运行的线程都是守护线程时Java虚拟机将退出tj1.setDaemon(true);tj2.setDaemon(true);tj1.start();tj2.start();for(int i0;i2;i) {System.out.println(Thread.currentThread().getName():i);}//刘备执行完后关羽和张飞会很快结束}
}1.7 线程生命周期 1.8 数据安全问题之案例买票 为什么出现问题?(这也是我们判断多线程程序是否会有数据安全问题的标准) 是否是多线程环境是否有共享数据是否有多条语句操作共享数据 如何解决多线程安全问题呢? 基本思想让程序没有安全问题的环境 怎么实现呢? 把多条语句操作共享 数据的代码给锁起来让任意时刻只能有一一个线程执行即可Java提供 了同步代码块的方式来解决
1.9 线程同步_同步代码块
锁多条语句操作共享数据可以使用同步代码块实现格式
synchronized(任意对象) {多条语句操作共享数据的代码
} 好处让多个线程实现先后依次访问共享资源解决了多线程的数据安全问题 弊端当线程很多的时候因为每个线程都会去判断同步上的锁这是很消耗资源的无形中降低程序的运行效率 sellTicket类
package test;//1,定义一个类SellTicket实现Runnable接口里面定义一个成员变量: private int tickets 100;
public class SellTicket implements Runnable{private int tickets 100;private Object obj new Object();//2,在ellTicket类中重写run0方法实现卖票 代码步骤如下Overridepublic void run() {while(true) {//tickes100//t1,t2,t3//假设t1抢到CPU执行器synchronized (obj){//t1进来后把代码锁起来了if (tickets 0) {try {Thread.sleep(100);//t1休息100毫秒} catch (InterruptedException e) {e.printStackTrace();}//窗口1正在出售第100张票System.out.println(Thread.currentThread().getName() 正在出售第 tickets 张票);tickets--; //tickets99}//t1出来了锁就被释放了}}}
}测试类
package test;public class SellTicketDemo {public static void main(String[] args) {//创建SellTicket类的对象SellTicket st new SellTicket();//创建三个Thread类的对象,把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();}
}1.10 线程同步_同步方法
作用把出现线程安全问题的核心方法给锁起来每次只能一个线程进入访问其他线程必须在方法外面等待同步方法就是把synchronized关键字加到方法上锁对象为this 格式修饰符 synchronized 返回值类型 方法名(方法参数) {} 同步静态方法就是把synchronized关键字加到静态方法上面锁对象为类名.class 格式修饰符 static synchronized 返回值类型 方法名(方法参数) {}
package test;public class SellTicket implements Runnable{
//1非静态 private int tickets 100;private static int tickets 100;private Object obj new Object();private int x 0;Overridepublic void run() {while(true) {if(x%20) {
//1非静态 synchronized (this) {synchronized (SellTicket.class) {if (tickets 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() 正在出售第 tickets 张票);tickets--; //tickets99}}} else {
// synchronized (obj) {
// if (tickets 0) {
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName() 正在出售第 tickets 张票);
// tickets--; //tickets99
// }
// }sellTicket();}x;}}
//1非静态
// private synchronized void sellTicket() {
// if (tickets 0) {
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName() 正在出售第 tickets 张票);
// tickets--; //tickets99
// }
// }private static synchronized void sellTicket() {if (tickets 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() 正在出售第 tickets 张票);tickets--; //tickets99}}
}1.11 线程安全的类了解
源码中方法都被synchronized修饰
StringBuffer
线程安全 可变的字符序列从版本JDK 5开始被StringBuilder替代。通常应该使用StringBuilder类, 因为它支持所有相同的操作,但它更快因为它不执行同步
Vector
从Java 2平台v1.2开始该类改进了List接口, 使其成为Java Collections Framework的成员。 与新的集合实现不同Vector被同步。 如果不需要线程安全的实现建议使用ArrayList代替Vector
Hashtable
该类实现了一个哈希表它将键映射到值。任何非null对象都可以用作键或者值从Java 2平台v1.2开始该类进行了改进实现了Map接口,使其成为Java Collections Framework的成员。与新的集合实现不同Hashtable被同步。 如果不需要线程安全的实现建议使用HashMap代替Hashtable
Collections类中static T ListT snchronizedList(ListT list)返回由指定列表支持的同步线程安全的列表
package test;import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;public class Demo {public static void main(String[] args) {//static T ListT snchronizedList(ListT list)返回由指定列表支持的同步线程安全的列表CollectionString list Collections.synchronizedList(new ArrayListString());/*源码都是返回Synchronizedpublic static T ListT synchronizedList(ListT list) {return (list instanceof RandomAccess ?new Collections.SynchronizedRandomAccessList(list) :new Collections.SynchronizedList(list));}*/}
}1.12 Lock锁
Lock是接口不能直接实例化采用实现类ReentrantLock来实例化JDK5以后ReentrantLock构造方法
方法名说明ReentrantLock()创建一个ReentrantLock的实例对象
Lock中获得锁和释放锁方法
方法名说明void lock()获得锁void unlock()释放锁
推荐使用try{} finall{}代码块来加锁和释放锁
package test;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class SellTicket implements Runnable{private static int tickets 100;private Lock lock new ReentrantLock();Overridepublic void run() {while(true) {try {lock.lock();if (tickets 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() 正在出售第 tickets 张票);tickets--;}}finally {lock.unlock();}}}
}1.13 线程通讯
线程通信一定是多个线程在操作同一个资源才需要通信
方法名说明public void wait()让当前线程进入到等待状态此方法必须锁对象调用public void notify()唤醒当前锁对象上等待状态的某个线程此方法必须锁对象调用public void notifyAll()唤醒当前锁对象上等待状态的全部线程此方法必须锁对象调用
1.14 生产者消费者
1.14.1 生产者消费者概述 为了体现生产和消费过程中的等待和唤醒Java就提供了几个方法供我们使用这几个方法在Object类中Object类的等待和唤醒方法
方法名说明void wait()导致当前线程等待直到另一个线程调用该对象的 notify() 方法或 notifyAll() 方法void notify()唤醒正在等待对象监视器的单个线程void notifyAll()唤醒正在等待对象监视器的所有线程
1.14.2 生产者消费者案例 奶箱类
package test;//1定义奶箱类
public class Box {//定义一个成员变量表示第x瓶奶private int milk;//定义一个成员变量表示奶箱的状态private boolean state false;//提供存储牛奶和获取牛奶的操作public synchronized void put(int milk) {//如果有牛奶等待消费if(state) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}//如果没有牛奶就生产牛奶this.milk milk;System.out.println(送奶工将第 this.milk 瓶奶放入奶箱);//生产完毕后修改奶箱状态state true;//唤醒其他等待线程notifyAll();}public synchronized void get() {//如果没有牛奶就等到生产if(!state) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}//如果有牛奶就消费牛奶System.out.println(用户拿到第 this.milk 瓶奶);//消费完毕后,修改奶箱状态state false;//唤醒其他等待线程notifyAll();}
}生产者类
package test;//2:生产者类(Producer):实现Runnable接口
public class Producer implements Runnable {private Box b;public Producer(Box b) {this.b b;}//重写run()方法调用存储牛奶的操作Overridepublic void run() {for (int i 1; i 5; i) {b.put(i);}}
}消费者类
package test;//3:消费者类(Customer);实现Runnable接口
public class Customer implements Runnable{private Box b;public Customer(Box b) {this.b b;}//重写run()方法调用获取牛奶的操作Overridepublic void run() {while(true) {b.get();}}
}测试类
package test;public class BoxDemo {public static void main(String[] args) {//创建奶箱对象这是共享数据区域Box b new Box();//创建生产者对象把奶箱对象作为构造方法参数传递。因为在这个类中要谓用存储牛奶的操作Producer p new Producer(b);//创建消费者对象把奶箱对象作为构造方法参数传递因为在这个类中要调用获取牛奶的操作Customer c new Customer(b);//创建2个线程对象分别把生产者对象和消费者对象作为构造方法参数传递Thread t1 new Thread(p);Thread t2 new Thread(c);//启动线程t1.start();t2.start();// 送奶工将第1瓶奶放入奶箱
// 用户拿到第1瓶奶
// 送奶工将第2瓶奶放入奶箱
// 用户拿到第2瓶奶
// 送奶工将第3瓶奶放入奶箱
// 用户拿到第3瓶奶
// 送奶工将第4瓶奶放入奶箱
// 用户拿到第4瓶奶
// 送奶工将第5瓶奶放入奶箱
// 用户拿到第5瓶奶}
}