public class Thread03 {
public static void main(String[] args) {
Thread t2 = new Thread(new T2());//创建子线程
//将当前进程变成后台进程,即守护线程
//当main线程结束后,子线程自动结束。
t2.setDaemon(true);
t2.start();
for (int i = 0; i < 10; i++) {//main线程
System.out.println("工作。。。");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class T2 implements Runnable{
@Override
public void run() {
for (;;) {//无限循环
System.out.println("聊天。。。");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
特点
应用场景
//多线程的实现
//需求:编写一个程序,创建两个线程,一个线程每隔1s输出:“hello,world”,输出10次,退出;一个现成每隔1s输出“hi”,输出5次退出。
public class Thread02 {
public static void main(String[] args) {
Thread t1 = new Thread(new T1());//创建线程1
Thread t2 = new Thread(new T2());//创建线程2
t1.start();//启动第 1 个线程
t2.start();//启动第 1 个线程
for (int i = 0; i < 10; i++) {//main线程
System.out.println("工作。。。");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class T1 implements Runnable{
int count;
@Override
public void run() {
while (true) {
//每隔 1 秒输出 “hello,world” ,输出 10 次
System.out.println("hello,world " + (++count));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(count == 10) {
break;
}
}
}
}
class T2 implements Runnable{
@Override
public void run() {
//每隔 1 秒输出 “hi” ,输出 5 次
for (int i = 0;i<5;i++) {
System.out.println("hi");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
说明
构造器
实现步骤
// 需求:开启一个线程,该线程每隔1秒,在控制台输出“喵喵,我是小猫咪”;当输出80次,结束该线程
public class Thread01 {
public static void main(String[] args) throws InterruptedException {
// 创建Cat对象,可以当做线程使用
Cat cat = new Cat();
// 源码
/*
(1)public synchronized void start() {
start0();
}
(2) start0() 是本地方法,是JVM调用, 底层是c/c++实现;真正实现多线程的效果, 是start0(), 而不是 run
private native void start0();
*/
cat.start();// 启动线程-> 最终会执行cat的run方法
// cat.run();
// run方法就是一个普通的方法, 没有真正的启动一个线程,就会把run方法执行完毕,才向下执行;
// 相当于串行化执行;
// 说明: 当main线程启动一个子线程 Thread-0, 主线程不会阻塞, 会继续执行,这时 主线程和子线程是交替执行..
System.out.println("主线程继续执行" + Thread.currentThread().getName());// 线程名字:main
for (int i = 0; i < 60; i++) {
System.out.println("主线程 i=" + i);
// 让主线程休眠
Thread.sleep(1000);
}
}
}
// 说明
// 1. 当一个类继承了 Thread 类, 该类就可以当做线程使用
// 2. 我们会重写 run方法,写上自己的业务代码
// 3. run Thread 类 实现了 Runnable 接口的run方法
/*
@Override
public void run() {
if (target != null) {
target.run();
}
}
*/
class Cat extends Thread {
int times = 0;
@Override
public void run() {// 重写run方法,写上自己的业务逻辑
while (true) {
// 该线程每隔1秒。在控制台输出 “喵喵, 我是小猫咪”
System.out.println("喵喵, 我是小猫咪" + (++times) + " 线程名=" + Thread.currentThread().getName());
// 让该线程休眠1秒 ctrl+alt+t
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (times == 80) {
break;// 当times 到80, 退出while, 这时线程也就退出..
}
}
}
}
实现步骤
//通过实现接口Runnable 来开发线程
//需求:该程序可以每隔1秒在控制台输出“hi”,当输出10次后,自动退出
public class Thread02 {
public static void main(String[] args) {
Dog dog = new Dog();
//dog.start(); 这里不能调用start,Runnable接口没有start()方法;
//创建了Thread对象,把 dog对象(实现Runnable),放入Thread
Thread thread = new Thread(dog);
thread.start();
}
}
class Dog implements Runnable { //通过实现Runnable接口,开发线程
int count = 0;
@Override
public void run() { //普通方法
while (true) {
System.out.println("小狗汪汪叫..hi" + (++count) + Thread.currentThread().getName());
//休眠1秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count == 10) {
break;
}
}
}
}
继承Thread:线程代码存放Thread子类run方法中。
实现Runnable:线程代码存在接口的子类的run方法;
从java设计上,通过extends Thread或implements Runnable 来创建线程,本质上没有区别
//isInterrupted()方法
public class Thread04 {
public static void main(String[] args) {
Thread t3 = new T3();
t3.interrupt();
}
}
public class T3 extends Thread{
@Override
public void run() {
if (!Thread.currentThread().isInterrupted()){
try {
//1.这里处理正常的线程业务逻辑
System.out.println("hello");
sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();//重新设置中断标识
}
}
if (Thread.currentThread().isInterrupted()){
//2.处理线程结束前必要的一些资源释放和清理工作;
//比如释放锁、存储数据到持久化层、发出异常通知等;用于线程的安全退出
System.out.println("保存数据");
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
注意:
sleep在抛出InterruptedException异常前会清楚终端标志位,在抛出异常后调用isInterrupted方法会返回false;
//join方法
低优先级的线程也可以获得执行;
应用案例:
1、主线程每隔1s,输出hi,一共10次;
2、当输出到hi 5时,启动一个子线程(要求实现Runnable),每隔1s输出hello,等该线程输出10次hello后,退出
3、主线程继续输出hi,直到主线程退出
代码实现:
Thread子类;
public class T1 implements Runnable{
private int count = 0;
@Override
public void run() {
while (true){
System.out.println("hello"+"子线程"+Thread.currentThread().getName()+(++count));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count==10){
break;
}
}
}
}
主程序
public class Thread02 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new T1());//创建子线程
for (int i = 1; i < 11; i++) {
System.out.println("hi" + i);
if (i == 5) {//说明主线程已输出5次hi
t1.start();//启动子线程,输出hello
t1.join();//立即将t1子线程插入到main线程,让t1先执行
}
try {
Thread.sleep(1000);//输出一次hi,让main线程休眠1s
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
线程终止
1、主程序
public class ThreadExit_ {
public static void main(String[] args) throws InterruptedException {
T t1 = new T();
t1.start();
//如果希望main线程去控制t1 线程的终止, 必须可以修改 loop
//让t1 退出run方法,从而终止 t1线程 -> 通知方式
//让主线程休眠 10 秒,再通知 t1线程退出
System.out.println("main线程休眠10s...");
Thread.sleep(10 * 1000);
t1.setLoop(false);
}
}
2、子线程
class T extends Thread {
private int count = 0;
//设置一个控制变量
private boolean loop = true;
@Override
public void run() {
while (loop) {
try {
Thread.sleep(50);// 让当前线程休眠50ms
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("T 运行中...." + (++count));
}
}
public void setLoop(boolean loop) {
this.loop = loop;
}
}
//互斥锁
// 需求:使用多线程,模拟三个窗口同时售票100张
public class SellTicket {
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;//让多个线程共享 ticketNum
private boolean loop = true;//控制run方法变量
Object object = new Object();
//同步方法(静态的)的锁为当前类本身
//老韩解读
//1. public synchronized static void m1() {} 锁是加在 SellTicket03.class
//2. 如果在静态方法中,实现一个同步代码块.
/*
synchronized (SellTicket03.class) {
System.out.println("m2");
}
*/
public synchronized static void m1() {
}
public static void m2() {
synchronized (SellTicket03.class) {
System.out.println("m2");
}
}
//说明
//1. public synchronized void sell() {} 就是一个同步方法
//2. 这时锁在 this对象
//3. 也可以在代码块上写 synchronize ,同步代码块, 互斥锁还是在this对象
public /*synchronized*/ void sell() { //同步方法, 在同一时刻, 只能有一个线程来执行sell方法
synchronized (/*this*/ 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));//1 - 0 - -1 - -2
}
}
@Override
public void run() {
while (loop) {
sell();//sell方法是一共同步方法
}
}
}
//死锁
//模拟线程死锁
public class DeadLock_ {
public static void main(String[] args) {
//模拟死锁现象
DeadLockDemo A = new DeadLockDemo(true);
A.setName("A线程");
DeadLockDemo B = new DeadLockDemo(false);
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 为 T, 线程A 就会先得到/持有 o1 对象锁, 然后尝试去获取 o2 对象锁
//2. 如果线程A 得不到 o2 对象锁,就会Blocked
//3. 如果flag 为 F, 线程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");
}
}
}
}
}