线程基础03

6.用户线程和守护线程

  1. 用户线程:也叫工作线程,当线程的任务执行完或者通知方法结束。平时用到的普通线程均是用户线程,当在Java程序中创建一个线程,它就被称为用户线程

  2. 守护线程(Daemon):一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束

  3. 常见的守护线程:垃圾回收机制

例子1:如何将一个线程设置成守护线程

package li.thread.method;

public class ThreadMethodExercise {
public static void main(String[] args) throws InterruptedException { MyDaemonThread myDaemonThread = new MyDaemonThread(); //如果我们希望当主线程结束后,子线程自动结束,只需要将子线程设置为守护线程
myDaemonThread.setDaemon(true); myDaemonThread.start(); for (int i = 1; i <= 10; i++) {//main线程
System.out.println("悟空在前方打妖精...");
Thread.sleep(1000);
}
}
} class MyDaemonThread extends Thread {
@Override
public void run() {
for (; ; ) {//无限循环
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("八戒收拾东西回高老庄...");
}
}
}

7.线程的生命周期

  • JDK中用Thread.State枚举表示了线程的几种状态:

例子

package li.thread.state;

public class ThreadState_ {
public static void main(String[] args) throws InterruptedException {
T t = new T();
System.out.println(t.getName() + "状态 " + t.getState());
t.start();
while (t.getState() != Thread.State.TERMINATED) {
System.out.println(t.getName() + "状态 " + t.getState());
Thread.sleep(1000);
} System.out.println(t.getName() + "状态 " + t.getState()); }
} class T extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("hi" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

8.线程同步机制

  • 线程同步机制
  1. 在多线程编程中,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整性。
  2. 也可以理解为:线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作。
  • 同步具体方法--Synchronized
  1. 同步代码块

    synchronized(对象){//得到对象的锁,才能操作同步代码
    //需要被同步的代码
    }
  2. synchronized还可以放在方法声明中,表示整个方法为同步方法

    public synchronized void m(String name){
    //需要被同步的代码
    }

    就好像某个小伙伴上厕所之前先把门关上(上锁),完事之后再出来(解锁),那么其他小伙伴就可以再使用厕所了

例子:使用synchronized解决3.1售票问题

package li.thread.syn;

//使用多线程,模拟三个窗口同时售票共100张
public class SynSellTicket {
public static void main(String[] args) { SellTicket03 sellTicket03 = new SellTicket03();
new Thread(sellTicket03).start();//第1个线程-窗口
new Thread(sellTicket03).start();//第2个线程-窗口
new Thread(sellTicket03).start();//第3个线程-窗口
}
} //实现接口方式,使用synchronized实现线程同步
class SellTicket03 implements Runnable { private int ticketNum = 100;
private boolean loop = true;//控制run方法变量 public synchronized void sell() {//同步方法,在在同一时刻,只能有一个线程来执行run方法
if (ticketNum <= 0) {
System.out.println("售票结束...");
loop = false;
return;
}
//休眠50毫秒,模拟
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口:" + Thread.currentThread().getName() + "售出一张票 "
+ "剩余票数:" + (--ticketNum));
} @Override
public void run() {
while (loop) {
sell();//sell方法是一个同步方法
}
}
}

8.1互斥锁

  • 基本介绍
  1. Java语言中,引入了对象互斥锁的额概念,来保证共享数据操作的完整性
  2. 每一个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象
  3. 关键字synchronized来与对象的互斥锁联系。当某个对象用synchronized修饰时,表明该对象在任一时刻只能有一个线程访问
  4. 同步的局限性:导致程序的执行效率降低
  5. 非静态的同步方法,锁可以是this(当前对象),也可以是其他对象(要求锁的是同一个对象)
  6. 同步方法(静态的)的锁为当前类本身(类.class)

synchronized实现同步的基础:Java中的每一个对象都可以作为锁。

具体表现为以下3种形式。

对于普通同步方法,锁是当前实例对象。

对于静态同步方法,锁是当前类的Class对象。

对于同步方法块,锁是Synchonized括号里配置的对象。

  • 注意事项和细节:

    • 同步方法如果没有使用static修饰:默认锁对象为this
    • 如果方法使用static修饰,默认锁对象:当前类.class
    • 实现的落地步骤:
      • 需要先分析上锁的代码
      • 选择同步代码块或者同步方法
      • 要求多个线程的锁对象为同一个
package li.thread.syn;

//使用多线程,模拟三个窗口同时售票共100张
public class SynSellTicket {
public static void main(String[] args) { SellTicket03 sellTicket03 = new SellTicket03();
new Thread(sellTicket03).start();//第1个线程-窗口
new Thread(sellTicket03).start();//第2个线程-窗口
new Thread(sellTicket03).start();//第3个线程-窗口
}
} //实现接口方式,使用synchronized实现线程同步
class SellTicket03 implements Runnable { private int ticketNum = 100;
private boolean loop = true;//控制run方法变量
Object object = new Object(); //1.public synchronized static void m1(){}的锁加在SellTicket03.class
public synchronized static void m1(){}
//2.如果在静态方法中,要实现一个同步代码块则应该这样写:(原因是静态方法适合类一起加载的,静态方法不能使用this)
public static void m2(){
synchronized (SellTicket03.class){
System.out.println("m2");
}
} // public synchronized void sell() {}就是一个同步方法。这时,锁在this对象
//也可以在代码块上写synchronized,同步代码块,互斥锁还是在this对象
public /*synchronized*/void sell() {//同步方法
synchronized (/*this*/object) {//如果是new Object就不是同一个对象
if (ticketNum <= 0) {
System.out.println("售票结束...");
loop = false;
return;
}
//休眠50毫秒,模拟
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口:" + Thread.currentThread().getName() + "售出一张票 "
+ "剩余票数:" + (--ticketNum));
}
} @Override
public void run() {
while (loop) {
sell();//sell方法是一个同步方法
}
}
}

8.2线程的死锁

  • 基本介绍:

多个线程都占用了对方的锁资源,但不肯相让,导致了死锁。

在编程中一定要避免死锁的发生。

例子:

package li.thread.syn;

//模拟线程死锁
public class DeadLock_ {
public static void main(String[] args) {
//模拟死锁现象
DeadLockDemo A = new DeadLockDemo(true);
DeadLockDemo B = new DeadLockDemo(false);
A.setName("A线程");
B.setName("B线程");
A.start();
B.start();
}
} class DeadLockDemo extends Thread {
static Object o1 = new Object();//保证多线程,共享一个对象,这里使用static
static Object o2 = new Object();
boolean flag; public DeadLockDemo(boolean flag) {//构造器
this.flag = flag;
} @Override
public void run() {
//下面业务逻辑的分析
//1.如果flag为true,线程就会先得到/持有 o1对象锁,然后尝试去获取o2对象锁
//2.如果线程A得不到o2对象锁,就会Blocked
//3.如果flag为false,线程B就会先得到/持有 o2对象锁,然后尝试去获取o1对象锁
//4.如果线程B得不到o1对象锁,就会Blocked
if (flag) {
synchronized (o1) {//对象互斥锁,下面就是同步代码
System.out.println(Thread.currentThread().getName() + "进入1");
synchronized (o2) {//这里获得li对象的监视权
System.out.println(Thread.currentThread().getName() + "进入2");
}
}
} else {
synchronized (o2) {
System.out.println(Thread.currentThread().getName() + "进入3");
synchronized (o1) {//这里获得li对象的监视权
System.out.println(Thread.currentThread().getName() + "进入4");
}
}
}
}
}

如下图:两个线程卡住了

8.3释放锁

下面操作会释放锁:

  1. 当前线程的同步方法、同步代码块执行结束
  2. 当前线程在同步代码块、同步方法中遇到break、return
  3. 当前线程在同步代码块、同步方法中出现了未处理的Error或者Exception,导致异常结束
  4. 当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁

下面的操作不会释放锁:

  1. 线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行,不会释放锁

  2. 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁。

​ 提示:应尽量避免使用suspend()和resume()来控制线程,这两个方法不再推荐使用

9.本章作业

9.1线程HomeWork01

(1)在main方法中启动两个线程

(2)第一个线程循环随机打印100以内的整数

(3)直到第二个线程从键盘读取了“Q”命令

练习:

package li.thread.syn.homework;

import java.util.Scanner;

//(1)在main方法中启动两个线程
public class ThreadHomeWork01 { public static void main(String[] args) { A a = new A();
B b = new B(a);//注意把a对象传入b构造器中 a.start();
b.start();
}
} //创建A线程类
class A extends Thread {
private boolean loop = true; @Override
public void run() {
while (loop) {
System.out.println((int) (Math.random() * 100 + 1));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("a线程退出...");
} public void setLoop(boolean loop) {
this.loop = loop;
}
} //创建B线程类
class B extends Thread {
private A a;
Scanner scanner = new Scanner(System.in); public B(A a) {
this.a = a;
} @Override
public void run() {
while (true) {
//接到用户输入
System.out.println("请输入你的指令(Q)表示退出");
char key = scanner.next().toUpperCase().charAt(0);
if (key == 'Q') {
//以通知的方式结束a线程
a.setLoop(false);
System.out.println("b线程退出...");
break;
}
}
}
}

9.2线程线程HomeWork02

(1)有两个用户分别从同一张卡上取钱(总额10000)

(2)每次都取1000,当余额不足时,就不能取款了

(3)不能出现超取现象==>线程同步问题

易错点:关于互斥锁的理解

对于普通同步方法,锁是当前实例对象

对于静态同步方法,锁是当前类的Class对象

对于同步方法块,锁是Synchonized括号里配置的对象

package li.thread.syn.homework;

public class ThreadHomeWork02 {
public static void main(String[] args) {
T t = new T();
Thread thread1 = new Thread(t);
Thread thread2 = new Thread(t);
thread1.setName("t1");
thread2.setName("t2");
thread1.start();
thread2.start();
}
} class T implements Runnable {
private int money = 10000; @Override
public void run() {
while (true) {//while不要放到同步代码块里面
//1.使用了synchronized实现线程同步
//2.当多个线程执行到这里的时候就会去争夺 this对象锁
//3.哪个线程争夺到(获取)this对象锁,就执行synchronized代码块
//4.争夺不到this对象锁,就Blocked,准备继续争夺
//5.this对象锁是非公平锁
synchronized (this) {
if (money <= 0) {
System.out.println("余额不足...");
break;
}
money -= 1000;
System.out.println(Thread.currentThread().getName() + "取出了1000元" + " 当前余额为:" + money);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

day33-线程基础03的更多相关文章

  1. javaSE基础03

    javaSE基础03 生活中常见的进制:十进制(0-9).星期(七进制(0-6)).时间(十二进制(0-11)).二十四进制(0-23) 进制之间的转换: 十进制转为二进制: 将十进制除以2,直到商为 ...

  2. javascript基础03

    javascript基础03 1. 算术运算符 后增量/后减量运算符 ++ ,-- 比较运算符 ( >, <, >=, <=, ==, !=,===,!== ) 逻辑运算符( ...

  3. Qt之线程基础

    何为线程 线程与并行处理任务息息相关,就像进程一样.那么,线程与进程有什么区别呢?当你在电子表格上进行数据计算的时候,在相同的桌面上可能有一个播放器正在播放你最喜欢的歌曲.这是一个两个进程并行工作的例 ...

  4. Android多线程研究(1)——线程基础及源代码剖析

    从今天起我们来看一下Android中的多线程的知识,Android入门easy,可是要完毕一个完好的产品却不easy,让我们从线程開始一步步深入Android内部. 一.线程基础回想 package ...

  5. JAVA与多线程开发(线程基础、继承Thread类来定义自己的线程、实现Runnable接口来解决单继承局限性、控制多线程程并发)

    实现线程并发有两种方式:1)继承Thread类:2)实现Runnable接口. 线程基础 1)程序.进程.线程:并行.并发. 2)线程生命周期:创建状态(new一个线程对象).就绪状态(调用该对象的s ...

  6. 【windows核心编程】 第六章 线程基础

    Windows核心编程 第六章 线程基础 欢迎转载 转载请注明出处:http://www.cnblogs.com/cuish/p/3145214.html 1. 线程的组成 ①    一个是线程的内核 ...

  7. C#当中的多线程_线程基础

    前言 最近工作不是很忙,想把买了很久了的<C#多线程编程实战>看完,所以索性把每一章的重点记录一下,方便以后回忆. 第1章 线程基础 1.创建一个线程 using System; usin ...

  8. Qt 线程基础(Thread Basics的翻译,线程的五种使用情况)

    Qt 线程基础(QThread.QtConcurrent等) 转载自:http://blog.csdn.net/dbzhang800/article/details/6554104 昨晚看Qt的Man ...

  9. 线程基础(CLR via C#)

    1.线程基础  1.1.线程职责  线程的职责是对CPU进行虚拟化.Windows 为每个进程豆提供了该进程专用的线程(功能相当于一个CPU).应用程序的代码进入死循环,于那个代码关联的进程会&quo ...

随机推荐

  1. 手把手教学~基于element封装tree树状下拉框

    在日常项目开发中,树状下拉框的需求还是比较常见的,但是element并没有这种组件以供使用.在这里,小编就基于element如何封装一个树状下拉框做个详细的介绍. 通过这篇文章,你可以了解学习到一个树 ...

  2. ms12-020漏洞

    一.环境说明 kali linux windows 7 sp1 二.ms12-020漏洞利用 msf5 exploit(windows/browser/ms10_002_aurora) > se ...

  3. 学习C4C的视频分享

  4. 详解SQL中Groupings Sets 语句的功能和底层实现逻辑

    摘要:本文首先简单介绍 Grouping Sets 的用法,然后以 Spark SQL 作为切入点,深入解析 Grouping Sets 的实现机制. 本文分享自华为云社区<深入理解 SQL 中 ...

  5. Tapdata 在线研讨会:DaaS vs 大数据平台,是竞争还是共处?

    从20年前的传统数仓,到10年前大数据平台,5年前开始火热的数据中台以及最近出现的湖仓一体新数据平台,今天被数据孤岛困扰的企业,面临着太多的选择.这些数据产品及架构有一个共性:他们本质上解决的大部分都 ...

  6. 当mysql表从压缩表变成普通表会发生什么

    前言 本文章做了把mysql表从压缩表过渡到普通表的实验过程,看看压缩表变成普通表会发生什么?本文针对mysql5.7和mysql8分别进行了实验. 1.什么是表压缩 在介绍压缩表变成普通表前,首先给 ...

  7. 【.NET基础】Linq常用语法代码演示

    前言:前言不重要,linq入门常用的语法,linq语法可以用来写操作集合.数据库表集合等等几乎所有集合类型的操作.下面就写几个案例(以List集合来做的),看代码和运行结果即可. 本文演示环境:VS2 ...

  8. C++对象的应用

    本篇文章将介绍对象数组,对象的动态分配以及对象在函数中的应用. 一.对象数组 1.对象数组的定义和初始化 定义对象数组与定义普通数组的语法形式基本相同.如定义一个Square obj[3]:表示一个正 ...

  9. 无需CORS,用nginx解决跨域问题,轻松实现低代码开发的前后端分离

    近年来,前后端分离已经成为中大型软件项目开发的最佳实践. 在技术层面,前后端分离指在同一个Web系统中,前端服务器和后端服务器采用不同的技术栈,利用标准的WebAPI完成协同工作.这种前后端分离的&q ...

  10. Redis配置与优化

    一.缓存概念 缓存是为了调节速度不一致的两个或多个不同的物质的速度,在中间对速度较慢的一方起到加速作用,比如CPU的一级.二级缓存是保存了CPU最近经常访问的数据,内存是保存CPU经常访问硬盘的数据, ...