目录
一、和线程调度有关的方法
二、线程同步机制
三、练习题
四、死锁🔒
五、守护线程和定时器
六、实现线程的第三种方式 ,实现Callable接口
七、Object类中的wait和notify方法—生产者和消费者模式
一、和线程调度有关的方法
1.void setPriority(int newPriority) 设置线程的优先级
2.int getPriority() 获取线程优先级
package com.java.javase.Thread;public class ThreadTest08 {public static void main(String[] args) {Thread currentThread=Thread.currentThread();System.out.println(currentThread.getName()+"线程的默认优先级是:"+currentThread.getPriority());Thread t=new Thread(new MyThread8());t.setName("t");t.start();}}class MyThread8 implements Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"线程的默认优先级是:"+Thread.currentThread().getPriority());}}
输出结果:
main线程的默认优先级是:5
t线程的默认优先级是:5
3.Thread.yield() 当前线程暂停一下
package com.java.javase.Thread;public class ThreadTest {public static void main(String[] args) {/*** 让位,让当前线程暂停,回到就绪状态,让给其他线程* 静态方法:Thread.yield();*/Thread t=new Thread(new MyRunable());t.setName("t");t.start();for(int i=0;i<100;i++){System.out.println(Thread.currentThread().getName()+"--->"+i);}}}class MyRunable implements Runnable{@Overridepublic void run() {for(int i=0;i<100;i++){// 每10个让位一次if(i%10==0){Thread.yield(); // 当前线程暂停一下,让给主线程}System.out.println(Thread.currentThread().getName()+"--->"+i);}}}
4.void join() 合并线程
package com.java.javase.Thread;public class ThreadTest {public static void main(String[] args) throws InterruptedException {System.out.println("main begin");Thread t=new Thread(new MyRunable());t.setName("t");t.start();// 合并线程t.join(); // t合并到当前线程中,当前线程受到阻塞,t线程执行直到结束,栈之间进行了协调System.out.println("main over");}}class MyRunable implements Runnable{@Overridepublic void run() {for(int i=0;i<5;i++){System.out.println(Thread.currentThread().getName()+"--->"+i);}}}
输出结果:
main begin
t--->0
t--->1
t--->2
t--->3
t--->4
main over
二、线程同步机制
不使用线程同步机制下—模拟两个线程对同一个账户取款
账户类
package com.java.javase.ThreadSafe;/**银行账户*/public class Account {private String no;private double balance;public Account() {}public Account(String no, double balance) {this.no = no;this.balance = balance;}public String getNo() {return no;}public void setNo(String no) {this.no = no;}public double getBalance() {return balance;}public void setBalance(double balance) {this.balance = balance;}// 提供一个取款的方法public void withDraw(double money){// 未使用线程同步机制// t1和t2并发这个方法(t1和t2是两个栈,两个栈操作堆中同一个对象)double before=this.getBalance();double after=before-money;this.setBalance(after);}}
线程类
package com.java.javase.ThreadSafe;public class AccountThread extends Thread {// 两个线程必须共享同一个账户对象private Account act;public AccountThread(Account act){this.act=act;}@Overridepublic void run() {double money=5000;act.withDraw(money);System.out.println(Thread.currentThread().getName()+"对001账户取款成功 "+"剩余余额:"+act.getBalance() );}}
测试类
package com.java.javase.ThreadSafe;public class Test {public static void main(String[] args) {// 相当于同一张银行卡,属性有卡的账号和余额Account act=new Account("001",10000);// 两个栈操作堆中同一个对象,相当于两个人对同一张银行卡进行操作Thread t1=new AccountThread(act);Thread t2=new AccountThread(act);t1.setName("t1");t2.setName("t2");t1.start();t2.start();}}
安全情况下:
t2对001账户取款成功 剩余余额:0.0
t1对001账户取款成功 剩余余额:5000.0
但是会出现线程安全问题:
t2对001账户取款成功 剩余余额:5000.0
t1对001账户取款成功 剩余余额:5000.0
如何使用线程同步机制解决线程安全问题:
我们只需要将账户类中的取款方法改为:
package com.java.javase.ThreadSafe;/**银行账户*/public class Account {private String no;private double balance;public Account() {}public Account(String no, double balance) {this.no = no;this.balance = balance;}public String getNo() {return no;}public void setNo(String no) {this.no = no;}public double getBalance() {return balance;}public void setBalance(double balance) {this.balance = balance;}// 提供一个取款的方法public void withDraw(double money) {// 使用线程同步机制synchronized (this){// 这几行代码必须是排队的,不能并发// 线程同步机制的语法:// synchronized (){线程同步代码块}// ()中的内容相当关键/*** ()中写什么?* 那要看你想要那些线程同步?* 假设你只希望t1,t2,t3线程在该处排队执行,则需要在()中写一个t1 t2 t3共享的对象* 这里的共享对象是:账户对象*/try{Thread.sleep(1000);}catch (InterruptedException e){}double before=this.balance;double after=before-money;this.setBalance(after);}}}
synchronized执行原理:
1.假设t1和t2线程并发,开始执行同步代码块的时候,肯定有一个先有一个后
2.假设t1先执行了,遇到了synchronized关键字,这个时候自动找”后面共享对象“的对象锁,找到之后占有这把锁,然后执行同步代码块中的程序,在程序执行过程中一直都是占有这把锁的,直到同步代码块执行结束,这把锁才会被释放
3.假设t1已经占有这把锁,此时t2也遇到synchronized关键字,也会去占有后面共享对象的这把锁,结果这把锁已经被t1占有,t2只能在同步代码块外面等待t1的结束,直到t1把同步代码块执行结束了,t1归还了这把锁,此时t2终于等到这把锁,然后t2占有这把锁,进入同步代码块执行程序,这样就达到了线程排队执行
synchronized可以出现实例方法上:
package com.java.javase.ThreadSafe;/**银行账户*/public class Account {private String no;private double balance;public Account() {}public Account(String no, double balance) {this.no = no;this.balance = balance;}public String getNo() {return no;}public void setNo(String no) {this.no = no;}public double getBalance() {return balance;}public void setBalance(double balance) {this.balance = balance;}// 提供一个取款的方法/*** 在实例方法上可以使用synchronized吗?可以* synchronized出现在实例方法上,一定锁的是this,这种方式不灵活* 另外还有一个缺点:synchronized出现在实例方法上,表示整个方法体都需要同步* 可能会无故扩大同步的范围,导致程序的执行效率降低,所以这种方式不常用* 优点:代码简洁,如果共享对象一定是this,建议使用这种方式*/public synchronized void withDraw(double money) {double before=this.balance;double after=before-money;try{Thread.sleep(1000);}catch (InterruptedException e){}this.setBalance(after);}}
三、练习题
练习1:
package com.java.javase.Thread;public class ThreadTest {public static void main(String[] args) throws InterruptedException {MyClass mc=new MyClass();Thread t1=new MyThread(mc);Thread t2=new MyThread(mc);t1.setName("t1");t2.setName("t2");t1.start();Thread.sleep(1000); // 确保t1线程先执行t2.start();}}class MyThread extends Thread{private MyClass mc;public MyThread(MyClass mc){this.mc=mc;}public void run(){if(Thread.currentThread().getName().equals("t1")){mc.doSome();}else if(Thread.currentThread().getName().equals("t2")){mc.doOther();}}}class MyClass{public synchronized void doSome(){System.out.println("doSome begin");try{Thread.sleep(1000*10);}catch (InterruptedException e){e.printStackTrace();}System.out.println("doSome over");}/*** doOther方法的执行需要等待doSome方法的结束吗?* 不需要,因为该方法的执行不需要获取锁*/public void doOther(){System.out.println("doOther begin");System.out.println("doOther over");}}
输出结果:
doSome begin
doOther begin
doOther over
doSome over
在上题中的doOther方法上加上synchronized,结果会怎么样?
package com.java.javase.Thread;public class ThreadTest {public static void main(String[] args) throws InterruptedException {MyClass mc=new MyClass();Thread t1=new MyThread(mc);Thread t2=new MyThread(mc);t1.setName("t1");t2.setName("t2");t1.start();Thread.sleep(1000); // 确保t1线程先执行t2.start();}}class MyThread extends Thread{private MyClass mc;public MyThread(MyClass mc){this.mc=mc;}public void run(){if(Thread.currentThread().getName().equals("t1")){mc.doSome();}else if(Thread.currentThread().getName().equals("t2")){mc.doOther();}}}class MyClass{public synchronized void doSome(){System.out.println("doSome begin");try{Thread.sleep(1000*10);}catch (InterruptedException e){e.printStackTrace();}System.out.println("doSome over");}/*** doOther方法的执行需要等待doSome方法的结束吗?* synchronized 出现在实例方法上,表示锁的是this* 需要,因为该方法的执行需要获取锁*/public synchronized void doOther(){System.out.println("doOther begin");System.out.println("doOther over");}}
输出结果:
doSome begin
doSome over
doOther begin
doOther over
synchronized 出现在静态方法上:
package com.java.javase.Thread;public class ThreadTest {public static void main(String[] args) throws InterruptedException {MyClass mc=new MyClass();Thread t1=new MyThread(mc);Thread t2=new MyThread(mc);t1.setName("t1");t2.setName("t2");t1.start();Thread.sleep(1000); // 确保t1线程先执行t2.start();}}class MyThread extends Thread{private MyClass mc;public MyThread(MyClass mc){this.mc=mc;}public void run(){if(Thread.currentThread().getName().equals("t1")){mc.doSome();}else if(Thread.currentThread().getName().equals("t2")){mc.doOther();}}}class MyClass{public synchronized static void doSome(){System.out.println("doSome begin");try{Thread.sleep(1000*10);}catch (InterruptedException e){e.printStackTrace();}System.out.println("doSome over");}/*** doOther方法的执行需要等待doSome方法的结束吗?* synchronized 出现在静态方法上,表示锁的是类锁* 对象可以有多个,但是类锁只有一把,所以需要等待*/public synchronized static void doOther(){System.out.println("doOther begin");System.out.println("doOther over");}}
doSome begin
doSome over
doOther begin
doOther over
四、死锁🔒
package com.java.javase.deadLock;public class ThreadTest {public static void main(String[] args) throws InterruptedException {Object o1=new Object();Object o2=new Object();// t1和t2两个线程共享o1,o2Thread t1=new MyThread1(o1,o2);Thread t2=new MyThread2(o1,o2);t1.start();t2.start();}}class MyThread1 extends Thread{Object o1;Object o2;public MyThread1(Object o1,Object o2){this.o1=o1;this.o2=o2;}@Overridepublic void run() {synchronized (o1){try {Thread.sleep(1000*5);} catch (InterruptedException e) {e.printStackTrace();}synchronized (o2){}}}}class MyThread2 extends Thread{Object o1;Object o2;public MyThread2(Object o1,Object o2){this.o1=o1;this.o2=o2;}@Overridepublic void run() {synchronized (o2){try {Thread.sleep(1000*5);} catch (InterruptedException e) {e.printStackTrace();}synchronized (o1){}}}}
五、守护线程和定时器
package com.java.javase.Thread;public class ThreadTest {public static void main(String[] args) throws InterruptedException {Thread t1=new MyThread();t1.setName("备份数据的线程");// 启动线程之前,将线程设置为守护线程t1.setDaemon(true);t1.start();// 主线程:用户线程for(int i=0;i<10;i++){System.out.println(Thread.currentThread().getName()+"---"+i);Thread.sleep(1000);}}}class MyThread extends Thread{@Overridepublic void run() {// 即使是死循环,但由于该线程是守护线程,当用户线程结束,守护线程自动停止int i=0;while (true) {System.out.println(Thread.currentThread().getName() + "---" + (i++));try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}
备份数据的线程---7
备份数据的线程---8
main---8
main---9
备份数据的线程---9
备份数据的线程---10
Process finished with exit code 0
定时器:
package com.java.javase.Timer;import java.text.SimpleDateFormat;import java.util.Date;import java.util.Timer;import java.util.TimerTask;public class TimerTest01 {public static void main(String[] args) throws Exception{SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date firstTime=sdf.parse("-11-01 14:36:00");// 创建定时器对象Timer timer=new Timer();// 三个参数:指定定时任务,第一次执行时间,间隔多久执行一次timer.schedule(new LogTimerTask(),firstTime,1000*10);}}// 编写一个定时任务类// TimerTask实现了Runnable接口class LogTimerTask extends TimerTask{@Overridepublic void run() {SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");String strTime=sdf.format(new Date());System.out.println(strTime+":完成了一次数据备份");}}
-11-01 14:36:00 016:完成了一次数据备份
-11-01 14:36:10 019:完成了一次数据备份
-11-01 14:36:20 035:完成了一次数据备份
六、实现线程的第三种方式 ,实现Callable接口
package com.java.javase.Thread;import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.FutureTask;/*** 实现线程的第三种方式:*实现Callable接口*/public class ThreadTest {public static void main(String[] args) throws InterruptedException, ExecutionException {// 创建一个"未来任务类"对象// 需要给一个Callable接口实现类对象FutureTask futureTask=new FutureTask(new Callable() {@Overridepublic Object call() throws Exception {// call()方法就相当于run方法,只不过这个有返回值// 线程执行一个任务,执行之后可能会有一个执行结果System.out.println("call begin");Thread.sleep(1000*10);System.out.println("call over");return new Object();}});// 创建线程对象// 这里是main方法,在主线程中// 在主线程中怎么获取t线程的返回结果?// get()方法的执行会导致当前线程阻塞Thread t=new Thread(futureTask);t.start();futureTask.get();// main方法的这里的程序要想执行必须等待get()方法的结束// 而get()方法可能需要很久System.out.println("拿到执行结果");}}
七、Object类中的wait和notify方法—生产者和消费者模式
package com.java.javase.ProducerAndCustomer;import java.util.ArrayList;import java.util.List;// 使用wait方法和notify方法实现“生产者和消费者模式”public class ThreadTest {public static void main(String[] args) {List list=new ArrayList();Thread t1=new Thread(new Producer(list));Thread t2=new Thread(new Customer(list));t1.setName("生产者线程");t2.setName("消费者线程");t1.start();t2.start();}}// 生产线程class Producer implements Runnable{// 仓库private List list;public Producer(List list) {this.list = list;}@Overridepublic void run() {while(true){// 给仓库对象list加锁synchronized (list){// 大于0,说明仓库中已经有一个元素了if(list.size()>0){try {// 当前线程进入等待状态,并且释放Producer之前占有的list集合的锁list.wait();} catch (InterruptedException e) {e.printStackTrace();}}Object obj = new Object();list.add(obj);System.out.println(Thread.currentThread().getName() + "-----生产了一个" + obj);list.notifyAll();}}}}// 消费线程class Customer implements Runnable{private List list;public Customer(List list) {this.list = list;}@Overridepublic void run() {while(true){synchronized (list){if(list.size()==0){try {list.wait();} catch (InterruptedException e) {e.printStackTrace();}}Object obj=list.remove(0);System.out.println(Thread.currentThread().getName()+"-----消费了一个"+obj);list.notifyAll();}}}}