目录

一、多线程

1、线程的基础知识

1.1 什么是进程?什么是线程?

1.2 进程和线程的关系

1.3 多线程并发的理解

2、实现多线程

2.1 第一种方式:

2.2 第二种方式

2.3 第三种方式

3、线程的生命周期

4、获取线程名字

5、线程的sleep方法

5.1 关于sleep方法的面试题

5.2 终止线程的睡眠

6、线程的调度(了解)

6.1 常见线程调度模型

6.2 java中提供给的与线程调度有关的方法

7、同步编程模型和异步编程模型

8、线程安全(重点)

8.1 存在的安全问题

8.2 解决线程安全问题

8.3 模拟账户取款安全问题

8.4 扩大同步范围

8.5 三大变量的安全问题

8.6 synchronized面试题1

8.7 synchronized面试题2

8.8 死锁

8.9 以后开发中怎么解决线程安全问题

9、守护线程

9.1 守护线程概念:

9.2 守护线程实现

10、定时器

11、Object类中的wait和notify方法

11.1 概述

11.2 生产者和消费者模式理解

11.3 wait和notify代码


一、多线程

1、线程的基础知识

1.1 什么是进程?什么是线程?

进程是一个应用程序(一个进程是一个软件)

线程是一个进程中的执行场景/执行单元。

一个进程可以启动多个线程。

1.2 进程和线程的关系

举例:

阿里巴巴:进程

马云:阿里巴巴的一个线程

童文红:阿里巴巴的一个线程

京东:进程

刘强东:京东的一个线程

进程可以看作是现实生活当中的公司

线程可以看作是公司当中的某个公司

注意:

线程A和线程B的内存独立不共享。(阿里和京东资源不共享)

在java语言中:线程A和线程B,堆内存和方法区内存共享。但是栈内存独立,一个线程一个栈

java中之所以有多线程机制,目的就是为了提高程序的处理效率

思考:在使用了多线程之后,main方法结束,只是主线程结束了,主栈空了,其他的栈(线程)可能还在压栈弹栈

1.3 多线程并发的理解

2、实现多线程

java语言中,实现线程有两种方式。

java支持多线程机制,并且java已经将多线程实现了,我们只需要继承就可以

2.1 第一种方式:

编写一个类,直接继承java.lang.Thread,重写run方法

package 多线程;/*** 实现线程的第一种方式* 编写一个类,继承Thread*** 注意:* 方法体当中的代码永远都是自上而下执行的*** 以下程序的输出的结果有以下的特点:* 有先有后* 有多又少*/public class ThreadTest02 {public static void main(String[] args) {//这里是main方法,这里的代码属于主线程,在主栈里运行//新建一个分支线程对象MyThread myThread=new MyThread();//启动线程//myThread.run();  //不会启动线程,不会分配新的分支栈,因此这种方式还是在主栈中,就是单线程//调用start方法,start()方法的作用是启动一个分支线程,在JVM中开辟一个新的栈空间这段代码完成之后,瞬间就结束了。//这段代码的任务只是为了开启一个新的栈空间,只要新的栈空间开辟出来,start()方法就结束了。线程就启动成功了。//启动成功的线程会自动调用run方法,并且run方法在分支栈的栈底部(压栈)//run方法在分支栈的栈底部,main方法在主栈的栈底部。run和main是平级的myThread.start(); //这行代码不结束,下面的代码不会执行,但这行代码执行很快就结束了,//这里的代码还是主线程中for(int i=0;i<100;i++){System.out.println("主线程---"+i);}}
}class MyThread extends Thread{@Overridepublic void run() {//编写程序,这段程序运行在分支线程种(分支线)for(int i=0;i<100;i++){System.out.println("分支线程---"+i);}}
}

可以看见,使用start()方法,主线程和分支线程并发进行,互不干扰

package 多线程;/*** 实现线程的第一种方式* 编写一个类,继承Thread*** 注意:* 方法体当中的代码永远都是自上而下执行的*** 以下程序的输出的结果有以下的特点:* 有先有后* 有多又少*/public class ThreadTest02 {public static void main(String[] args) {//这里是main方法,这里的代码属于主线程,在主栈里运行//新建一个分支线程对象MyThread myThread=new MyThread();//启动线程myThread.run();  //不会启动线程,不会分配新的分支栈,因此这种方式还是在主栈中,就是单线程//调用start方法,start()方法的作用是启动一个分支线程,在JVM中开辟一个新的栈空间这段代码完成之后,瞬间就结束了。//这段代码的任务只是为了开启一个新的栈空间,只要新的栈空间开辟出来,start()方法就结束了。线程就启动成功了。//启动成功的线程会自动调用run方法,并且run方法在分支栈的栈底部(压栈)//run方法在分支栈的栈底部,main方法在主栈的栈底部。run和main是平级的//myThread.start(); //这行代码不结束,下面的代码不会执行,但这行代码执行很快就结束了,//这里的代码还是主线程中for(int i=0;i<100;i++){System.out.println("主线程---"+i);}}
}class MyThread extends Thread{@Overridepublic void run() {//编写程序,这段程序运行在分支线程种(分支线)for(int i=0;i<100;i++){System.out.println("分支线程---"+i);}}
}

可以看见,使用run();方法是没有启动新的线程的,因此不是多线程,只是一个方法的调用,先执行完分支线程的输出,然后才执行主线程的输出

2.2 第二种方式

编写一个类,实现java.lang.Runnable接口,实现run方法

package 多线程;public class ThreadTest03 {public static void main(String[] args) {//创建一个可运行的对象//MyRunnable r=new MyRunnable();//将可运行的对象封装成一个线程对象//Thread t=new Thread(r);//合并代码Thread t=new Thread(new MyThread());//启动线程t.start();for(int i=0;i<100;i++){System.out.println("主线程---"+i);}}
}//这并不是一个线程类,只是一个可运行的类,它还不是一个线程
class MyRunnable implements Runnable{@Overridepublic void run() {for(int i=0;i<100;i++){System.out.println("分支线程---"+i);}}
}

注意:第二种方式实现接口比较常用,因为一个类实现了接口,它还可以去继承别的类(Java是单继承),更加灵活。

附:通过匿名内部类实现

package 多线程;public class ThreadTest04 {public static void main(String[] args) {//采用匿名内部类的方式//这里是通过一个没有名字的类,new出来的对象/**注意:!!* 这里参数放new接口实际上是new[匿名] implements Runnable,并不是真的用接口new对象*/Thread t = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println("分支线程---" + i);}}});//启动线程t.start();for (int i = 0; i < 100; i++) {System.out.println("主线程---" + i);}}
}

2.3 第三种方式

实现Callable接口。(JDK8新特性)

这种方式实现的线程可以获取线程的返回值,之前的两种方式是不可以获取线程返回值的,因为run方法返回void。

系统委派一个线程去执行任务,该线程执行完任务之后,可能会有一个执行结果,我们怎么才能拿到这个执行结果呢?使用第三种方式:实现Callable接口方式。

package 多线程;import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;//JUC包下的,属于java的并发包,老JDK种没有这个包/*** 实现线程的第三种方法:* 实现Callable接口* 这种方式的优点:可以获取到线程的执行结果* 这种方式的缺点:效率低,在获取t线程执行结果的时候,当前线程受阻*/
public class ThreadTest14 {public static void main(String[] args) throws ExecutionException, InterruptedException {//第一步:创建一个“未来任务类”对象//参数非常重要,需要给一个Callable接口实现类对象//Callable是接口,new的话要使用匿名内部类FutureTask task=new FutureTask(new Callable() {@Overridepublic Object call() throws Exception { //call()方法相当于方法,只不过有一个返回值System.out.println("call method begin");Thread.sleep(1000*10);System.out.println("call method end");int a=100;int b=100;return a+b;//自动装箱,返回一个Object对象,因此300结果自动变成Integer。}});//创建线程对象Thread t=new Thread(task);//启动线程t.start();//这里是main方法,这是在主线程中,那么在主线程中,怎么获取t线程中的返回结果?//使用get()方法会导致"当前线程阻塞"Object obj=task.get();System.out.println("线程执行结果"+obj);//main方法这里的而程序要想执行必须等待get()方法的结束//而get()方法可能需要很久,因为get()方法是为了拿另一个线程的执行结果,但是另一个线程的执行是需要时间的System.out.println("Java");}
}

3、线程的生命周期

新建状态,就绪状态,运行状态,阻塞状态,死亡状态

4、获取线程名字

package 多线程;/*** 1、怎么获取当前线程对象?* Thread t=Thread.currentThread();* 返回值t就是当前线程** 2、获得线程对象名字。* 线程对象.getName("名字");** 3、修改线程对象名字* 线程对象.setName("名字");** 4、注意:当线程没有设置名字的时候,默认名字是Thread-0,Thread-1,Thread-2*/
public class ThreadTest05 {public void dosome(){Thread.currentThread().getName();}public static void main(String[] args) {//代码出现在main方法当中,所以当前线程就是主线程Thread currentThread=Thread.currentThread();//这是个静态方法System.out.println(currentThread.getName());System.out.println("----"+currentThread.getName());////创建线程对象MyThread2 t=new MyThread2();MyThread2 t2=new MyThread2();//获取线程名字System.out.println(t.getName());//设置线程的名字t.setName("tttt");String tName=t.getName();System.out.println(tName);t2.setName("qqqq");//启动线程t.start();//t线程启动t2.start();}
}
class MyThread2 extends Thread{public void run(){for(int i=0;i<100;i++){//currentThread就是当前线程对象,当前线程是谁启动的,当前线程的名字就是谁的//当t1线程执行run方法,那么这个线程就是t1,名字为tttt//当t2线程执行run方法,那么这个线程就是t2,名字为qqqqThread currentThread=Thread.currentThread();System.out.println(currentThread.getName()+i);//这个方法更加常用,下面两个方法有局限性//System.out.println(this.getName()+i);//System.out.println(super.getName()+i);}}
}

5、线程的sleep方法

package 多线程;/*** static void sleep(long millis)* 1、静态方法:Thread.sleep(1000);* 2、参数是毫秒* 3、作用:让当前线程进入休眠,进入”阻塞状态“,放弃占有CPU时间片,让给其他线程使用。*        这行代码出现在A线程种,A线程就会进入休眠* 4、Thread.sleep()方法,可以做到这种效果:*    间隔特定的时间,去执行一段 特点的代码,每隔多久执行一次*/public class ThreadTest06 {public static void main(String[] args) {try {Thread.sleep(1000*5);} catch (InterruptedException e) {e.printStackTrace();}//5秒之后执行这里的代码System.out.println("hello world");for(int i=0;i<10;i++){System.out.println(Thread.currentThread().getName()+"--->"+i);try {Thread.sleep(1000);}catch (InterruptedException e){e.printStackTrace();}}}
}

5.1 关于sleep方法的面试题

package 多线程;/*** 关于Thread.sleep()方法的一个面试题** 注意:sleep方法出现在哪里哪里睡,*/public class ThreadTest07 {public static void main(String[] args) {Thread t=new MyThread3();t.setName("t");t.start();try {//问题:这行代码会让线程t进入休眠状态吗?t.sleep(1000*5);  //在执行的时候还是会转换为Thread.sleep(1000*5)// 这行代码的作用是:让当前线程进入休眠,也就是说main线程进入休眠//这行代码出现在main方法种,main线程睡眠}catch (Exception e){e.printStackTrace();}System.out.println("hello world");}
}
class MyThread3 extends Thread{public void run(){for(int i=0;i<1000;i++){//如果在这里使用sleep方法,那么就是该分支线程睡眠System.out.println(Thread.currentThread().getName());}}
}

5.2 终止线程的睡眠

方法一:通过interrupt()方法

package 多线程;/*** sleep睡眠太久了,如果希望半道上醒来,你应该怎么办?也就是说怎么叫醒一个正在睡眠的线程?* 注意:这个不是终断线程的执行,是终断线程的睡眠*/
public class ThreadTest08 {public static void main(String[] args) {Thread t=new Thread(new MyRunnable2());t.setName("ttt");t.start();//希望5秒之后,t线程醒来(5秒之后主线程手里的活儿干完了)try {Thread.sleep(1000*5);} catch (InterruptedException e) {e.printStackTrace();}//终断t线程的睡眠(这种终端睡眠的方式依靠了java的异常处理机制,会在sleep出报出异常来停止)t.interrupt(); //干扰,一盆冷水过去!}
}
class MyRunnable2 implements Runnable{//重点:run方法当中的异常不能throw,只能try catch,因为run()方法在父类中没有抛出任何异常,子类不能比父类抛出更多异常。@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"----> begin");try {Thread.sleep(1000*60*60*24*365);} catch (InterruptedException e) {e.printStackTrace();// 如果不打印异常,那么看起来就是终断异常}System.out.println(Thread.currentThread().getName()+"----> end");System.out.println(Thread.currentThread().getName());//其他方法可以throws(这些不是继承的父类方法,而是自己的方法)/*public void doOther(){}*/}
}

方法二:强行终止线程进行(合理方法)

package 多线程;public class ThreadTest10 {public static void main(String[] args) {MyRunnable4 r=new MyRunnable4();Thread t=new Thread(r);t.setName("t");t.start();try {Thread.sleep(1000*5);} catch (InterruptedException e) {e.printStackTrace();}//终止线程,你想什么时候终止t的执行,那么你把标记改为false就结束了r.run=false;}
}
class MyRunnable4 implements Runnable{//打一个布尔标记boolean run=true;@Overridepublic void run() {for(int i=0;i<10;i++){if(run) {System.out.println(Thread.currentThread().getName() + "--->" + i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}else{return;}}}
}

6、线程的调度(了解)

6.1 常见线程调度模型

常见的线程调度模型有两种:抢占式调度线程和均分式调度模型

抢占式调度线程:哪个线程的优先级比较高,抢到CPU时间片的概率就高一些。java中采用的就是抢占式调度线程

均分式调度模型:平均分配CPU时间片,每个线程占有的CPU时间片时间长度一样。平均分配,一切平等。

6.2 java中提供给的与线程调度有关的方法

实例方法:

1、void setPriority(int newPriority) //设置线程的优先级

2、int getPriority() //获取线程优先级

最高优先级是10,最低优先级是1,默认优先级是5。

package 多线程;public class ThreadTest11 {public static void main(String[] args) {//设置主线程的优先级为1Thread.currentThread().setPriority(1);/*System.out.println("最高优先级"+Thread.MAX_PRIORITY);System.out.println("最低优先级"+Thread.MIN_PRIORITY);System.out.println("默认优先级"+Thread.NORM_PRIORITY);//获取当前线程对象,获取当前线程的优先级Thread currentThread=Thread.currentThread();//main线程的默认优先级是:5System.out.println(currentThread.getName()+"线程的默认优先级是"+currentThread.getPriority());
*/Thread t=new Thread(new MyRunnable5());t.setPriority(10);t.setName("t");t.start();//优先级较高的,只是抢到CPU的时间片相对多一些(大概率方向偏向于优先级比较高的)for(int i=0;i<100;i++){System.out.println(Thread.currentThread().getName()+"--->"+i);}}
}
class MyRunnable5 implements Runnable{@Overridepublic void run() {//获取线程优先级,默认为5//System.out.println(Thread.currentThread().getName()+"线程的默认优先级"+Thread.currentThread().getPriority());for(int i=0;i<100;i++){System.out.println(Thread.currentThread().getName()+"--->"+i);}}
}

3、void join() 合并线程

package 多线程;public class ThreadTest13 {public static void main(String[] args) {System.out.println("main begin");Thread t=new Thread(new MyRunnable7());t.setName("t");t.start();//合并线程,t合并到当前线程中,当前线程受堵塞try {t.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("main over");}
}
class MyRunnable7 implements Runnable{@Overridepublic void run() {for(int i=0;i<100;i++){System.out.println(Thread.currentThread().getName()+"--->"+i);}}
}

静态方法:

1、static void yield() //让位方法。暂停当前正在执行的线程对象,并且执行其他线程。yiled()方法不是阻塞方法,让当前线程让位,让给其他线程使用。yield()方法的执行会让当前线程从"运行状态"回到就绪状态。

package 多线程;/*** 让位,让当前线程暂停,回到就绪状态,让给其他线程* 静态方法:Thread.yield();*/
public class ThreadTest12 {public static void main(String[] args) {Thread t=new Thread(new MyRunnable6());t.setName("t");t.start();for(int i=0;i<=100;i++){System.out.println(Thread.currentThread().getName()+"--->"+i);}}
}
class MyRunnable6 implements Runnable{@Overridepublic void run() {for(int i=1;i<=100;i++){//每10个让位一次if(i%10==0){Thread.yield();}System.out.println(Thread.currentThread().getName()+"--->"+i);}}
}

7、同步编程模型和异步编程模型

8、线程安全(重点)

8.1 存在的安全问题

从上图思考,什么时候数据在多线程并发的环境下会存在安全问题呢?

条件1:多线程并发。

条件2:有共享数据。

条件3:共享数据有修改的行为

满足以上3个条件以后,就会存在线程安全问题

8.2 解决线程安全问题

8.3 模拟账户取款安全问题

Test类:

package 线程安全测试;public class Test {public static void main(String[] args) {//创建账户对象Account act=new Account("act001",10000);//创建两个线程Thread t1=new AccountThread(act);Thread t2=new AccountThread(act);//设置namet1.setName("t1");t2.setName("t2");//启动线程取款t1.start();t2.start();}
}

Account类:

package 线程安全测试;/*** 银行账号*/
public class Account {//账号private String actno;//余额private double balance;public Account() {}public Account(String actno, double balance) {this.actno = actno;this.balance = balance;}public String getActno() {return actno;}public void setActno(String actno) {this.actno = actno;}public double getBalance() {return balance;}public void setBalance(double balance) {this.balance = balance;}//取款的方法:public void  withdraw(double money){//t1和t2并发这个方法。(t1和t2是两个栈,两个栈操作堆中的同一个对象)double before=this.getBalance();double after=before-money;//更新余额//当t1执行到这里了,但是还没有来得及执行这行代码,t2线程进来withdraw方法了,此时一定出问题//在这里模拟网络延迟,100%出现问题try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}this.setBalance(after);}
}

AccountThread类:

package 线程安全测试;public class AccountThread extends Thread{//两个线程必须共享同一个账户对象private Account act;//通过构造方法传递public AccountThread(Account act){this.act=act;}public void run(){//run方法的执行表示取款操作//假设取款5000double money=5000;//多线程并发实现取款act.withdraw(money);System.out.println(Thread.currentThread().getName()+"对"+act.getActno()+"取款成功,余额"+act.getBalance());}
}

结果:

解决方法:增加synchronized () 方法

package 线程安全测试.解决方案;/*** 银行账号* 不适用线程同步机制,多线程对同一个用户进行取款,出现线程安全问题*/
public class Account {//账号private String actno;//余额private double balance;public Account() {}public Account(String actno, double balance) {this.actno = actno;this.balance = balance;}public String getActno() {return actno;}public void setActno(String actno) {this.actno = actno;}public double getBalance() {return balance;}public void setBalance(double balance) {this.balance = balance;}//取款的方法:public void withdraw(double money) {//以下这几行代码必须是线程排队的,不能并发//一个线程吧这里的代码全部执行结束之后,另一个线程才能进来/*** 线程同步机制的语法:*  synchronized () {*      //线程同步代码块*  }**  synchronized后面小括号中传的这个数据是十分重要的。*  这个数据必须是多线程共享的数据,才能达到多线程排队***  ()中写什么?*      那么看你想让哪些线程同步,假设有t1,t2,t3,t4,t5,有5个线程*      如果你只希望t1,t2,t3排队,t4,t5不需要排队,你一定要在()中写一个t1,t2,t3共享的对象,而t4和t5不共享*//*这里的共享对象是:账户对象。* 账户对象是共享的,那么this就是账户对象* 不一定是this,只要是多线程共享的那个对象*//*在java中,任何一个对象都有一把锁,其实这把锁就是标记(只是把他叫作锁)100个对象,100把锁以下代码的执行原理1、假设t1和t2线程并发,开始执行以下代码的时候,肯定一个先一个后。2、假设t1先执行了,遇到synchronized,这个时候自动找”后面共享对象“的对象锁,找到之后,并占有这把锁的,直到同步代码块代码结束,这把锁才会释放3、假设t1已经占有这把锁,此时t2也遇到了synchronized关键字,也去占有后面共享对象的这把锁,结这把锁被t1占有,t2只能在同步代码块外面等待t1的结束,直到t1把同步代码结束了,t1会归还这把锁,此时t2才可以占有这把锁,进入同步代码块执行程序需要注意的是:这个共享对象一定要选好了。这个共享对象一定是你需要排队执行的这些线程对象所共享的*///synchronized (this) {//最好选这个,因为在这个例子中,别的线程操作的是别的账户,只要不是同时对同一个账户同时取款的都不需要等待//this指向的Account只有一个,那么Account里面的所有成员变量都只有一个,因此都可以作为参数//synchronized (actno) {//可以使用//Object obj2=new Object();//不能使用,注意这里是局部变量,每一个对象new的时候都会创建一个新的obj2对象,不是共享对象//synchronized (obj2){//synchronized ("abc"){//可以使用,因为"abc"在字符串常量池中,而字符串常量池是共享的(此时所有线程都会同步)synchronized (this) {double before = this.getBalance();double after = before - money;try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}this.setBalance(after);}}
}

8.4 扩大同步范围

package 线程安全测试.解决方案;public class AccountThread extends Thread{//两个线程必须共享同一个账户对象private Account act;//通过构造方法传递public AccountThread(Account act){this.act=act;}public void run(){//run方法的执行表示取款操作//假设取款5000double money=5000;//多线程并发实现取款synchronized (act) {//这种方法也可以,只不过扩大了同步范围,效率更低。//synchronized (this){} 这里的this是AccountThread对象,这个对象不共享act.withdraw(money);}System.out.println(Thread.currentThread().getName()+"对"+act.getActno()+"取款成功,余额"+act.getBalance());}
}

8.5 三大变量的安全问题

synchronized重点:
synchronized重点(转载)https://blog.csdn.net/qq_41279172/article/details/104308214

8.6 synchronized面试题1

package 线程安全测试.synchronized面试题目;/*** doOther方法执行的时候需要等待doSome结束吗?*  不需要,因为doOther()方法没有使用synchronized,没有被锁*/public class Exam01 {public static void main(String[] args) throws InterruptedException {MyClass mc=new MyClass();Thread t1=new MyThread(mc);Thread t2=new MyThread(mc);t1.setName("t1");t2.setName("t2");t1.start();Thread.sleep(1000);//这个睡眠的作用是保证t1线程先进行t2.start();}
}class MyThread extends Thread{private MyClass mc;public MyThread(MyClass mc){this.mc=mc;}public void run(){if(Thread.currentThread().getName().equals("t1")){mc.doSome();}if(Thread.currentThread().getName().equals("t2")){mc.doOther();}}
}class MyClass{public synchronized void doSome() {System.out.println("doSome begin");try {Thread.sleep(1000 * 10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("doSome over");}//如果这里改成public synchronized void doOther{},执行doOther时就需要public void doOther(){System.out.println("doOther begin");System.out.println("doOther over");}}

8.7 synchronized面试题2

package 线程安全测试.synchronized面试题2.synchronized面试题目;/*** doOther方法执行的时候需要等待doSome结束吗?* 需要,因为静态方法是类锁,类锁不管创建了几个对象,类锁只有一把。*/public class Exam01 {public static void main(String[] args) throws InterruptedException {MyClass mc=new MyClass();Thread t1=new MyThread(mc);Thread t2=new MyThread(mc);t1.setName("t1");t2.setName("t2");t1.start();Thread.sleep(1000);//这个睡眠的作用是保证t1线程先进行t2.start();}
}class MyThread extends Thread{private MyClass mc;public MyThread(MyClass mc){this.mc=mc;}public void run(){if(Thread.currentThread().getName().equals("t1")){mc.doSome();}if(Thread.currentThread().getName().equals("t2")){mc.doOther();}}
}class MyClass{public synchronized static void doSome() {System.out.println("doSome begin");try {Thread.sleep(1000 * 10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("doSome over");}//如果这里改成public synchronized void doOther{},执行doOther时就需要public void doOther(){System.out.println("doOther begin");System.out.println("doOther over");}}

8.8 死锁

package 线程安全测试.死锁;/*** 死锁代码要会写,一般面试官会要求你写* 只有会写,才能在以后的开放中避免这种现象* 死锁很难调试*//*** o1锁了以后睡了,o2锁了也睡了,醒来以后互相卡住,无法继续* 因此 synchronized使用时不要嵌套使用*/
public class DeadLock {public static void main(String[] args) {Object o1=new Object();Object o2=new Object();Thread t1=new MyThread1(o1,o2);Thread t2=new MyThread2(o1,o2);t1.start();t2.start();}
}
class MyThread1 extends Thread{Object o1;Object o2;public MyThread1(Object o1,Object o2){this.o1=o1;this.o2=o2;}public void run(){synchronized (o1){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (o2){}}}
}
class MyThread2 extends Thread{Object o1;Object o2;public MyThread2(Object o1,Object o2){this.o1=o1;this.o2=o2;}public void run(){synchronized (o2){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (o1){}}}
}

8.9 以后开发中怎么解决线程安全问题

9、守护线程

9.1 守护线程概念:

9.2 守护线程实现

package 守护线程;public class ThreadTest1 {public static void main(String[] args) {Thread t=new BakDateThread();t.setName("备份数据的线程");//启动线程之前,将线程变为守护线程,那么死循环在用户线程结束后会自动结束t.setDaemon(true);t.start();//主线程:主线程就是用户线程for(int i=0;i<10;i++){System.out.println(Thread.currentThread().getName()+"-->"+i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
class BakDateThread extends Thread{public void run(){int i=0;while(true){System.out.println(Thread.currentThread().getName()+"-->"+(++i));try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}

死循环自动终止了

10、定时器

package 计时器;import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;/*** 使用定时器指定定时任务*/
public class TimerTest {public static void main(String[] args) throws ParseException {//创建定时器对象Timer timer = new Timer();//Timer timer=new Timer(true); //将计数器作为守护线程的方式//指定定时任务//timer.schedule(定时任务,第一次执行时间,间隔多久执行一次);SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date firstTime =sdf.parse("2022-02-28 19:33:00");timer.schedule(new LogTimeTask(),firstTime,1000*10);//也可以使用匿名内部类实例化TimerTask抽象方法timer.schedule(new TimerTask(){@Overridepublic void run() {}},firstTime,1000*10);}
}}
}//编写一个定时任务类
//假设这是ige记录日志的定时任务
//实例化抽象类TimeTask通过子类继承,然后实例化子类
class LogTimeTask extends TimerTask{@Overridepublic void run() {//编写你需要执行的任务就行了SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String strTime=sdf.format(new Date());System.out.println(strTime+"成功完成了一次数据备份");}
}

11、Object类中的wait和notify方法

11.1 概述

11.2 生产者和消费者模式理解

11.3 wait和notify代码

要多理解此处代码和原理

package 多线程;import java.util.ArrayList;
import java.util.List;/*** 1、使用wait方法和notify方法实现“生产者和消费者模式”** 2、什么是’生产者和消费者模式“?* 生产线程负责生产,消费线程负责消费。* 线程生产和消费线程要达到均衡。* 这是一种特殊的业务需求,在这种特殊的情况下需要使用wait方法和notify方法。** 3、wait和notify方法不是线程对象的方法,是普通java对象都有的方法。** 4、wait方法和notify方法建立在线程同步的基础之上。因为多线程要同时操作一个仓库。有线程安全问题** 5、wait方法作用:o.wait()让正在o对象上活动的线程t进入等待状态,并且释放t线程之前占有的o对象的锁** 6、notify方法作用:o.notify()让正在o对象上等待的线程唤醒,** 7、模拟这样一个需求:*     仓库我们采用List集合。*     1个元素就表示仓库满了。*     如果List集合中元素个数为0,就表示仓库空了。*     保证List集合中永远都是最多存储1个元素*     必须做到这种效果:生产1个消费一个**/
public class ThreadTest15 {public static void main(String[] args) {//创建一个仓库对象,共享的List list=new ArrayList();//创建两个线程对象//生产者线程Thread t1=new Thread(new Producer(list));//消费者线程Thread t2=new Thread(new Consumer(list));t1.setName("生产者线程");t2.setName("消费者线程");t1.start();t2.start();}
}
//生产线程
class Producer implements Runnable {//仓库private List list;public Producer(List list) {this.list = list;}@Overridepublic void run() {//一直生产(使用死循环模拟一直)while (true) {//给仓库对象list加锁synchronized (list) {if (list.size() > 0) {//大于0,说明仓库中已经又1个元素//当前线程进入等待状态,并且释放Producer之前占有的list集合的锁try {list.wait();} catch (InterruptedException e) {e.printStackTrace();}}//程序能执行到这里说明仓库是空的,可以生产Object obj=new Object();list.add(obj);System.out.println(Thread.currentThread().getName()+"-->"+obj);//唤醒生产者生产list.notifyAll();}}}
}//消费线程
class Consumer implements Runnable{private List list;public Consumer(List list){this.list=list;}@Overridepublic void run() {//一直消费while(true){synchronized (list){if(list.size()==0){//仓库已经空了,消费者线程等待,释放掉list集合的锁try {list.wait();} catch (InterruptedException e) {e.printStackTrace();}}//程序能执行到此处说明仓库中有数据,进入消费Object obj=list.remove(0);System.out.println(Thread.currentThread().getName()+"-->"+obj);//唤醒生产者生产list.notifyAll();}}}
}

java学习笔记第七周(二)相关推荐

  1. Java学习的第七周之简单的SQL语句

    Java学习的第七周之简单的SQL语句 一 简单SQL语句: 1.查询表结构 desc 表名; 2.插入数据 --方式一: 默认全部插入数据INSERT INTO 表名 VALUES (值1,值2,值 ...

  2. Java学习笔记(七)

    在完成对C语言的学习后,我最近开始了对C++和Java的学习,目前跟着视频学习了一些语法,也跟着敲了一些代码,有了一定的掌握程度.现在将跟着视频做的笔记进行整理.本篇博客是整理Java知识点的第七篇博 ...

  3. Java学习笔记(第二周)

    一.Java程序的基本结构 一个Java应用程序是由若干个类组成的. 通常将类的属性称之为类的全局变量(成员变量),将方法中的属性称之为局部变量.全局变量声明在类体中,局部变量声明在方法体中.全局变量 ...

  4. java学习笔记-第七章:面向对象编程(基础部分)

    第七章:面向对象编程(基础部分) 总体内容 类与对象 引出类与对象 类与对象概述 类与对象的关系示意图 属性概念及其细节 类与对象快速入门案例 对象内存布局 类与对象内存分配机制 引申:java内存的 ...

  5. Java学习笔记(七)--格式化字符串及格式输出

    一.格式化字符串 String 类的静态 format() 方法用于创建格式化的字符串. 1.format(String format, Object···args) 该方法使用指定的格式字符串和参数 ...

  6. 带有毫秒 转换日期_【Java学习笔记(七)】之日期类的介绍

    本文章由公号[开发小鸽]发布!欢迎关注!!! 老规矩--妹妹镇楼: 一. Date类 (一) 定义 代表了一个特定的时间,精确到毫秒 (二) 构造方法 1. public Date() 构造并初始化 ...

  7. 【Java学习笔记(七十六)】之 泛型编程, 泛型类,泛型方法,类型参数限定,类型擦除

    本文章由公号[开发小鸽]发布!欢迎关注!!! 老规矩–妹妹镇楼: 一. 为什么要使用泛型编程 (一) 未使用泛型编程时 在没有泛型编程的时候,我们要实现同一个功能,但是一个参数可能有不同的类型,就只能 ...

  8. java学习笔记--黑马徐老师二

    153 file文件 package File;import java.io.File;/*** 学会创建file对象,定位操做系统的文件*/ public class FileDemo {publi ...

  9. JAVA学习笔记 第三周

    18 面向对象 18.1基本概念 18.1.1 对象 万物皆对象,对象有特征和行为,也就是属性和方法. 例如 人有特征 身高体重姓名.. 人有行为 奔跑 吃饭睡觉.. 18.1.2属性和方法 类是由属 ...

最新文章

  1. 分享一个MD5加密的工具类
  2. linux 开启独立iptables日志
  3. 编程疑难杂症の设置正确却无效的事件代码
  4. JQuery中的基本筛选选择器
  5. 小米11 Pro最新渲染图曝光:后置体积巨大的矩阵四摄
  6. 笨办法学python在线阅读_『笨办法』学Python 3
  7. vs.net 2005中文版下载
  8. Android应用程序开发入门
  9. mysql查询一张表中一共有多少条数据
  10. 加速度传感器,磁场传感器和陀螺仪传感器案例
  11. 实战一:输出“王者荣耀”的游戏角色
  12. python 微服务 网关_关于API微服务网关
  13. 机器学习 周志华 第三章 推导详细过程
  14. spark-streaming 编程(二) word count单词计数统计
  15. 产品公司解决方案、解决方案公司解决方案,可能你做了一辈子IT你也不知道...
  16. 【python】opencv教程CV2模块——图片处理,HSV、色调、亮度调节
  17. 2022国内网络安全事件大盘点
  18. esxi显卡给2个虚拟机_使用虚拟机ESXI,显卡直通VM,使一台实体机虚拟成HTPC、WEB服务器、NAS服务器等经验 - 小众知识...
  19. 西游记中孙悟空的蜕变
  20. 【可视化分析案例】用python分析Top100排行榜数据

热门文章

  1. html中1em等于多少像素,CSS:度量单位(px,em,rem,vw,%等)
  2. linux扁平化设备树,如何从Linux驱动程序访问和调试FDT / DTS设备树(seg-fault)
  3. 机器学习Sklearn总结2——分类算法
  4. Unity iOS打包发布流程
  5. 【大数据】HBase启动
  6. GroupNormalization
  7. Android集成百度定位,超详细,拒绝坑,附demo!!!
  8. 步进电机转速与脉冲频率的关系(这篇文章有些地方似乎不对)
  9. 天翼云电脑和企业安全 “锁”了
  10. 百度地图SDK for iOS v2.0.0全新发布