Java守护线程
守护线程是为其他线程服务的线程,在所守护线程执行完毕之后就会退出
TurtleThread2 t2就是main的守护线程
package test01;
class TurtleThread2 extends Thread{
public TurtleThread2(String name) {
super(name);
}
public void run(){
long timeStart=System.currentTimeMillis();
System.out.println(Thread.currentThread().getName() + "起跑了");
for (int i = 1; i <= 1000; i++) {
try {
System.out.println(Thread.currentThread().getName() + "跑了" + i + "米");
Thread.sleep(700);
} catch (InterruptedException e) {
System.out.println("触发");
}
if(Thread.currentThread().isInterrupted()){
System.out.println("检测到已经中断");
}
}
System.out.println(Thread.currentThread().getName() + "到终点了");
long timeEnd=System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+"用时"+(timeEnd-timeStart));
}
}
public class Main {
public static void main(String[] args) throws Exception {
long timeStart=System.currentTimeMillis();
TurtleThread2 t2=new TurtleThread2("乌龟2");
t2.setDaemon(true);//设为守护线程,需要在start()之前
//main线程结束 守护进程就会结束
t2.start();
System.out.println(Thread.currentThread().getName() + "起跑了");
for (int i = 1; i <= 10; i++) {
try {
System.out.println(Thread.currentThread().getName() + "跑了" + i + "米");
Thread.sleep(700);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "到终点了");
long timeEnd=System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+"用时"+(timeEnd-timeStart));
}
}
线程安全与线程不安全
测试类Main.java
package test01;
public class Main {
public static int count=0;
public static void main(String[] args) throws InterruptedException {
int ticket=10;
TicketWindow window=new TicketWindow(ticket);
Thread thread1=new Thread(window);
thread1.setName("窗体一");
Thread thread2=new Thread(window);
thread2.setName("窗体二");
Thread thread3=new Thread(window);
thread3.setName("窗体三");
thread1.start();
thread2.start();
thread3.start();
}
}
线程安全
package test01;
public class TicketWindow implements Runnable {
private int ticket = 10;
private Object obj = new Object();
public TicketWindow(int ticket) {
this.ticket = ticket;
}
@Override
public void run() {
while (true) {
//对变量加锁,线程安全
synchronized (obj) {//同步代码块,只有一个线程访问ticket
if (ticket > 0) {
try {
Thread.sleep(1000);
ticket -= 1;
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖票 1 张,余票 "+ticket+" ,时间戳"+System.currentTimeMillis());
}else{
return;
}
}
}
}
}
输出:
窗体一卖票 1 张,余票 9 ,时间戳1610983793485
窗体一卖票 1 张,余票 8 ,时间戳1610983794558
窗体三卖票 1 张,余票 7 ,时间戳1610983795573
窗体二卖票 1 张,余票 6 ,时间戳1610983796587
窗体三卖票 1 张,余票 5 ,时间戳1610983797598
窗体一卖票 1 张,余票 4 ,时间戳1610983798611
窗体三卖票 1 张,余票 3 ,时间戳1610983799624
窗体二卖票 1 张,余票 2 ,时间戳1610983800633
窗体三卖票 1 张,余票 1 ,时间戳1610983801648
窗体三卖票 1 张,余票 0 ,时间戳1610983802658
一次打印一行,余票逐一递减,有时会出现只有一个窗口售票的情况,很有可能是因为自己的cpu运行状态和我不同导致的,可以尝试增加票量,private int ticket = 100;
线程不安全
package test01;
public class TicketWindow implements Runnable {
private int ticket = 10;
private Object obj = new Object();
public TicketWindow(int ticket) {
this.ticket = ticket;
}
@Override
public void run() {
while (true) {
//线程不安全
if (ticket > 0) {//满足卖票条件ticket一定>0
try {
Thread.sleep(1000);//如果不是线程安全,在sleep的时候ticket可能会被其他线程改变
ticket -= 1;//这里ticket就不一定>0了
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖票 1 张,余票 "+ticket+" ,时间戳"+System.currentTimeMillis());
}else{
return;
}
}
}
}
输出:
窗体二卖票 1 张,余票 8 ,时间戳1610983544023
窗体一卖票 1 张,余票 7 ,时间戳1610983544024
窗体三卖票 1 张,余票 8 ,时间戳1610983544023
窗体二卖票 1 张,余票 6 ,时间戳1610983545099
窗体三卖票 1 张,余票 5 ,时间戳1610983545100
窗体一卖票 1 张,余票 5 ,时间戳1610983545100
窗体二卖票 1 张,余票 2 ,时间戳1610983546114
窗体一卖票 1 张,余票 3 ,时间戳1610983546114
窗体三卖票 1 张,余票 3 ,时间戳1610983546114
窗体二卖票 1 张,余票 1 ,时间戳1610983547129
窗体三卖票 1 张,余票 0 ,时间戳1610983547129
窗体一卖票 1 张,余票 -1 ,时间戳1610983547129
大概是每次(三个打印时间很接近)打印三行,可以看出余票显示混乱,而且有时还出现负数
上面采用同步代码块实现线程安全,下面采用同步方法实现同样效果
package test01;
public class TicketWindow implements Runnable {
private int ticket = 10;
private Object obj = new Object();
public TicketWindow(int ticket) {
this.ticket = ticket;
}
synchronized void sell(){
if (ticket > 0) {//满足卖票条件ticket一定>0
try {
Thread.sleep(1000);//如果不是线程安全,在sleep的时候ticket可能会被其他线程改变
ticket -= 1;//这里ticket就不一定>0了
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖票 1 张,余票 "+ticket+" ,时间戳"+System.currentTimeMillis());
}
}
@Override
public void run() {
while (true){
sell();
if(!(ticket>0)){
return;
}
}
}
}
还可以通过手动加锁解锁实现线程安全
package test01;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TicketWindow implements Runnable {
private int ticket = 100;
private Object obj = new Object();
Lock lock=new ReentrantLock(true);
public TicketWindow(int ticket) {
this.ticket = ticket;
}
void sell() {
if (ticket > 0) {
try {
Thread.sleep(1000);
ticket -= 1;
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖票 1 张,余票 " + ticket + " ,时间戳" + System.currentTimeMillis());
}
}
@Override
public void run() {
while (true) {
lock.lock();//上锁
sell();
lock.unlock();//解锁
if (!(ticket > 0)) {
return;
}
}
}
}