1.多线程创建
方法1:通过 继承 thread 类
[子线程代码] MyThread.java
package example01_thread; public class MyThread extends Thread { @Override public void run() { // super.run(); for (int i = 0; i <= 1000; i++) { System.out.println("===> 子线程 " + i); } } }
[主线程代码] Test.java
package example01_thread; public class Test { public static void main(String[] args) { MyThread myThread = new MyThread(); //myThread.run(); //方法调用,单线程。 myThread.start(); // 启动线程,多线程。 这个.statr() 继承自Thread类 for (int i = 0; i <= 1000; i++) { System.out.println("===> 主线程 " + i); } } }
[运行结果]
方法2:通过 实现 Runnable 接口
[子线程代码] MyRunnable.java
package example02_runnable; public class MyRunnable implements Runnable { @Override public void run() { for (int i = 0; i <= 1000; i++) { System.out.println("===> 子线程 " + i); } } }
[主线程代码] Test2.java
package example02_runnable; public class Test2 { public static void main(String[] args) { Runnable runnable = new MyRunnable(); Thread thread = new Thread(runnable); thread.start(); for (int i = 0; i <= 1000; i++) { System.out.println("===> 主线程 " + i); } } }
[运行结果]
2.线程中的相关方法
(1)设置优先级 setPrlorty()
优先级可以用从1到10的范围指定。10表示最高优先级(Thread.MAX_PRIORITY),1表示最低优先级(Thread.MIN_PRIORITY),5是普通优先级(Thread.NORM_PRIORITY,默认)。
优先级不能超出1-10的取值范围,否则抛出IllegalArgumentException
效果受操作系统影响,可能存在无效情况,因此不要有业务逻辑依赖于线程优先级,结果会无法预期
- Thread.setPriority()用来设定线程的优先级
- 在线程 start 方法被调用之前,线程的优先级应该被设定
- 线程的优先级不能超过所属线程组的优先级,即可以通过控制线程组的优先级,来控制线程组下线程的最大优先级
- 线程的优先级未设定时,默认所属的线程组的优先级
- 未指定线程优先级时,所有线程都携带普通优先级
- 优先级最高的线程在执行时被给予优先,最终由 CPU 调度程序决定哪一个线程被执行
- 与线程池中等待运行机会的线程相比,当前正在运行的线程可能总是拥有更高的优先级
- 高优先级线程不一定先于低优先级的线程运行。
[子线程代码] MyThread3.java
package example03_priority; public class MyThread3 extends Thread{ public MyThread3(String name){ super.setName(name); //设置线程名字 } @Override public void run() { //super.run(); for(int i = 0;i<=1000;i++){ System.out.println("===> 子线程 " +super.getName() + " "+ i); } } }
[主线程代码] Test3.java (未设置线程优先级)
package example03_priority; public class Test3 { public static void main(String[] args) { MyThread3 mt1 = new MyThread3("A"); MyThread3 mt2 = new MyThread3("B"); mt1.start(); mt2.start(); } }
[运行结果]
[主线程代码] Test3.java (设置线程优先级)
package example03_priority; public class Test3 { public static void main(String[] args) { MyThread3 mt1 = new MyThread3("A"); MyThread3 mt2 = new MyThread3("B"); mt1.setPriority(1); mt2.setPriority(10); mt1.start(); mt2.start(); } }
[运行结果]
(2)设置睡眠 sleep()
sleep,每隔x时间 去执行一个y操作。参数单位:毫秒
package example04_sleep; import java.text.SimpleDateFormat; import java.util.Date; public class MyThread4 extends Thread{ @Override public void run() { //让子系统不停的显示系统当前时间 while(true){ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date d = new Date(); System.out.println(sdf.format(d)); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { MyThread4 mt = new MyThread4(); mt.start(); } }
等待子线程执行完毕 join()
让主线程等待当前执行中子线程执行完毕。
[子线程代码]MyThread5.java
package example05_join; public class MyThread5 extends Thread{ @Override public void run() { // super.run(); for(int i = 0;i<=1000;i++){ System.out.println("===> 子线程 " + i); } } }
[主线程代码]Test5.java
package example05_join; public class Test { public static void main(String[] args) { MyThread5 myThread5 = new MyThread5(); myThread5.start(); for(int i = 0;i<=100;i++){ System.out.println("===> 主线程 " + i); } try { myThread5.join(); //此时让主线程等待子线程执行完毕,再继续执行 }catch (InterruptedException e){ e.printStackTrace(); } System.out.println("主线程执行完毕"); } }
让出CPU资源,让其他线程执行 yield()
让行,不代表不执行。具体谁先进行,依然由CPU决定,不能完全确定
[子线程代码]MyThread6.java
package example06_yield; public class MyThread6 extends Thread { public MyThread6(String name) { super.setName(name); } @Override public void run() { // super.run(); for (int i = 0; i <= 500; i++) { System.out.println(super.getName() + ":" + i); if (i % 10 == 0) { Thread.yield(); } } } }
[主线程代码]Test6.java
package example06_yield; import example04_sleep.yield.MyThread6; public class Test6 { public static void main(String[] args) { MyThread6 mt1 = new MyThread6("A"); MyThread6 mt2 = new MyThread6("B"); mt1.start(); mt2.start(); } }
[运行结果截图]
正如上图所见,让行是开发者设定的希望情况,但是并不代表一定成功让行。最终的决定权还是由CPU来决定
打断线程(尤其是正在睡眠中的线程) interrupt
[子线程代码]MyThread6.java
package example07.interrupt; public class MyThread7 extends Thread{ @Override public void run() { // super.run(); System.out.println("线程即将进行休眠"); try{ Thread.sleep(1000000); }catch (InterruptedException e){ // e.printStackTrace(); System.out.println("异常:线程休眠被打断"); } System.out.println("线程被激活"); } }
[主线程代码]Test6.java
package example07.interrupt; public class Test7 { public static void main(String[] args) { MyThread7 myThread7 = new MyThread7(); myThread7.start(); for(int i = 0;i<=100;i++){ System.out.println("===> 主线程 " + i); } myThread7.interrupt(); // 打断正在休眠中的子线程 } }
[运行结果截图]
3.线程同步
线程同步:当多个线程共享同一个资源的时候,我们可以在某一个线程访问到这个资源的时候,将这个资源暂时封锁
等待当前线程执行结束,解锁该资源,其他线程才可以来继续执行。
总结:等待其他线程释放锁
目的:让线程更加安全
下面以银行取钱案例说明。
[账户对象]Account.java
package example08_sync; public class Account { private double balance; public Account(double balance){ this.balance = balance; } public void getMoney(){ //在方法声明上添加synchronized关键字 一旦执行到该方法,瞬间锁定对象Account if(this.balance<=0){return;} System.out.println("即将取走1000,目前金额为:"+this.balance); this.balance -= 1000; System.out.println("成功取走1000,目前金额为:"+this.balance); } }
[取钱线程]GetMoneyThread.java
package example08_sync; public class GetMoneyThread extends Thread{ private Account acc; public GetMoneyThread(Account acc){ this.acc = acc; } @Override public void run() { acc.getMoney(); } }
[测试类]Test8.java
package example08_sync; public class Test8 { public static void main(String[] args) { //创建账户 Account account = new Account(1000); //创建ATM线程 GetMoneyThread atm = new GetMoneyThread(account); //创建柜台线程 GetMoneyThread table = new GetMoneyThread(account); //取钱 atm.start(); table.start(); } }
[运行结果]
可以发现,设置的余额不足拦截(if(this.balance<=0){return;}
)没有生效,出现了账户余额为负数情况。
为避免这些情况的出现,因此需要线程同步。即将多个处理统一资源的线程,进行队列化管理(锁)
实现线程同步的方法
方法1:在方法声明上添加一个synchronized
关键字
package example08_sync; public class Account { private double balance; public Account(double balance){ this.balance = balance; } public synchronized void getMoney(){ //在方法声明上添加synchronized关键字 一旦执行到该方法,瞬间锁定对象Account if(this.balance<=0){return;} System.out.println("即将取走1000,目前金额为:"+this.balance); this.balance -= 1000; System.out.println("成功取走1000,目前金额为:"+this.balance); } }
方法2:在方法内部添加一个synchronized
关键字
package example08_sync; public class Account { private double balance; public Account(double balance){ this.balance = balance; } public synchronized void getMoney(){ //在方法声明上添加synchronized关键字 一旦执行到该方法,瞬间锁定对象Account synchronized (this){ if(this.balance<=0){return;} System.out.println("即将取走1000,目前金额为:"+this.balance); this.balance -= 1000; System.out.println("成功取走1000,目前金额为:"+this.balance); } } }
方法3:手动上锁
该方法用的最少,因为容易出现忘记解锁。
package example08_sync; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Account { private double balance; private Lock lock = new ReentrantLock(); //创建锁 public Account(double balance){ this.balance = balance; } public void getMoney(){ //在方法声明上添加synchronized关键字 一旦执行到该方法,瞬间锁定对象Account lock.lock(); // 上锁 if(this.balance<=0){return;} System.out.println("即将取走1000,目前金额为:"+this.balance); this.balance -= 1000; System.out.println("成功取走1000,目前金额为:"+this.balance); lock.unlock(); //解锁 } }
死锁
线程同步时最容易发生的问题
eg:线程A锁定资源1后,等待访问资源2;线程2锁定资源2后,等待访问资源1。
[资源对象] ResourceObjec.java
package example09_dead; public class ResourceObject { public static Object obj1 = new Object(); public static Object obj2 = new Object(); }
[线程1] DeadLock1.java
package example09_dead; public class DeadLock1 extends Thread{ @Override public void run() { // super.run(); synchronized (ResourceObject.obj1){ System.out.println("线程1 第一个资源锁定"); try{ //通过休眠使CPU去执行其他线程 Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (ResourceObject.obj2){ System.out.println("线程1 第二个资源锁定"); System.out.println("线程1执行完毕"); } } } }
[线程2] DeadLock2.java
package example09_dead; public class DeadLock2 extends Thread{ @Override public void run() { // super.run(); synchronized (ResourceObject.obj2){ System.out.println("线程2 第二个资源锁定"); try{ //通过休眠使CPU去执行其他线程 Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (ResourceObject.obj1){ System.out.println("线程2 第一个资源锁定"); System.out.println("线程2执行完毕"); } } } }
[测试] Test9.java
package example09_dead; public class Test9 { public static void main(String[] args) { DeadLock1 dl1 = new DeadLock1(); DeadLock2 dl2 = new DeadLock2(); dl1.start(); dl2.start(); } }
[运行结果]
线程的生命周期
- [1]创建线程
- ---[start()]--> [2]就绪状态
- ---[CPU调度]--> [3]运行状态
- ---[stop()]--> (跳转到4)
- ---[IO操作、sleep]--> 阻塞状态 ---[IO结束、sleep()结束]--> (跳转到2)
- --------------> [4]消亡状态
生产者消费者模型
案例:实现对视频进行审查,如果是不良视频,则对视频进行删除
[视频对象] Video.java
package example10_check; public class Video { private String name; public Video (String name){ this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
[检查视频] CheckVideo.java
package example10_check; import java.util.concurrent.BlockingQueue; import java.util.concurrent.atomic.AtomicInteger; public class CheckVideo extends Thread{ private static AtomicInteger i = new AtomicInteger(); //AtomicInteger原子型整数。特点:线程安全 效果等同于 int i = 0,但是比int i = 0 更安全。 //将视频添加到缓冲区(队列) private BlockingQueue<Video> videos; public CheckVideo(BlockingQueue<Video> videos){ this.videos = videos; } @Override public void run() { while (true){ String name = "不良视频"+i.incrementAndGet();// 相当于i++ Video v = new Video(name); //将视频添加到队列 try { System.out.println("发现不良视频"+name); videos.put(v); Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } } }
[删除视频] DelVideo.java
package example10_check; import java.util.TreeMap; import java.util.concurrent.BlockingQueue; public class DelVideo extends Thread { private BlockingQueue<Video> videos; public DelVideo(BlockingQueue<Video> videos){ this.videos = videos; } @Override public void run() { while(true){ try{ Video video = videos.take(); System.out.println("删除不良视频"+video.getName()); Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
[测试] Test10.java
package example10_check; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; public class Test10 { public static void main(String[] args) { BlockingQueue<Video> videos = new LinkedBlockingQueue<Video>(); //创建3个检查视频的线程 CheckVideo ck1 = new CheckVideo(videos); CheckVideo ck2 = new CheckVideo(videos); CheckVideo ck3 = new CheckVideo(videos); //创建两个删除视频的线程 DelVideo d1 = new DelVideo(videos); DelVideo d2 = new DelVideo(videos); ck1.start(); ck2.start(); ck3.start(); d1.start(); d2.start(); } }
[运行结果]