package thread;
// 除了守护线程都是前台线程
/**
* 守护线程
* 守护线程也称后台线程
* 守护线程是通过普通线程调用setDaemon(boolean on)方法设置而来的
* 因此创建上与普通线程没有区别。
* 守护线程的结束时机上有一点与普通线程不同,即:进程的结束。
* 进程结束:当一个进程中的所有普通线程都结束时,进程就会结束,此时会杀掉所有正在运行的守护线程
*
* 通常当我们不关心某个线程的任务什么时候停下来,它可以一直运行,
* 但是程序主要的工作都结束时它应当跟着结束,这样的任务就适合放在守护线程上执行,
* 比如GC就是在守护线程上运行的。
*
* @author YanLy
* @date 2021/6/2 9:33
*/
public class DaemonThreadDemo {
public static void main(String[] args) {
Thread rose = new Thread(){
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("rose:let me go!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("rose: 啊啊啊啊啊啊啊啊啊啊啊啊啊啊~");
System.out.println("噗通!");
}
};
Thread jack = new Thread(){
@Override
public void run() {
while(true){
System.out.println("jack:You jump,I jump!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
rose.start();
jack.setDaemon(true);// 将jack设置为守护线程
jack.start();
// 主线程在这里死循环,只要有普通线程活着,进程就不会结束,jack就不会被杀死。
// while(true);
}
}
package thread;
/**
* 线程提供了一组获取相关信息的方法
*
* @author YanLy
* @date 2021/6/2 10:08
*/
public class ThreadInfo {
public static void main(String[] args) {
// 1.获取运行当前代码的线程
Thread t = Thread.currentThread();
System.out.println("运行当前代码的线程:"+t); // Thread[main,5,main] 线程名,优先级,所属线程组
// 2.获取线程的名字
// t = new Thread();
String name = t.getName();
System.out.println("线程名字:"+name); // main
// 3.获取该线程的唯一标识
long id = t.getId();
System.out.println("唯一标识:"+id); // 1
// 4.获取该线程的优先级
int priority = t.getPriority();
System.out.println("该线程的优先级是:"+priority);
// 5.判断线程是否活着
boolean isAlive = t.isAlive();
System.out.println("该线程是否活着:"+isAlive);
// 6.判断线程是否为守护线程
boolean isDaemon = t.isDaemon();
System.out.println("该线程是否为守护线程:"+isDaemon);
// 7.判断线程是否被中断了
boolean isInterrupted = t.isInterrupted();
System.out.println("该线程是否被中断:"+isInterrupted);
}
}
package thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 线程池
* 线程池是一个管理线程的机制,主要解决两个问题:
* 1.控制线程的数量
* 2.重用线程
*
* @author YanLy
* @date 2021/6/2 10:20
*/
public class ThreadPoolDemo {
private static int num = 1;
public static void main(String[] args) {
// 创建固定大小的线程池(参数代表线程池的容量,这里是两条线程)
ExecutorService threadPool =
Executors.newFixedThreadPool(2); // 几条线程
for (int i = 0; i < 5; i++) {
Runnable r = new Runnable() {
@Override
public void run() {
Thread t = Thread.currentThread();
System.out.println(t.getName()+"正在执行任务..." + num++);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(t.getName()+"执行任务完毕!");
}
};
threadPool.execute(r);
System.out.println("交给线程池一个任务...");
}
/*
面试题:线程池的关闭有几种方法?它们之间有什么区别?
线程池的关闭 ThreadPoolExecutor 提供了两个方法用于线程池的关闭,分别是:
shutdown()和 shutdownNow()
shutdown() : 不会立即终止线程池,而是要等所有任务缓冲队列中的任务都执行完后才终止,
但再也不会接收新的任务。
shutdownNow() :会立即终止线程池,并尝试打断正在执行的任务,
并且清空任务缓冲队列,返回尚未执行的任务。
*/
threadPool.shutdown();
// threadPool.shutdownNow();
// 返回值是一个集合 所有被打断的任务中没有执行的任务都会成为一个返回值返回给集合
System.out.println("线程池关闭了!");
}
}
package thread;
/**
* 多线程并发安全问题:
* 当多个线程并发操作同一个临界资源,由于线程切换的时机不确定,
* 导致操作临界资源的顺序出现混乱,严重时可能导致系统瘫痪。
*
* 临界资源:操作该资源的全过程同时只能被单个线程完成。
*
* 相当于现实生活中多个人抢同一个东西导致的混乱。
*
* @author YanLy
* @date 2021/6/2 10:52
*/
public class SyncDemo1 {
public static void main(String[] args) {
Ticket ticket = new Ticket();
Thread t1 = new Thread(){
@Override
public void run() {
while(true){
int t = ticket.getTickets();
Thread.yield(); // 让出时间片
System.out.println("还剩几张票:"+getName()+":"+t);
}
}
};
Thread t2 = new Thread(){
@Override
public void run() {
while(true){
int t = ticket.getTickets();
Thread.yield(); // 让出时间片
System.out.println("还剩几张票:"+getName()+":"+t);
}
}
};
Thread t3 = new Thread(){
@Override
public void run() {
while(true){
int t = ticket.getTickets();
Thread.yield(); // 让出时间片
System.out.println("还剩几张票:"+getName()+":"+t);
}
}
};
Thread t4 = new Thread(){
@Override
public void run() {
while(true){
int t = ticket.getTickets();
Thread.yield(); // 让出时间片
System.out.println("还剩几张票:"+getName()+":"+t);
}
}
};
Thread t5 = new Thread(){
@Override
public void run() {
while(true){
int t = ticket.getTickets();
Thread.yield(); // 让出时间片
System.out.println("还剩几张票:"+getName()+":"+t);
}
}
};
Thread t6 = new Thread(){
@Override
public void run() {
while(true){
int t = ticket.getTickets();
Thread.yield(); // 让出时间片
System.out.println("还剩几张票:"+getName()+":"+t);
}
}
};
Thread t7 = new Thread(){
@Override
public void run() {
while(true){
int t = ticket.getTickets();
Thread.yield(); // 让出时间片
System.out.println("还剩几张票:"+getName()+":"+t);
}
}
};
Thread t8 = new Thread(){
@Override
public void run() {
while(true){
int t = ticket.getTickets();
Thread.yield(); // 让出时间片
System.out.println("还剩几张票:"+getName()+":"+t);
}
}
};Thread t9 = new Thread(){
@Override
public void run() {
while(true){
int t = ticket.getTickets();
Thread.yield(); // 让出时间片
System.out.println("还剩几张票:"+getName()+":"+t);
}
}
};Thread t10 = new Thread(){
@Override
public void run() {
while(true){
int t = ticket.getTickets();
Thread.yield(); // 让出时间片
System.out.println("还剩几张票:"+getName()+":"+t);
}
}
};
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
t7.start();
t8.start();
t9.start();
t10.start();
}
}
class Ticket{
private int tickets = 20; // 有20张票
/*
synchronized 线程安全锁 ”兴可柔奈子的“
不能修饰属性 只能修饰方法
当一个方法使用synchronized 修饰后,这个方法称为"同步方法",即:
多个线程不能同时在方法内部执行,只能有先后顺序的一个一个进行。
将并发操作同一临界资源的过程改为同步执行就可以有效的解决并发安全问题。
*/
public synchronized int getTickets(){
if (tickets==0){
throw new RuntimeException("没有票了");
}
return tickets--;
}
}
package thread;
/**
* 同步块
* 有效的缩小同步范围可以在保证并发安全的前提下尽可能的提高并发效率
* 同步块可以更准确的控制需要排队执行的代码片段。
* 语法:
* synchronized(同步监视器对象){
* 需要多线程同时执行的代码片段
* }
*
* @author YanLy
* @date 2021/6/2 11:44
*/
public class SyncDemo2 {
public static void main(String[] args) {
Shop shop = new Shop();
Thread t1 = new Thread(){
@Override
public void run() {
shop.buy();
}
};
Thread t2 = new Thread(){
@Override
public void run() {
shop.buy();
}
};
t1.start();
t2.start();
}
}
class Shop{
Object o = new Object();
// 在方法上使用synchronized时,锁对象就是当前方法所属对象this
// 一个人挑衣服,试衣服,结账 之后才能另一个人来试衣服、挑衣服、结账
// 调用方法者 都被线程锁住了
// public synchronized void buy(){
public void buy(){
try {
Thread t = Thread.currentThread();// 获取运行buy方法的线程
System.out.println(t.getName()+":正在挑衣服...");
Thread.sleep(5000);
/*
使用同步块时,要指定同步监视器对象,即:上锁的对象,这个对象可以是java中任何引用类型的实例,
但是需要注意:多个需要排队执行的线程看到的该对象必须是同一个对象,否则没有效果!
*/
synchronized (this){
// synchronized (o){ 提前创建好的不变的对象,可以实现效果。
// this 保证锁的是同一个对象
// synchronized (new Object()){无效!多个线程看到的不是同一个锁
System.out.println(t.getName()+":正在试衣服...");
Thread.sleep(5000);
}
System.out.println(t.getName()+":结账离开!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package thread;
/**
* 当在静态方法上使用synchronized关键字之后,该方法是一个同步方法。
* 由于静态方法属于类,所以一定具有同步效果。
*
* @author YanLy
* @date 2021/6/2 14:03
*/
public class SyncDemo3 {
public static void main(String[] args) {
// 静态方法与对象无关
Thread t1 = new Thread(){
@Override
public void run() {
A.doSome();
}
};
Thread t2 = new Thread(){
@Override
public void run() {
A.doSome();
}
};
t1.start();
t2.start();
}
}
class A{
/*
静态方法上使用synchronized关键字,同步监视器对象为当前类的类对象(Class的实例)
JVM中每个被加载的类都有且只有一个Class实例与之对应,每个Class的实例都可以描述其标识的类的信息。
(后期反射知识点中会介绍Class)
下面doSome方法的同步监视器对象就是A类的类对象。
*/
// public synchronized static void doSome(){
public static void doSome(){
// 使用同步块时,通常指定的也是当前类的类对象,获取方式:类名.class
synchronized(A.class){
Thread t = Thread.currentThread();
System.out.println(t.getName()+": 正在执行doSome方法...");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(t.getName()+": 执行doSome方法完毕!");
}
}
}
package thread;
/**
* 互斥锁
*
* 互斥:当多个线程执行不同的代码片段,但是这些代码片段之间不能同时运行时就要设置为互斥的。
*
* 当使用synchronized锁定多个代码片段,并且这些同步块指定的同步监视器对象是同一个时,
* 这些代码片段之间就是互斥的,多个线程不能同时执行它们。
*
* 还有乐观锁、悲观锁、可重复锁、不可重复锁等等
* 抽象方法不能锁
*
* @author YanLy
* @date 2021/6/2 14:23
*/
public class SyncDemo4 {
public static void main(String[] args) {
// 同一个对象里的方法上锁,是互斥,this 指向当前类的对象
// 两个对象就不是指向的同一个对象了,就不会产生互斥现象
B b = new B();
// B b2 = new B();
Thread t1 = new Thread(){
@Override
public void run() {
b.m1();
}
};
Thread t2 = new Thread(){
@Override
public void run() {
b.m2();
// b2.m2();
}
};
t1.start();
t2.start();
}
}
class B{
public synchronized void m1(){
try {
Thread t = Thread.currentThread();
System.out.println(t.getName()+": 正在执行m1方法...");
Thread.sleep(5000);
System.out.println(t.getName()+": 执行m1方法完毕!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// public synchronized void m2(){
public void m2(){
synchronized(this){
try {
Thread t = Thread.currentThread();
System.out.println(t.getName()+": 正在执行m2方法...");
Thread.sleep(5000);
System.out.println(t.getName()+": 执行m2方法完毕!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package thread;
/**
* 死锁
* 线程持有一个锁并要求等待另一个线程释放它的锁,而如果两个线程同时等待对方释放锁时就形成了死锁。
*
* @author YanLy
* @date 2021/6/2 15:07
*/
public class SyncDemo5 {
public static Object spoon = new Object(); // 勺子
public static Object chopsticks = new Object();// 筷子
// "南方人喝完了汤去拿筷子" 和 "北方人吃完了饭去拿勺" 死锁了
// 备注为死锁
public static void main(String[] args) {
// 北方人
Thread t1 = new Thread() {
@Override
public void run() {
try {
// 北方人吃饭习惯:先吃饭后喝汤。
System.out.println("北方人去拿筷子");
synchronized (chopsticks) {//指定锁对象为"筷子"
System.out.println("北方人拿起了筷子开始吃饭...");
Thread.sleep(5000); // 吃饭过程
System.out.println("北方人吃完了饭将筷子放回去去拿勺");
}
synchronized (spoon){ // 指定锁对象为"勺"
System.out.println("北方人拿起了勺开始喝汤");
Thread.sleep(5000); // 喝汤过程
System.out.println("北方人将勺放回去");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
// Thread t1 = new Thread() {
// @Override
// public void run() {
// try {
// // 北方人吃饭习惯:先吃饭后喝汤。
// System.out.println("北方人去拿筷子");
// synchronized (chopsticks) {//指定锁对象为"筷子"
// System.out.println("北方人拿起了筷子开始吃饭...");
// Thread.sleep(5000); // 吃饭过程
// System.out.println("北方人吃完了饭去拿勺");
// synchronized (spoon){ // 指定锁对象为"勺"
// System.out.println("北方人拿起了勺开始喝汤");
// Thread.sleep(5000); // 喝汤过程
// }
// System.out.println("北方人将勺放回去");
// }
// System.out.println("北方人将筷子放回去");
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
// };
// 南方人
Thread t2 = new Thread() {
@Override
public void run() {
try {
// 南方人吃饭习惯:先喝汤后吃饭。
System.out.println("南方人去拿勺");
synchronized (spoon) {//指定锁对象为"勺子"
System.out.println("南方人拿起了勺子开始喝汤...");
Thread.sleep(5000); // 喝汤过程
System.out.println("南方人喝完了汤将勺子放回去去拿筷子");
}
synchronized (chopsticks){ // 指定锁对象为"筷子"
System.out.println("南方人拿起了筷子开始吃饭");
Thread.sleep(5000); // 吃饭过程
System.out.println("南方人将筷子放回去");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
// Thread t2 = new Thread() {
// @Override
// public void run() {
// try {
// // 南方人吃饭习惯:先喝汤后吃饭。
// System.out.println("南方人去拿勺");
// synchronized (spoon) {//指定锁对象为"勺子"
// System.out.println("南方人拿起了勺子开始喝汤...");
// Thread.sleep(5000); // 喝汤过程
// System.out.println("南方人喝完了汤去拿筷子");
// synchronized (chopsticks){ // 指定锁对象为"筷子"
// System.out.println("南方人拿起了筷子开始吃饭");
// Thread.sleep(5000); // 吃饭过程
// }
// System.out.println("南方人将筷子放回去");
// }
// System.out.println("南方人将勺子放回去");
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
// };
t1.start();
t2.start();
}
}