(6)守护线程
守护线程是一种特殊的线程,它属于一种陪伴线程。
简答来讲Java中含有两种线程:用户线程和守护线程。可以永isDaemon()方法判断,若返回false则是用户线程,若返回true则是守护线程。主线程main是用户线程。
典型的守护线程即垃圾回收线程,只要当前JVM中存在任何一个非守护线程没有结束,守护线程就在工作,只有当最后一个非守护线程结束时,守护线程才会随着非守护线程一起停止工作。
/* * 守护线程 * */ class MyRunnable implements Runnable{ private int num; public void run() { try { while(true) { num++; System.out.println("线程名是:"+Thread.currentThread().getName() +" num:"+num+"是不是守护线程:"+Thread.currentThread().isDaemon()); Thread.sleep(1000); } } catch (InterruptedException e) { System.out.println("线程名是"+Thread.currentThread().getName()+"中断了"); } } } public class MoreThread4_25{ public static void main(String args[]) throws InterruptedException { Thread thread = new Thread(new MyRunnable(),"子线程1"); //设置为守护线程,必须在start之前 thread.setDaemon(true); thread.start(); Thread thread2 = new Thread(new MyRunnable(),"子线程2"); thread2.start(); Thread.sleep(1000); //中断守护线程 thread2.interrupt(); Thread.sleep(10000); System.out.println("代码结束"); } }
1、线程的同步与死锁
线程的同步与死锁操作的核心问题:每一个线程对象轮番抢占资源带来的问题。
(1)同步问题的引出
/* * 同步问题的引出 * */ class MyRunnable implements Runnable{ private int ticketNum = 10; public void run() { //还有票 while(this.ticketNum > 0) { try { //模拟网络延迟 Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第"+ this.ticketNum-- +"张票"); } } } public class MoreThread4_25{ public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable(); new Thread(myRunnable,"一号售票处").start(); new Thread(myRunnable,"二号售票处").start(); new Thread(myRunnable,"三号售票处").start(); } }
由以上运行结果可以发现票数出现了负数,则将这种问题称为不同步操作。不同步的唯一好处就是:多个线程并发执行,处理速度较快。
(2)同步处理
所谓同步即就是所有线程并非一起进入方法中执行而是按照一个一个的顺序有序进入。
A. synchronized处理同步问题
我们可以使用synchronize关键字来实现这把“锁”的功能。
方案一:使用同步代码块
/* * 同步代码块: * 进入方法中的线程可能有多个 * */ class MyRunnable implements Runnable{ private int ticketNum = 10; public void run() { for(int i = 0 ;i < 100 ;i++) { //在同一个线程中只允许一个线程进入代码块中 synchronized (this) {//为程序逻辑上锁 if(this.ticketNum > 0) { try { //模拟网络延迟 Thread.sleep(300); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第"+ this.ticketNum-- +"张票"); } } } } } public class MoreThread4_25{ public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable(); new Thread(myRunnable,"一号售票处").start(); new Thread(myRunnable,"二号售票处").start(); new Thread(myRunnable,"三号售票处").start(); } }
方案二:使用同步方法
/* * 同步方法: * */ class MyRunnable implements Runnable{ private int ticketNum = 10; public void run() { for(int i = 0 ; i < 100 ; i++) { this.saleTicket(); } } public synchronized void saleTicket() { //还有票 if(this.ticketNum > 0) { try { Thread.sleep(300); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第"+ this.ticketNum-- +"张票"); } } } public class MoreThread4_25{ public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable(); new Thread(myRunnable,"一号售票处").start(); new Thread(myRunnable,"二号售票处").start(); new Thread(myRunnable,"三号售票处").start(); } }
由上面同步代码块和同步方法的使用可以看出,同步可以保证数据的完整性(线程安全操作),但是执行的速度会很慢。
class Sync{ public synchronized void test() { System.out.println("test方法的开始,线程名为: " + Thread.currentThread().getName()); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test方法结束,线程名为: " + Thread.currentThread().getName()); } } class MyRunnable implements Runnable{ public void run() { Sync sync = new Sync(); sync.test(); } } public class MoreThread4_25{ public static void main(String[] args) { for(int i = 1; i < 4;i++) { MyRunnable myRunnable = new MyRunnable(); new Thread(myRunnable).start(); } } }
由此我们可以发现,synchronize并没有起到作用,三个线程同时运行test方法。
实则synchronize(this)及非静态的synchronize方法,只是防止多个线程同时执行同一个对象的同步代码块。即synchronize锁住的是括号中的对象而非代码。对于非静态的synchronize方法,锁住的即就是对象本身this。
当synchronize锁住一个对象后,别的线程如果也想拿到这个对象的锁就必须等待这个线程释放锁,才能继续给此对象加锁,这样才能达到线程同步的目的。即使两个不同的代码块要对同一对象进行锁操作,那么这两个代码段也不能在多线程的环境下同时运行。
那么如何对如上问题进行封锁呢?
我们可以对其使用全局锁,即锁这个类对象的类。
/* * 全局锁 * */ class Sync{ public synchronized void test() { //对Sync类的类进行封锁 synchronized(Sync.class){ System.out.println("test方法的开始,线程名为: " + Thread.currentThread().getName()); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test方法结束,线程名为: " + Thread.currentThread().getName()); } } } class MyRunnable implements Runnable{ public void run() { Sync sync = new Sync(); sync.test(); } } public class MoreThread4_25{ public static void main(String[] args) { for(int i = 1; i < 4;i++) { MyRunnable myRunnable = new MyRunnable(); new Thread(myRunnable).start(); } } }
使用synchronized(Sync.class)即可实现全局锁的功能,所以要想锁住代码段,锁住多个对象的同一方法,使用这种全局锁,锁的是类而不是this。
(3)死锁
同步的本质:一个线程等待另外一个线程执行完后才可继续执行,但是若相关的几个线程彼此都在等待则就会造成死锁。且死锁一旦出现,整个程序就将中断执行。过多的同步会造成死锁。
/* * 死锁 * */ class Pen{ private String pen = "笔"; public String getPen() { return pen; } } class NoteBook{ private String noteBook = "笔记本"; public String getnoteBook() { return noteBook; } } public class MoreThread4_25{ private static Pen pen = new Pen(); private static NoteBook noteBook = new NoteBook(); public void deadLock() { //笔的线程 Thread thread1 = new Thread(new Runnable() { public void run() { synchronized (pen) { System.out.println(Thread.currentThread()+"我有笔但我不给你!!"); synchronized (noteBook) { System.out.println(Thread.currentThread()+"请把你的笔记本给我"); } } } },"Pen"); //笔记本的线程 Thread thread2 = new Thread(new Runnable() { public void run() { synchronized (noteBook) { System.out.println(Thread.currentThread()+"我有笔记本但我不给你!!"); synchronized (pen) { System.out.println(Thread.currentThread()+"请把你的笔给我"); } } } },"Pen"); thread1.start(); thread2.start(); } public static void main(String[] args) { new MoreThread4_25().deadLock(); } }