多线程
1.进程和线程
- 进程是一个应用程序(1个进程是一个软件)
- 线程是一个进程中的执行场景/执行单元
- 一个进程可以启动多个线程。
- 两者关系:进程A和进程B内存独立不共享。线程A和线程B 堆内存和方法区内存共享,栈内存独立,一个线程一个栈,各栈之间互不干扰,这就是多线程并发。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xWIkp7IL-1602401030284)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200927102902302.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7H082ALo-1602401030286)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200927103521561.png)]
package com.codesing;
/*
* 除垃圾回收线程外,有几个线程??????
* 1个,只有一个主线程main,只有一个栈
* */
public class ThreadTest01 {
public static void main(String[] args) {
System.out.println("main-begin");
m1();
System.out.println("main-over");
}
private static void m1() {
System.out.println("m1-begin");
m2();
System.out.println("m1-over");
}
private static void m2() {
System.out.println("m2-begin");
m3();
System.out.println("m2-over");
}
private static void m3() {
System.out.println("m3-begin");
System.out.println("m3-over");
}
}
2.java中实现线程方式
- 编写一个类,继承java.lang.Thread,重写run()方法。
package com.codesing;
/*
* 1.myThread.start();这段代码不结束,for (int i = 0; i < 100; i++) 这段代码不会执行???????
* 2.是的,在方法体中,代码始终是自上而下执行的。只不过myThread.start()瞬间结束而已。
* */
public class ThreadTest02 {
//这是主线程
public static void main(String[] args) {
//新建一个分支线程
MyThread myThread = new MyThread();
// myThread.run();//这里只是调用方法,没有开辟栈空间,没有启动线程,(只是单线程)
//启动线程:star():启动一个分支线程,未其在JVM中开辟一个新栈空间,执行完瞬间结束。
//线程启动,自动调用run()方法,而且run()方法在分支栈底部(压栈)
//main()方法在主栈底部,run()方法在分栈底部,main()和run()是平级的。
myThread.start();
for (int i = 0; i < 100; i++) {
System.out.println("主线程---》"+ i);
}
}
}
//这是分支线程
class MyThread extends Thread{
@Override
public void run() {//这个方法必须重写,相当于main()方法一样
for (int i = 0; i <100 ; i++) {
System.out.println("分支线程----》"+i);
}
}
}
- 编写一个类,实现java.lang.Runnable接口,重写run方法(可以匿名内部类实现)
package com.codesing;
/*
* 第二种实现接口:Runnable
* */
public class ThreadTest03 {
public static void main(String[] args) {
//创建一个可运行对象
Myrunnable myrunnable = new Myrunnable();
//封装成一个线程对象
Thread thread = new Thread(myrunnable);
//Thread thread = new Thread(new Myrunnable());//这是合并写法
//启动线程
thread.start();
for (int i = 0; i < 100; i++) {
System.out.println("主线程---》"+ i);
}
}
}
//创建可运行类
class Myrunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("分支线程---》"+ i);
}
}
}
注意:一般会用第二种方法,因为实现了一个类,还可以继承其他类,而java是一个单继承模式,第一用种方式有局限性。
3.线程生命周期
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YgfMoRIB-1602401030288)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200927133343653.png)]
- 新建状态-就绪状态-阻塞状态-运行状态-死亡状态
4.常用方法
4.1 Thread.currentThread()
package com.codesing;
/*
* static Thread.currentThread() 静态方法,获取当前线程对象
* */
public class ThreadTest04 {
public static void main(String[] args) {
//获取当前线程对象引用
Thread cruuentthread= Thread.currentThread();
System.out.println("当前线程名字:"+ cruuentthread.getName());
//创建一个线程对象
MyThread01 myThread01 = new MyThread01();
//获取线程名字
System.out.println(myThread01.getName());//Thread-0
//设置线程名字
myThread01.setName("t1");
//再次获取线程名字
System.out.println(myThread01.getName());//t1
//启动线程
myThread01.start();
//创建一个线程对象
MyThread01 myThread02 = new MyThread01();
//获取线程名字
System.out.println(myThread02.getName());//Thread-1
//设置线程名字
myThread02.setName("t2");
//再次获取线程名字
System.out.println(myThread02.getName());//t2
myThread02.start();
}
}
class MyThread01 extends Thread{
@Override
public void run() {
//获取当前线程对象的引用
System.out.println("当前线程名字:"+ Thread.currentThread().getName());
}
}
4.2 sleep(long millis)
package com.codesing;
/*
* 1. static void sleep(long millis) 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
* 2. 让当前线程进入休眠,处于”阻塞状态“,放弃占有CPU时间片,让其他线程使用。
* 3. 在哪个线程调用,就让这个休眠
* sleep()方法:可以做到:间隔特定时间,去执行特定代码,每隔多久执行一次。
* */
public class ThreadTest05 {
public static void main(String[] args) {
// try {
// Thread.sleep(1000*5);//让当前线程休眠5秒
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// //5秒之后执行这行代码
// System.out.println("hello,world!");
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
}
}
package com.codesing;
public class ThreadTest05_1 {
public static void main(String[] args) {
//sleep面试题:
Thread t = new MyThread02();
t.setName("t");
t.start();
try {//这里会让t线程休眠吗??????
t.sleep(1000*5);//在执行的时候,还是会转换成:Thread.sleep()执行
//让当前线程进入休眠状态,当前线程是main。所以会让main线程休眠5秒
} catch (InterruptedException e) {
e.printStackTrace();
}
//5秒之后执行
System.out.println("Hello main Sleep over!");
}
}
class MyThread02 extends Thread{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName()+"--->"+ i);
}
}
}
4.3 interrupt()
package com.codesing;
/*
* 如何唤醒(中断睡眠)一个正在睡眠的线程。
* 不是中断线程的执行,只是终止线程睡眠。
*
* */
public class ThreadTest05_2 {
public static void main(String[] args) {
//创建一个线程对象
Thread t = new Thread(new myRuunable01());
t.setName("t");
//启动线程
t.start();
//希望5秒后t线程醒来
try {
Thread.sleep(1000*5);
} catch (InterruptedException e) {
e.printStackTrace();
}
//5秒之后t中断睡眠(这种中断方式靠java的异常机制,打印异常信息)
t.interrupt();//干扰,会让
}
}
class myRuunable01 implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"----->begin");
//这里run()方法:只能tyr catch,不能在方法上throws异常。
//因为,run()方法在父类中没有抛出异常,子类不能比父类抛出更多的异常
try {
Thread.sleep(1000*24*60*60*365);//让这个线程睡一年
} catch (InterruptedException e) {
e.printStackTrace();
}
//其他方法可以throws,因为不是继承的方法,但在这里只能try catch,不能在类上抛出。
try {
doSome();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"----->end");
}
//这个方法可以抛出throsws异常,
private void doSome() throws InterruptedException {
Thread.sleep(1000);
System.out.println("我怎么被叫醒了");
}
}
4.4 合理终止一个线程
package com.codesing;
/*
* 合理终止一个线程:
* 1.stop()方法:已弃用,可能会丢失数据
* 2.设置一个布尔标识,推荐
* */
public class ThreadTest06 {
public static void main(String[] args) {
myRuun r = new myRuun();
Thread t = new Thread(r);
t.setName("t");
t.start();
//模拟5秒
try {
Thread.sleep(1000*5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("caonima");
//终止t线程
r.run = false;
}
}
class myRuun implements Runnable{
//打一个布尔标识
boolean run = true;
public 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;
}
}
}
}
4.5 线程调度
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BjAqWzev-1602401030290)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928124350056.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7MsdMj5n-1602401030292)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928124503682.png)]
4.5 优先级问题
package com.codesing;
public class ThreadTest07 {
public static void main(String[] args) {
System.out.println("最高优先级"+Thread.MAX_PRIORITY );//10
System.out.println("最低优先级"+Thread.MIN_PRIORITY);//1
System.out.println("默认优先级"+Thread.NORM_PRIORITY);//5
//设置线程优先级
Thread.currentThread().setPriority(1);
System.out.println(Thread.currentThread().getPriority());//1
myRun7 r = new myRun7();
Thread thread = new Thread(r);
thread.start();
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"线程:"+i);
}
}
}
class myRun7 implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getPriority());
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"线程:"+i);
}
}
}
4.6 线程让位Thread.yield()
package com.codesing;
/*
* 线程让位:静态方法 Thread.yield()
* 当前线程暂停,回到就绪状态,让给其他线程
* */
public class ThreadTest08 {
public static void main(String[] args) {
Thread thread = new Thread(new myRun8());
thread.setName("t");
thread.start();
for (int i = 0; i < 10000; i++) {
System.out.println(Thread.currentThread().getName()+"---->"+i);
}
}
}
class myRun8 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
if (i%100 == 0){//每100让位一次
Thread.yield();//当前线程暂停,让位给主线程
continue;
}
System.out.println(Thread.currentThread().getName()+"---->"+i);
}
}
}
4.7 线程合并:Thread.join()
package com.codesing;
/*
* 线程合并:Thread.join()
* */
public class ThreadTest09 {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+"----begin");
Thread t = new Thread(new myRun9());
t.start();
//合并线程:阻塞当前线程,t线程执行,直到t结束,当前线程继续执行
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"----over");//始终最后输出
}
}
class myRun9 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000000; i++) {
System.out.println(Thread.currentThread().getName()+"---->"+i);
}
}
}
5.线程安全
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5uZOKwOd-1602401030294)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928134430993.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aFZlZK8H-1602401030295)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928134512278.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8391dLy8-1602401030296)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928134531289.png)]
5.1 模拟账户取款
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-21qxJFAM-1602401030297)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928144605807.png)]
package com.codesing.ThreadSafe2;
public class Application {
public static void main(String[] args) {
//创建一个账户
Account account = new Account("account", 10000);
//创建两个线程
ThreadAccount user1 = new ThreadAccount(account);
ThreadAccount user2 = new ThreadAccount(account);
user1.setName("user1");
user2.setName("user2");
//启动线程取款
user1.start();
user2.start();
}
}
package com.codesing.ThreadSafe2;
public class ThreadAccount extends Thread{
//共享同一个账户
private Account account;
//通过构造方法传递过来账户对象
public ThreadAccount(Account account){
this.account = account;
}
@Override
public void run() {
//执行取款操作,假设取5000
int money = 5000;
account.withDraw(money);
System.out.println(Thread.currentThread().getName()+"对"+account.getActno()+"取款后,剩余:"+account.getBalance());
}
}
- 未使用同步机制(E:\学习文档\数据库课程设计\IDE-Workspace\java多线程\001-java多线程\src\com\codesing\ThreadSafe)
package com.codesing.ThreadSafe;
/*
* 银行取款:
* 1.未使用线程同步机制,多线程对同一个账户取款,出现线程安全问题
* ======================
* user1取款后,剩余:5000
* user2取款后,剩余:5000
* =======================
*
* */
public class Account {
private String actno;//账号名
private int balance;//余额
public Account(String actno, int balance) {
this.actno = actno;
this.balance = balance;
}
public String getActno() {
return actno;
}
public void setActno(String actno) {
this.actno = actno;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
//取款方法:
public void withDraw(int money){
//user1,user2线程并发取款,(user1,user2两个栈,操作堆中同一个对象。)
//取款前余额
int before = this.balance;
//取款后余额
int after = this.balance - money;
//设置网络延迟,100%出问题
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//更新余额
//思考:t1执行到这里,没来及更新,t2线程执行withDraw了。此时会出问题
this.setBalance(after);
}
}
- 使用同步机制(E:\学习文档\数据库课程设计\IDE-Workspace\java多线程\001-java多线程\src\com\codesing\ThreadSafe2)
package com.codesing.ThreadSafe2;
/*
* 银行取款:
*.使用线程同步机制synchronized(),解决线程安全问题
* ======================
* user1对account取款后,剩余:5000
* user2对account取款后,剩余:0
* =======================
*
* */
public class Account {
private String actno;//账号名
private int balance;//余额
public Account(String actno, int balance) {
this.actno = actno;
this.balance = balance;
}
// 1. Object obj = new Object();//实例变量(Account对象是多线程共享的,则Account对象中的实例变量obj对象也是共享的)
//所以,下面代码synchronized (obj)写成这样也是可以的,因为共享线程会排队。
public String getActno() {
return actno;
}
public void setActno(String actno) {
this.actno = actno;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
//取款方法:
public void withDraw(int money
/*
* 2.synchronized (actno)//这么写也能做到线程排队,actno也是一个共享对象。
* 3.synchronized ("abc")//这样写,abc在字符串常量池中,所有线程共享,这样会导致,所有线程同步(一人取款,全天下人等)
* 4.synchronized (null)// 空指针异常,报错
*/
// 5.Object obj1= new Object();//这里obj是一个局部变量,不是共享对象,synchronized (obj1 )这样写就不安全了,没有排队,各new各的变量。
//以下几行代码排队执行,不能并发。
synchronized (this){//这里账户对象共享。this(不一定是this,只要是共享对象就可以)
int before = this.balance;
int after = before - money;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setBalance(after);
}
}
}
5.2 synchronized()详解
synchronized (){
线程同步代码块
}
synchronized (),括号中写什么????
必须是多线程共享的数据,才能达到线程同步,假设线程t1,t2,t3,t4,t5,你只想要t1,t2,t3排队,t4,t5不排队。
那么,你就在()中写一个t1,t2,t3,共享的对象。而这个对象对t4,t5不共享。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-578aYdPl-1602401030298)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928135726983.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jj6JGq5t-1602401030300)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928135944289.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KXBVmLlY-1602401030301)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928140046045.png)]
5.3 哪些变量存在线程安全问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IgDYyYkZ-1602401030302)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928140250045.png)]
5.4 扩大synchronized范围
ThreadAccount 修改
package com.codesing.ThreadSafe2;
public class ThreadAccount extends Thread{
//共享同一个账户
private Account account;
//通过构造方法传递过来账户对象
public ThreadAccount(Account account){
this.account = account;
}
@Override
public void run() {
//执行取款操作,假设取5000
int money = 5000;
//synchronized (this)这里this就不行了,因为this---->ThreadAccount.而我们是两线程共享一账户,
// 这个就相当于各自创建各自线程,两个栈,不存在排队。
synchronized (account) {//扩大范围,但效率更低了
account.withDraw(money);
}
System.out.println(Thread.currentThread().getName()+"对"+account.getActno()+"取款后,剩余:"+account.getBalance());
}
}
局部变量和常量不存在线程安全问题。成员变量可能有线程安全问题。
5.5 方法上加synchronized
//取款方法:
//synchronized出现在实例方法上一定锁的是this.
//缺点:不灵活。导致整个方法都需要同步,扩大同步范围,降低效率。
//优点: 代码节俭。如果共享的对象就是this,且整个方法都需要同步,选这种
public synchronized void withDraw(int money){
int before = this.balance;
int after = before - money;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setBalance(after);
// }
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6anuBppQ-1602401030303)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928150523450.png)]
5.6 synchronized三种写法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8dBsqAOO-1602401030304)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928150904948.png)]
6.面试题
(一个厕所门,两个隔间,一个隔间上锁,另一个隔间没锁)
package com.codesing.examHR;
/*面试题:
* doOther方法执行需要等待doSome方法的结束吗?
* 不需要。doOther没有synchronized,不需要排队
* */
public class Exam01 {
public static void main(String[] args) {
myClass mc = new myClass();
Thread t1 = new myThread(mc);
Thread t2 = new myThread(mc);
t1.setName("t1");
t2.setName("t2");
t1.start();
try {
Thread.sleep(1000);//保证t1执行完
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
}
}
class myThread extends Thread{
private myClass mc;
public myThread(myClass mc){
this.mc = mc;
}
public void run(){
if (Thread.currentThread().getName() == "t1"){
mc.doSome();
}
if (Thread.currentThread().getName() == "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 void doOther(){
System.out.println("doOther------>begin");
System.out.println("doOther----->over");
}
}
(一个厕所门,两个隔间,门上上锁)
package com.codesing.examHR;
/*面试题:
* doOther方法执行需要等待doSome方法的结束吗?
* 需要。doOther有synchronized
* */
public class Exam01 {
public static void main(String[] args) {
myClass mc = new myClass();
Thread t1 = new myThread(mc);
Thread t2 = new myThread(mc);
t1.setName("t1");
t2.setName("t2");
t1.start();
try {
Thread.sleep(1000);//保证t1执行完
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
}
}
class myThread extends Thread{
private myClass mc;
public myThread(myClass mc){
this.mc = mc;
}
public void run(){
if (Thread.currentThread().getName() == "t1"){
mc.doSome();
}
if (Thread.currentThread().getName() == "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(){
System.out.println("doOther------>begin");
System.out.println("doOther----->over");
}
}
(两个厕所门,每个厕所两个隔间,两个门上上锁)
package com.codesing.examHR;
/*面试题:
* doOther方法执行需要等待doSome方法的结束吗?
* 不需要。两个myClass对象,两把锁,不存在排队
* */
public class Exam01 {
public static void main(String[] args) {
myClass mc1 = new myClass();
myClass mc2 = new myClass();
Thread t1 = new myThread(mc1);
Thread t2 = new myThread(mc2);
t1.setName("t1");
t2.setName("t2");
t1.start();
try {
Thread.sleep(1000);//保证t1执行完
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
}
}
class myThread extends Thread{
private myClass mc;
public myThread(myClass mc){
this.mc = mc;
}
public void run(){
if (Thread.currentThread().getName() == "t1"){
mc.doSome();
}
if (Thread.currentThread().getName() == "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(){
System.out.println("doOther------>begin");
System.out.println("doOther----->over");
}
}
(一个厕所门,两个隔间,门上上锁,天下人只有一个厕所)
package com.codesing.examHR;
/*面试题:
* doOther方法执行需要等待doSome方法的结束吗?
* 需要,静态方法是类锁,不管创建了几个对象,类锁只有一把。
* */
public class Exam01 {
public static void main(String[] args) {
myClass mc1 = new myClass();
myClass mc2 = new myClass();
Thread t1 = new myThread(mc1);
Thread t2 = new myThread(mc2);
t1.setName("t1");
t2.setName("t2");
t1.start();
try {
Thread.sleep(1000);//保证t1执行完
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
}
}
class myThread extends Thread{
private myClass mc;
public myThread(myClass mc){
this.mc = mc;
}
public void run(){
if (Thread.currentThread().getName() == "t1"){
mc.doSome();
}
if (Thread.currentThread().getName() == "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 static void doOther(){
System.out.println("doOther------>begin");
System.out.println("doOther----->over");
}
}
7.搞清楚
- 排它锁(synchronized属于这个),互斥锁
- 对象锁(一个对象一个锁,100个对象100个锁),类锁(天下对象一个锁)
8.死锁
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XlcsZtuC-1602401030306)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928154941548.png)]
t1自上往下锁,t2自下往上锁。每个只锁住一个,另一个僵持住了。
package com.codesing.deadlock;
/*
* 死锁很难调试,会一直处于运行状态,会写。
* */
public class DeadLock {
public static void main(String[] args) {
Object o1 = new Object();
Object o2 = new Object();
//t1,t2线程共享o1,o2
myThread1 t1 = new myThread1(o1,o2);
myThread2 t2 = new myThread2(o1,o2);
t1.setName("t1");
t2.setName("t2");
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(){//先锁o1,再锁o2
synchronized (o1){
try {
Thread.sleep(1000);//保证锁住o1
} 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(){//先锁o2,再锁o1
synchronized (o2){
try {
Thread.sleep(1000);//保证锁住o2
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1){
}
}
}
}
9.聊聊
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JhYup34E-1602401030307)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928161708466.png)]
10.守护线程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JPRtkD3S-1602401030308)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928184358745.png)]
package com.codesing;
//守护线程
public class ThreadGuard {
public static void main(String[] args) {
myThread t = new myThread();
t.setName("守护线程");
//启动线程前,设置为守护线程
t.setDaemon(true);//主线程结束,守护线程结束。
t.start();
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"---->"+i);
}
}
}
class myThread 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();
}
}
}
}
11.定时器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HFFDWVNl-1602401030309)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928194927504.png)]
package com.codesing;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
/**
* 制作一个定时器 new Timer
*
*/
public class TimerTest {
public static void main(String[] args) {
Timer timer = new Timer();
// new Timer(true);也可以设置为守护线程
//timer.schedule(定时任务,开始时间,间隔);
try {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date firstTime = simpleDateFormat.parse("2020-09-28 20:50:00");
timer.schedule(new myTask(),firstTime,1000*10);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
//定义定时任务
class myTask extends TimerTask{
@Override
public void run() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date time = new Date();
String now = sdf.format(time);
System.out.println(now+"数据备份了一次!!!");
}
}
/*
2020-09-28 20:50:00数据备份了一次!!!
2020-09-28 20:50:10数据备份了一次!!!
2020-09-28 20:50:20数据备份了一次!!!
2020-09-28 20:50:30数据备份了一次!!!
2020-09-28 20:50:40数据备份了一次!!!
*/
12.实现线程的第三种方法
package com.codesing;
/*
* 实现线程第三种方法:
* 实现Callable接口
* 优点:能获取到线程返回结果
* 缺点:执行效率低,在获取t线程执行结果时,当前线程阻塞。
* */
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;//JUC包下的,属于java并发包,老JDK没有这个包,8新特性
public class ThreadCallable {
public static void main(String[] args) {
//创建一个”未来类“对象
//参数实现Callable接口
FutureTask task = new FutureTask(new Callable() {
@Override
public Object call() throws Exception {//类似于run(),但有返回值。
System.out.println("call methond begin!");
Thread.sleep(1000*10);
System.out.println("call methond over!");
int a = 100;
int b = 200;
return a+b;
}
});
Thread t = new Thread(task);
t.start();
try {
//获取返回结果
Object o = task.get();//这里主线程main会受阻塞。因为要等待执行完get()方法,而get方法需要
// 等待t线程结束获取结果,之后main线程才继续往下执行
System.out.println(o);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("hello world");
}
}
/*
call methond begin!
call methond over!
300
hello world
*/
13.关于Object类中的wait和notify方法(生产者和消费者模式)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hG4rhBHT-1602401030310)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928211916809.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FbZPQyp3-1602401030312)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928212052271.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rl2tC0lU-1602401030313)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928212523425.png)]
package com.codesing.Product_Consumme;
/*
* 使用wait方法和notify方法实现生产者模式-消费者模式
* 1.生产线程负责生产,消费线程负责消费,生产线程和消费线程达到均衡。
* 2.wait和notify方法每个java对象都有,不是线程对象的方法
* 3.wait和notify方法建立在线程同步的基础上。多线程共享一个仓库,存在线程安全问题。
* 4.wait方法作用:o.wait()让正在o对象上运行的t线程进入等待状态,并且释放t线程之前所占有的o对象的锁。
* 5.notify方法作用:o.notify()让正在o对象上等待的线程唤醒,只是通知,不会释放o对象上之前占有的锁。
* 模拟:
* list集合代表仓库,1个就表示满了,0个表示为空
*一个消费者线程,一个生产者线程
* */
import com.sun.javafx.collections.ListListenerHelper;
import java.util.ArrayList;
import java.util.List;
public class Demo {
public static void main(String[] args) {
List list = new ArrayList();
Thread product = new Thread(new Product(list));
Thread consummer = new Thread(new Consummer(list));
product.setName("生产者模式");
consummer.setName("消费者模式");
product.start();
consummer.start();
}
}
//生产者
class Product implements Runnable{
private List list;
public Product(List list) {
this.list = list;
}
@Override
public void run() {
//一直生产
while (true){
//给仓库对象list加锁
synchronized (list){
if (list.size() > 0){//仓库满了
try {
list.wait();//当前线程进入等待状态,释放list集合的锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//执行到这里,消费完了,仓库为空,开始生产
Object o = new Object();
list.add(o);
System.out.println(Thread.currentThread().getName()+"--生产--->"+o);
//生产完成,唤醒消费者消费
list.notify();
}
}
}
}
//消费者
class Consummer implements Runnable{
private List list;
public Consummer(List list) {
this.list = list;
}
@Override
public void run() {
//一直消费
while (true){
synchronized (list){
if (list.size() == 0){//仓库空了
try {
list.wait();//当前线程进入等待状态,释放list集合锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//执行到这里,仓库满了,开始消费
Object remove = list.remove(0);
System.out.println(Thread.currentThread().getName()+"--消费--->"+remove);
//消费完了,唤醒生产者生产
list.notify();
}
}
}
}