Java多线程基础(二)
1.多线程数据安全
线程同步:多个线程需要访问同一资源时,需要以某种顺序来确定该资源某一时刻只能被一个线程使用。从而,解决并发操作可能带来的异常。
2.同步代码块实现同步(部分代码的访问,我们希望它同步)
synchronized(lock){ //同步代码块 }
其中lock就是同步监视器,它的含义是:线程开始执行同步代码块之前,必须先获得对同步监视器的锁定。任何时刻只能有一个线程可以获得对同步监视器的锁定,当同步代码块执行完成后,该线程会释放对该同步监视器的锁定。虽然java程序允许使用任何对象作为同步监视器,但是同步监视器的目的就是为了阻止两个线程对同一个共享资源进行并发访问,因此通常使用可能被并发访问的共享资源充当同步监视器。
下面的实例可方便理解:
package com.test; public class ThreadDemo1 { public static void main(String[] args) { // TODO Auto-generated method stub //创建共享资源对象 TicketRes ticketRes=new TicketRes(); //创建线程对象 Thread w1=new Thread(ticketRes,"窗口1"); Thread w2=new Thread(ticketRes,"窗口2"); Thread w3=new Thread(ticketRes,"窗口3"); w1.start(); w2.start(); w3.start(); } } //共享资源类 class TicketRes implements Runnable{ private int ticket=100; private Object lock=new Object();//锁 @Override public void run() { // TODO Auto-generated method stub while(true) { synchronized (lock) { if(ticket>=1) { try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"卖第"+ticket+"张票"); ticket--; }else { break; } } } } }
3.同步方法实现同步(强调功能,方法的访问是同步的)
同步方法就是使用synchronized关键字修饰某个方法,用synchronized修饰的方法就是同步方法。这个同步方法(非static方法)无须显式指定同步监视器,同步方法的同步监视器是this,也就是调用该方法的对象,通过同步方法可以非常方便的实现线程安全的类。
下面的实例可方便理解:
package com.test; public class ThreadDemo2 { public static void main(String[] args) { // TODO Auto-generated method stub //创建共享资源对象 TicketRes1 ticketRes=new TicketRes1(); //创建线程对象 Thread w1=new Thread(ticketRes,"窗口1"); Thread w2=new Thread(ticketRes,"窗口2"); Thread w3=new Thread(ticketRes,"窗口3"); w1.start(); w2.start(); w3.start(); } } //共享资源类 class TicketRes1 implements Runnable{ private static int ticket=100; private Object lock=new Object();//锁 @Override public void run() { // TODO Auto-generated method stub while(true) { if(!TicketRes1.saleTicket()) { break; } } } public synchronized static boolean saleTicket() { if(ticket>=1) { try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"卖第"+ticket+"张票"); ticket--; return true; }else { return false; } } }
4.锁的概念以及死锁问题
多线程在运行的时候可能会遇到这样的问题,多个线程要用到同一个资源,那可能会出现错乱,比如线程要改动资源里的数据,那么多个线程同时改就乱了套了。就像公共厕所,必须要一个一个接着上,不能两个人或者多个人同时上。那么锁这个概念就是像上厕所的门,一个人在上厕所,锁上了门,那下一个人就不能进去了。同样的,如果我们想让某一个程序或者某一个变量只能同时被一个线程运行,就得给程序上锁。所以上了锁,就能保证线程有秩序地运行。
当两个线程相互等待对方释放同步监视器时就会发生死锁,Java虚拟机没有检测,也没有采取措施来处理死锁情况,所以多线程编程时应该采取措施避免死锁出现。一旦出现死锁,程序既不会发生任何异常,也不会给出任何提示,只是所有线程都处于阻塞状态,无法继续。
接下来看一个死锁的Java实例(是不是停不下来了,就相当于循环语句的死循环)
package com.test; public class DeadLock { public static void main(String[] args) { // TODO Auto-generated method stub DeadLockThread he=new DeadLockThread(true,"小明");//他 DeadLockThread she=new DeadLockThread(false,"小华 ");//她 he.start(); she.start(); } } //线程 class DeadLockThread extends Thread{ boolean flag=false; public DeadLockThread() { } protected DeadLockThread(boolean flag,String name) { super(name); this.flag=flag; } public void run() { while(true) { if(flag) { synchronized (Lock.locka) {//他 System.out.println(Thread.currentThread().getName()+"抢到了locka"); } synchronized (Lock.lockb) { System.out.println(Thread.currentThread().getName()+"抢到了lockb"); System.out.println(Thread.currentThread().getName()+"可以上厕所了"); } }else { synchronized (Lock.lockb) {//她 System.out.println(Thread.currentThread().getName()+"抢到了lockb"); } synchronized (Lock.locka) { System.out.println(Thread.currentThread().getName()+"抢到了locka"); System.out.println(Thread.currentThread().getName()+"可以上厕所了"); } } } } } //创建锁对象 class Lock{ public static Object locka=new Object(); public static Object lockb=new Object(); }
5.线程终止
1.使用标志,使run()方法正常执行完毕
package com.test; import java.util.Scanner; public class Demo1 { public static void main(String[] args) { // TODO Auto-generated method stub FlagStop flagStop=new FlagStop("线程1"); flagStop.start(); Scanner input=new Scanner(System.in); System.out.println("输入任意字符结束主线程"); input.next(); flagStop.flag=false;//主线程修改其他线程的变量 System.out.println("主线程结束"); } } class FlagStop extends Thread{ //定义标志 volatile:易挥发的、不稳定的,使用volatile修饰后,获取变量不会从缓存中取,从内存中取。 volatile boolean flag=true; public FlagStop() { // TODO Auto-generated constructor stub } public FlagStop(String name) { super(name); // TODO Auto-generated constructor stub } @Override public void run() { // TODO Auto-generated method stub System.out.println(Thread.currentThread().getName()+"开始执行了"); while(flag) { } System.out.println(Thread.currentThread().getName()+"执行完毕"); } }
2.使用stop方法强制终止线程
package com.test; import java.util.Scanner; public class Demo2 { public static void main(String[] args) { // TODO Auto-generated method stub WordStop wordStop=new WordStop("线程1"); wordStop.start(); Scanner input=new Scanner(System.in); System.out.println("输入任意字符结束主线程"); input.next(); wordStop.stop(); wordStop.flag=false;//主线程修改其他线程的变量 System.out.println("主线程结束"); } } class WordStop extends Thread{ //定义标志 volatile:易挥发的、不稳定的,使用volatile修饰后,获取变量不会从缓存中取,从内存中取。 volatile boolean flag=true; public WordStop() { // TODO Auto-generated constructor stub } public WordStop(String name) { super(name); // TODO Auto-generated constructor stub } @Override public void run() { // TODO Auto-generated method stub System.out.println(Thread.currentThread().getName()+"开始执行了"); while(flag) { } System.out.println(Thread.currentThread().getName()+"执行完毕"); } }
3.使用interrupt方法中断线程
package com.test; import java.io.IOException; public class Demo3 { public static void main(String[] args) throws IOException, InterruptedException { // TODO Auto-generated method stub InterruptStop interruptStop=new InterruptStop(); interruptStop.start(); System.out.println("在10秒之内输入任意符号结束"); System.in.read(); interruptStop.interrupt();//打断正在休眠的线程 interruptStop.join(); System.out.println("主线程结束..."); } } class InterruptStop extends Thread{ @Override public void run() { try { Thread.sleep(10000);//抛出一个异常InterruptedException } catch (InterruptedException e) { // TODO Auto-generated catch block // e.printStackTrace(); System.out.println("执行了catch"); } System.out.println("子线程执行完毕"); } }
6.线程间通信
1.wait:挂起当前线程,释放共享资源锁
2.notify:在所有的wait线程当中随机选择一条唤醒
3.notifyAll:唤醒全部wait线程