补充:
并发:两个或多个事件在同一时间段发生—-交替执行—效率低
并行:两个或多个事件在同时发生—————同时执行—效率高
多线程语法
多线程是java中的一个并发的概念:
- 在计算机的运行中,所有的进程都是同时运行的(CPU会同时调用,之后会随机的获取分配)
- 1.在java程序中也是支持的
- 2.当java执行main方法的时候,其实就是执行一个名字叫做main的线程—主线程
- 可以在main线程执行的时候,开启多个其他的线程A,B,C等等
- 多个线程A,B,C等等都是同时执行的,同时等待CPU的调用,相互抢夺CPU调用自己的时间片段
- 3.Thread类是java中的专门处理线程的api,是java.lang包下的常用类,每一个Thread类的
- 对象,就代表一个某种状态的线程
- java.lang包下常用的类(不需要导包):String,System,八大基本类型,Thread…
- 进程:表示一个个的正在运行的程序
- 线程:表示一个个正在运行的程序中的子进程(线程)
- 包含关系:一个进程包含多个线程 ```java package com.xincheng;
/**
- @author Lynn
- @create 2020-12-14-14:46 */
import java.util.Date;
/**
- 多线程是java中的一个并发的概念:
- 在计算机的运行中,所有的进程都是同时运行的(CPU会同时调用,之后会随机的获取分配) *
- 1.在java程序中也是支持的
- 2.当java执行main方法的时候,其实就是执行一个名字叫做main的线程
- 可以在main线程执行的时候,开启多个其他的线程A,B,C等等
- 多个线程A,B,C等等都是同时执行的,同时等待CPU的调用,相互抢夺CPU调用自己的时间片段
- 3.Thread类是java中的专门处理线程的api,是java.lang包下的常用类,每一个Thread类的
- 对象,就代表一个某种状态的线程 *
- java.lang包下常用的类(不需要导包):String,System,八大基本类型,Thread… *
- 进程:表示一个个的正在运行的程序
- 线程:表示一个个正在运行的程序中的子进程(线程)
包含关系:一个进程包含多个线程 */ public class ThreadDemo { //main是一个主线程 public static void main(String[] args) {
//是主线程调用的一个其他线程Date d=new Date();System.out.println(d);//是主线程调用的一个其他线程System.out.println(System.currentTimeMillis());say();
}
public static void say(){
System.out.println("hello");
案例1
```java package com.xincheng_01;
/**
- @author Lynn
- @create 2020-12-14-15:20 */
/**
- 自定义线程类 *
- 定义一个Thread的子类 *
创建线程的第一种方式:继承Thread类 */ public class MyThread extends Thread {
//重写run方法—相关的逻辑写进去 @Override public void run(){
for (int i = 0; i <50 ; i++) {/*** this表示当前被调用的的这个线程* 获取现成的名字是java干的事情,我们不管*/System.out.println(this.getName()+":"+i);}
} }
java package com.xincheng_01;
/**
- @author Lynn
- @create 2020-12-14-15:13 */
/**
- java中使用Thread类来创建线程: 第一种方式: *
- 步骤:
- 1.指定执行现成的目标:定义一个Thread子类,重写run方法,将相关的逻辑实现
- public void run():将需要的逻辑写在里面,该方法相当于线程中的main方法
- 2.创建自定义线程的子类对象
- 3.开启线程操作—触发
- public void start():只要调用了这个方法,线程才会启动,开始执行 *
- Thread中的相关方法:
- public final String getName():获取线程的名字
- public final void setName():设置线程的名字
- public static Thread currentThread():获取当前的线程对象 *
线程的运行默认是不可控制的,是CPU随机调用的,我们只能看到运行的结果 / public class ThreadDemo { //主线程 public static void main(String[] args) { //创建线程对象—用于开启线程 Thread t1=new MyThread(); Thread t2=new MyThread();
//开启线程—直接运行 t1.start(); t2.start();
System.out.println(“———————————————————“);
//返回一个main线程—其实就是main方法的线程 Thread mainThread=Thread.currentThread(); System.out.println(mainThread);//Thread[main,5,main]
for (int i = 0; i <50 ; i++) {
System.out.println(mainThread.getName()+":"+i);
案例2
```java package com.xincheng_01;
/**
- @author Lynn
- @create 2020-12-14-15:36 */
/**
- 创建线程的第二种方式:
- 实现Runnable接口,lang包下的: *
- 1.实现接口,并且要实现run方法
- 2.在run方法中,添加逻辑代码
- 3.通过start启动线程
*/
public class MyRunable implements Runnable{
@Override
public void run(){
} }//返回当前线程Thread thisThread=Thread.currentThread();for (int i = 0; i <20 ; i++) {System.out.println(thisThread.getName()+":"+i);}
java package com.xincheng_01;
/**
- @author Lynn
- @create 2020-12-14-15:13 */
/**
- java中使用Thread类来创建线程: *
- 第二种方式: *
- 步骤:
- 1.指定线程执行的目标,定义Runnable接口,并且实现接口,重写run方法,制定目标逻辑
- 2.通过指定线程执行目标的构造方法创建线程对象
- public Thread(Runnable target)
- 创建线程并且执行目标对象
- 通过线程执行目标对象创建线程对象
- 3.开启线程操作(start) *
-
//创建线程执行目标MyRunable mr=new MyRunable();//线程对象--任务//通过指定线程执行目标的构造函数来创建线程Thread thread=new Thread(mr);//线程的名称thread.setName("jack");Thread thread2=new Thread(mr);//线程的名称thread2.setName("rose");//开启线程thread.start();thread2.start();
演示匿名内部类创建线程—实际开发中使用
- 1.使用匿名内部类创建线程的原理和普通类创建线程的原理完全一样
- 2.区别在于利用匿名内部类创建线程的写法更加简洁,使用非常多 ```java package com.xincheng_02;
/**
- @author Lynn
- @create 2020-12-14-16:06 */
/**
- 演示匿名内部类创建线程—实际开发中使用 *
- 1.使用匿名内部类创建线程的原理和普通类创建线程的原理完全一样
2.区别在于利用匿名内部类创建线程的写法更加简洁,使用非常多 */ public class AnonysThreadDemo { public static void main(String[] args) {
//方式一:使用匿名内部类创建线程的子类对象Thread t1 = new Thread() {@Overridepublic void run() {System.out.println("还记得去年陪你过圣诞节的人吗");}};t1.start();System.out.println("----------------------------------------------------------------");//使用匿名内部类,创建线程的匿名子类对象,并且启动线程new Thread() {@Overridepublic void run() {System.out.println("还记得上一次陪你去看演唱会的人吗");}}.start();//方式二:使用匿名内部类的方式,创建执行目标类的对象Runnable r = new Runnable() {@Overridepublic void run() {System.out.println("她来听我的演唱会");}};//传入到构造函数Thread t2 = new Thread(r);t2.start();System.out.println("-----------------------------");//组合写法-精简写法new Thread(new Runnable() {@Overridepublic void run() {System.out.println("往事只能回味");}}).start();//组合写法--普通写法Thread t3=new Thread(new Runnable() {@Overridepublic void run() {System.out.println("往事只能回味");}});t3.start();
练习(售卖车票)
```java package com.xincheng_03;
/**
- @author Lynn
- @create 2020-12-14-16:29 */
import javax.crypto.interfaces.PBEKey;
/**
- 这是一个任务
使用第二种方式—使用Runnable接口 */ public class Ticket implements Runnable{ //定义车票数 private int number=100;
//卖票的逻辑 @Override public void run() {
//模拟车站不停地卖票--卖光为止while (true){//有票就卖if (number>0){System.out.println(Thread.currentThread().getName()+"正在销售第"+(number--)+"张票");}else {//没有票了break;}}
} }
java package com.xincheng_03;
/**
- @author Lynn
- @create 2020-12-14-16:25 */
/**
- 多线程模拟售卖火车票 *
- 要求使用第二种方式实现,方便数据共享 *
- 步骤:
- 1.定义卖票的线程执行目标:(任务)
- 在成员变量定义为票数100张,卖掉一张,改数字就减去1,一直到0为止
- 重写run方法,完成案例
- 2.创建买票的线程且执行该目标的对象
- 3.使用该卖票子线程执行对象创建多个线程任务
4.开启多个线程 */ public class TicketDemo { public static void main(String[] args) {
//创建车票的线程执行目标对象Ticket ticket=new Ticket();/*//使用该卖票的线程执行目标的对象创建多个任务Thread t1 = new Thread(ticket,"jack.Ma");Thread t2 = new Thread(ticket,"Huateng.Ma");Thread t3 = new Thread(ticket,"Yanhong.Li");t1.start();t2.start();t3.start();*/new Thread(ticket,"jack.Ma").start();new Thread(ticket,"Huateng.Ma").start();new Thread(ticket,"Yanhong.Li").start();
回顾
线程池与线程生命周期
线程生命周期图示
线程池
```java
package com.igeek_01;
/**
- @author Lynn
- @create 2020-12-14-16:29 */
/**
- 这是一个任务
使用第二种方式—使用Runnable接口 */ public class Ticket implements Runnable{ //定义车票数 private int number=100;
//卖票的逻辑 @Override public void run() {
//模拟车站不停地卖票--卖光为止while (true){//有票就卖if (number>0){System.out.println(Thread.currentThread().getName()+"正在销售第"+(number--)+"张票");}else {//没有票了break;}}
} }
java package com.igeek_01;
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;
/**
- @author Lynn
@create 2020-12-15-9:08 */ public class ThreadPoolDemo { public static void main(String[] args) {
//实例化线程池/*** ExecutorService是一个接口,专门处理线程池的* Executors是一个工具类,提供了很多的方法服务于线程* newFixedThreadPool(3)该方法传入的参数表示在线程启动的时候初始化几个线程*/ExecutorService threadPool= Executors.newFixedThreadPool(3);//创建线程的执行目标Ticket ticket=new Ticket();//向线程池中提交任务目标threadPool.submit(ticket);threadPool.submit(ticket);threadPool.submit(ticket);threadPool.submit(ticket);threadPool.submit(ticket);//在适当的时候也可以关闭线程池,一般不关闭//意味着线程池中的所有的线程都销毁
// threadPool.shutdown(); } } ```
Callable接口

package com.igeek_02;/*** @author Lynn* @create 2020-12-14-16:29*//*** 这是一个任务* 使用第二种方式--使用Runnable接口*/public class Ticket implements Runnable{//定义车票数private int number=100;//卖票的逻辑@Overridepublic void run() {//模拟车站不停地卖票--卖光为止while (true){//有票就卖if (number>0){System.out.println(Thread.currentThread().getName()+"正在销售第"+(number--)+"张票");}else {//没有票了break;}}}}
package com.igeek_02;import java.util.concurrent.Callable;/*** @author Lynn* @create 2020-12-15-10:03*/public class MyCallable implements Callable<String> {/*** 定义一个线程执行的目标逻辑* @return* @throws Exception*/@Overridepublic String call() throws Exception {return "我是线程任务的返回值结果";}}
package com.igeek_02;import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;/*** @author Lynn* @create 2020-12-15-9:08*/public class ThreadPoolDemo {public static void main(String[] args) throws ExecutionException, InterruptedException {//实例化线程池/*** ExecutorService是一个接口,专门处理线程池的* Executors是一个工具类,提供了很多的方法服务于线程* newFixedThreadPool(3)该方法传入的参数表示在线程启动的时候初始化几个线程*/ExecutorService threadPool= Executors.newFixedThreadPool(3);//创建线程的执行目标Ticket ticket=new Ticket();//利用Callable接口返回Future,(没有返回值的run方法,返回的是null,没有意义)Future<?> ticketFuture = threadPool.submit(ticket);System.out.println(ticketFuture.get());//创建带有返回值的线程执行目标MyCallable callable=new MyCallable();//向线程中提交任务,并返回线程目标的执行结果Future<String> future1= threadPool.submit(callable);Future<String> future2= threadPool.submit(callable);Future<String> future3= threadPool.submit(callable);//从执行结果中返回call的具体值String result1=future1.get();String result2=future2.get();String result3=future3.get();System.out.println(result1);System.out.println(result2);System.out.println(result3);//在适当的时候也可以关闭线程池,一般不关闭//意味着线程池中的所有的线程都销毁// threadPool.shutdown();}}
线程安全问题
线程安全问题的解决方案—-加锁

package com.igeek_04;/*** @author Lynn* @create 2020-12-15-10:32*//*** 使用第二种方式创建线程,原因是执行线程目标对象共享** 定义一个卖票的线程执行目标对象--加锁*/public class Ticket implements Runnable{//票数private int number=100;//声明成员变量为定义锁对象private Object lock=new Object();@Overridepublic void run() {while (true){synchronized (lock){//线程暂停try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}//有票就卖if (number>0){String threadName=Thread.currentThread().getName();System.out.println(threadName+"正在销售第"+number+"张票");number--;}else {//没票了break;}}}}}
package com.igeek_04;/*** @author Lynn* @create 2020-12-15-10:44*//*** 线程安全问题的解决方案 :* 同步代码块* synchronized(锁对象){* 可能出现线程安全问题的代码* }*/public class ThreadDemo {public static void main(String[] args) {//实例化目标Ticket ticket=new Ticket();//使用该卖票的目标创建多个线程Thread thread1=new Thread(ticket,"Jack");Thread thread2=new Thread(ticket,"Rose");Thread thread3=new Thread(ticket,"Tom");//启动多个线程thread1.start();thread2.start();thread3.start();}}
优化
package com.igeek_05;/*** @author Lynn* @create 2020-12-15-10:32*//*** 使用第二种方式创建线程,原因是执行线程目标对象共享** 定义一个卖票的线程执行目标对象--加锁*/public class Ticket implements Runnable{//票数private int number=100;//声明成员变量为定义锁对象private Object lock=new Object();//创建一个标记,让不同的人来执行不同的代码块private int x=0;@Overridepublic void run() {while (true){//进行取余的运算,模拟卖票的人if(x%2==0){//代码块//将完整的一套动作使用synchronized包裹synchronized (lock){//线程暂停try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}//有票就卖if (number>0){String threadName=Thread.currentThread().getName();System.out.println(threadName+"正在销售第"+number+"张票");number--;}else {//没票了break;}}}else {//代码块//将完整的一套动作使用synchronized包裹synchronized (lock){//线程暂停try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}//有票就卖if (number>0){String threadName=Thread.currentThread().getName();System.out.println(threadName+"正在销售第"+number+"张票");number--;}else {//没票了break;}}}x++;}}}
package com.igeek_05;/*** @author Lynn* @create 2020-12-15-10:44*//*** 线程安全问题的解决方案 :* 同步代码块* synchronized(锁对象){* 可能出现线程安全问题的代码* }*/public class ThreadDemo {public static void main(String[] args) {//实例化目标Ticket ticket=new Ticket();//使用该卖票的目标创建多个线程Thread thread1=new Thread(ticket,"Jack");Thread thread2=new Thread(ticket,"Rose");Thread thread3=new Thread(ticket,"Tom");//启动多个线程thread1.start();thread2.start();thread3.start();}}
同步方法(用synchronized修饰)有静态和非静态的

非静态方法
package com.igeek_06;/*** @author Lynn* @create 2020-12-15-10:32*//*** 使用第二种方式创建线程,原因是执行线程目标对象共享** 定义一个卖票的线程执行目标对象--加锁** 演示synchronized可以用来修饰方法,叫做同步方法** 当前案例中,使用对象锁对当前的这个Ticket加锁,目的是this是当前正在被调用的这个Ticket对象,* 不管是synchronized代码块中还是封装的一个synchronized方法,都可以保证使用的是同一个锁对象*/public class Ticket implements Runnable{//票数private int number=100;//声明成员变量为定义锁对象private Object lock=new Object();//创建一个标记,让不同的人来执行不同的代码块private int x=0;@Overridepublic void run() {while (true){//进行取余的运算,模拟卖票的人if(x%2==0){//代码块//将完整的一套动作使用synchronized包裹//把当前这个被调用的对象锁住synchronized (this){//线程暂停try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}//有票就卖if (number>0){String threadName=Thread.currentThread().getName();System.out.println(threadName+"正在销售第"+number+"张票");number--;}else {this.sell();}}}//当票数=0,就表示没票了if(number<0){break;}x++;}}public synchronized void sell(){try {Thread.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}if (number>0){String threadName=Thread.currentThread().getName();System.out.println(threadName+"正在销售第"+number+"张票");number--;}}}
package com.igeek_06;/*** @author Lynn* @create 2020-12-15-10:44*/import com.igeek_05.Ticket;/*** 线程安全问题的解决方案 :* 同步代码块* synchronized(锁对象){* 可能出现线程安全问题的代码* }*/public class ThreadDemo {public static void main(String[] args) {//实例化目标com.igeek_05.Ticket ticket=new Ticket();//使用该卖票的目标创建多个线程Thread thread1=new Thread(ticket,"Jack");Thread thread2=new Thread(ticket,"Rose");Thread thread3=new Thread(ticket,"Tom");//启动多个线程thread1.start();thread2.start();thread3.start();}}
静态方法
package com.igeek_07;/*** @author Lynn* @create 2020-12-15-10:32*//*** 使用第二种方式创建线程,原因是执行线程目标对象共享** 定义一个卖票的线程执行目标对象--加锁** 演示synchronized可以用来修饰方法,叫做同步方法* 同时也支持修饰静态方法!!!** 当前案例中,使用对象锁对当前的这个Ticket加锁,目的是this是当前正在被调用的这个Ticket对象,* 不管是synchronized代码块中还是封装的一个synchronized方法,都可以保证使用的是同一个锁对象*/public class Ticket implements Runnable{//票数--静态方法不能调用非静态资源private static int number=100;//声明成员变量为定义锁对象private Object lock=new Object();//创建一个标记,让不同的人来执行不同的代码块private int x=0;@Overridepublic void run() {while (true){//进行取余的运算,模拟卖票的人if(x%2==0){//代码块//将完整的一套动作使用synchronized包裹//类名直接调用静态方法synchronized (Ticket.class){//线程暂停try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}//有票就卖if (number>0){String threadName=Thread.currentThread().getName();System.out.println(threadName+"正在销售第"+number+"张票");number--;}/*else {//没票了break;}*/}}else {// this.sell();Ticket.sell();}//当票数=0,就表示没票了if(number<0){break;}x++;}}public static synchronized void sell(){try {Thread.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}if (number>0){String threadName=Thread.currentThread().getName();System.out.println(threadName+"正在销售第"+number+"张票");number--;}}}
package com.igeek_07;/*** @author Lynn* @create 2020-12-15-10:44*/import com.igeek_05.Ticket;/*** 线程安全问题的解决方案 :* 同步代码块* synchronized(锁对象){* 可能出现线程安全问题的代码* }*/public class ThreadDemo {public static void main(String[] args) {//实例化目标Ticket ticket=new Ticket();//使用该卖票的目标创建多个线程Thread thread1=new Thread(ticket,"Jack");Thread thread2=new Thread(ticket,"Rose");Thread thread3=new Thread(ticket,"Tom");//启动多个线程thread1.start();thread2.start();thread3.start();}}
回顾
演示等待和唤醒机制
案例
package com.igeek_08;/*** @author Lynn* @create 2020-12-15-15:18*//*** 资源类(实体类)** 定义共享的数据,其中成员:name,sex*/public class Person {//不使用private是为了便于后面的操作String name;String sex;//状态标记/*** flag:* true:有数据,生产者等待被消费* false:没有数据,生产者生产,消费者等待*/boolean flag;public Person(){super();}}
package com.igeek_08;/*** @author Lynn* @create 2020-12-15-15:20*//*** 生产者** 生产者的执行目标:* 反复为Person对象赋值:Jack 男 与 Rose 女*/public class PutIn implements Runnable{//实例化对象--声明private Person person;//创建构造方法用于接收外部传入的共享数据Personpublic PutIn(Person person){this.person=person;}@Overridepublic void run() {//使用循环达到反复赋值的目的//如果是奇数,就生成Jack 男,如果是偶数,救生成Rose 女int i=0;while (true){synchronized (person){//判断状态flag是true还是falsewhile (person.flag){//有数据,就等待try {person.wait();//线程阻塞} catch (InterruptedException e) {e.printStackTrace();}}//如果没有数据或者是刚被唤醒//判断生产的内容if (i%2==1){//男person.name="Jack";try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}person.sex="男";}else {//女person.name="Rose";try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}person.sex="女";}System.out.println(person);System.out.println("生产了"+person.name+"-"+person.sex);//切换状态为trueperson.flag=true;//如果有消费者等待,就唤醒消费者,如果没有消费者等待,就不需要唤醒,相当于没有执行person.notify();}i++;}}}
package com.igeek_08;/*** @author Lynn* @create 2020-12-15-15:29*//*** 消费者目标对象** 反复的获取Person对象,并且打印*/public class GetOut implements Runnable{//实例化共享资源private Person person;public GetOut(Person person) {this.person=person;}@Overridepublic void run() {//使用循环获取while (true) {//利用共享对象作为锁synchronized (person){//获取判断状态--flagwhile (!person.flag){//没有数据就等待生产try {person.wait();} catch (InterruptedException e) {e.printStackTrace();}}//有数据或者是刚刚被唤醒try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}//进行获取操作String name= person.name;String sex= person.sex;System.out.println(person);System.out.println("消费了:"+name+"-"+sex);//切换状态为falseperson.flag=false;//唤醒生产者,如果有就唤醒,如果没有就不需要唤醒,相当于没有执行person.notify();}}}}
package com.igeek_08;/*** @author Lynn* @create 2020-12-15-15:15*//*** 演示等待和唤醒机制*/public class WaitNotifyDemo {public static void main(String[] args) {//实例化对象Person person=new Person();//创建生产者的执行目标PutIn putIn=new PutIn(person);//创建消费者的执行目标GetOut getOut=new GetOut(person);//开启生产者线程Thread putInThread=new Thread(putIn);putInThread.start();//开启消费者线程Thread getOutThread=new Thread(getOut);getOutThread.start();}}


