用户线程:也叫工作线程,当线程的任务执行完或者通知方法结束。平时用到的普通线程均是用户线程,当在Java程序中创建一个线程,它就被称为用户线程
守护线程(Daemon):一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
常见的守护线程:垃圾回收机制
例子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("八戒收拾东西回高老庄...");
}
}
}
例子
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();
}
}
}
}
同步代码块
synchronized(对象){//得到对象的锁,才能操作同步代码
//需要被同步的代码
}
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方法是一个同步方法
}
}
}
synchronized实现同步的基础:Java中的每一个对象都可以作为锁。
具体表现为以下3种形式。
对于普通同步方法,锁是当前实例对象。
对于静态同步方法,锁是当前类的Class对象。
对于同步方法块,锁是Synchonized括号里配置的对象。
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方法是一个同步方法
}
}
}
多个线程都占用了对方的锁资源,但不肯相让,导致了死锁。
在编程中一定要避免死锁的发生。
例子:
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");
}
}
}
}
}
如下图:两个线程卡住了
下面操作会释放锁:
下面的操作不会释放锁:
线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行,不会释放锁
线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁。
提示:应尽量避免使用suspend()和resume()来控制线程,这两个方法不再推荐使用
(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;
}
}
}
}
(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();
}
}
}
}
转载自: https://www.cnblogs.com/liyuelian/p/16659594.html