Java多线程
Posted MINIpower
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java多线程相关的知识,希望对你有一定的参考价值。
两种实现方式继承Thread类或者实现Runnable接口
使用实现Runnable接口和继承Thread类这两种开辟新线程的方法的选择应该优先选择实现Runnable接口这种方式去开辟一个新的线程。因为接口的实现可以实现多个,而类的继承只能是单继承。因此在开辟新线程时能够使用Runnable接口就尽量不要使用从Thread类继承的方式来开辟新的线程。
1 public class mainTest { 2 public static void main(String[] args) { 3 System.out.println("main Start....."); 4 //Thread有Runnable参数的构造方法 5 new Thread(new Runnable() { 6 public void run() { 7 for (int i = 0; i < 10; i++) { 8 System.out.println(Thread.currentThread().getName()+"--->["+i+"]"); 9 } 10 } 11 }).start(); 12 //继承Thread类 13 new Mythread().start(); 14 System.out.println("main End...."); 15 } 16 17 static class Mythread extends Thread{ 18 @Override 19 public void run() { 20 for (int i = 0; i < 10; i++) { 21 System.out.println(Thread.currentThread().getName()+"===>["+i+"]"); 22 } 23 } 24 } 25 }
Thread的常用方法
线程生命周期
线程同步synchronized
方法一:同步代码块
1 synchronized(同步监视器){ 2 需要被同步的代码 3 }
操作的共享数据即为需要被同步的代码
同步监视器又名锁,任意对象都可作为锁,为解决线程安全问题当多个线程操作共享数据时这些线程必须公用一把锁
通过实现Runnable可以用this作为锁,继承Thread类可以用子类的Class作为锁
1 public class mainTest { 2 public static void main(String[] args) { 3 System.out.println("main Start....."); 4 //只有一个票池,作为成员变量的锁也只有一个 5 ticketPool pool = new ticketPool(); 6 Thread t1 = new Thread(pool,"t1"); 7 Thread t2 = new Thread(pool,"t2"); 8 Thread t3 = new Thread(pool,"t3"); 9 t1.start(); 10 t2.start(); 11 t3.start(); 12 13 //有多个票池,共享数据和锁应该用static修饰 14 new ticketPool02().start(); 15 new ticketPool02().start(); 16 new ticketPool02().start(); 17 System.out.println("main End...."); 18 } 19 20 static class ticketPool implements Runnable{ 21 //共享数据 22 private int num = 20; 23 //锁 24 Object obj = new Object(); 25 public void run() { 26 while(true){ 27 synchronized(this){ 28 if (num > 0){ 29 System.out.println(Thread.currentThread().getName()+"--->["+num+"]"); 30 num --; 31 try { 32 Thread.sleep(100); 33 } catch (InterruptedException e) { 34 e.printStackTrace(); 35 } finally { 36 } 37 } 38 } 39 } 40 } 41 } 42 43 static class ticketPool02 extends Thread{ 44 //共享数据 45 private static int num = 20; 46 //锁 47 private static Object obj = new Object(); 48 @Override 49 public void run() { 50 while (true){ 51 synchronized(ticketPool02.class){ 52 if(num > 0){ 53 System.out.println(Thread.currentThread().getName()+"--->["+num+"]"); 54 num --; 55 try { 56 Thread.sleep(100); 57 } catch (InterruptedException e) { 58 e.printStackTrace(); 59 } finally { 60 } 61 } 62 } 63 } 64 } 65 } 66 }
方法二:同步方法
仍然有锁,只是不需要自己显式地声明
非静态同步方法的锁是this
静态同步方法的锁是当前类的Class
1 public class mainTest { 2 public static void main(String[] args) { 3 System.out.println("main Start....."); 4 //只有一个票池,作为成员变量的锁也只有一个 5 ticketPool pool = new ticketPool(); 6 Thread t1 = new Thread(pool,"t1"); 7 Thread t2 = new Thread(pool,"t2"); 8 Thread t3 = new Thread(pool,"t3"); 9 t1.start(); 10 t2.start(); 11 t3.start(); 12 13 //有多个票池,共享数据和锁应该用static修饰 14 new ticketPool02().start(); 15 new ticketPool02().start(); 16 new ticketPool02().start(); 17 System.out.println("main End...."); 18 } 19 20 static class ticketPool implements Runnable{ 21 //共享数据 22 private int num = 20; 23 24 public void run() { 25 while(true){ 26 synMethod(); 27 } 28 } 29 30 //同步方法 31 public synchronized void synMethod(){//锁默认是this 32 if (num > 0){ 33 System.out.println(Thread.currentThread().getName()+"--->["+num+"]"); 34 num --; 35 try { 36 Thread.sleep(100); 37 } catch (InterruptedException e) { 38 e.printStackTrace(); 39 } finally { 40 } 41 } 42 } 43 } 44 45 static class ticketPool02 extends Thread{ 46 //共享数据 47 private static int num = 20; 48 49 @Override 50 public void run() { 51 while (true){ 52 synMethod(); 53 } 54 } 55 56 //因为有static修饰所以锁默认是当前类的Class即ticketPool02.class 57 public static synchronized void synMethod(){ 58 if(num > 0){ 59 System.out.println(Thread.currentThread().getName()+"--->["+num+"]"); 60 num --; 61 try { 62 Thread.sleep(100); 63 } catch (InterruptedException e) { 64 e.printStackTrace(); 65 } finally { 66 } 67 } 68 } 69 } 70 }
解决懒汉式单例模式的线程安全问题
1 @Data 2 class Student{ 3 private String name; 4 private Integer age; 5 private String studentNo; 6 7 private Student(){} 8 private static Student student; 9 /*1 10 public static synchronized Student getInstance(){ 11 if(student == null){ 12 student = new Student(); 13 } 14 return student; 15 }*/ 16 17 /*2、和上面方法1一样,都有效率低的弊端,当前面的线程实例化对象后后面的线程仍要等待获得锁 18 public static Student getInstance(){ 19 synchronized (Student.class) { 20 if(student == null){ 21 student = new Student(); 22 } 23 return student; 24 } 25 }*/ 26 27 //3、效率提高了,前面的线程实例化后,后面的线程就不用排队等锁了 28 public static Student getInstance(){ 29 if(student == null){ 30 synchronized (Student.class) { 31 if(student == null){ 32 student = new Student(); 33 } 34 } 35 } 36 return student; 37 } 38 }
死锁:不同线程分别占用对方线程需要的同步资源不放弃,都在等待对方释放自己需要的同步资源。死锁不会报异常也没有提示,相关线程都将处于阻塞状态,无法继续
开发中尽量减少同步资源的定义
尽量避免嵌套同步
应用专门的算法。
1 public class test02 { 2 public static void main(String[] args) { 3 4 final StringBuffer s1 = new StringBuffer(); 5 final StringBuffer s2 = new StringBuffer(); 6 7 new Thread(){ 8 @Override 9 public void run() { 10 synchronized (s1){ 11 s1.append("A"); 12 try { 13 Thread.sleep(100); 14 } catch (InterruptedException e) { 15 e.printStackTrace(); 16 } 17 synchronized(s2){ 18 s2.append("B"); 19 System.out.println(s1.toString()+" "+s2.toString()); 20 } 21 } 22 } 23 }.start(); 24 25 new Thread(new Runnable() { 26 public void run() { 27 synchronized (s2){ 28 s2.append("F"); 29 try { 30 Thread.sleep(100); 31 } catch (InterruptedException e) { 32 e.printStackTrace(); 33 } 34 synchronized(s1){ 35 s1.append("G"); 36 System.out.println(s1.toString()+" "+s2.toString()); 37 } 38 } 39 } 40 }).start(); 41 } 42 }
Lock接口解决线程安全问题,ReentrantLock
1 public class test02 { 2 public static void main(String[] args) { 3 Runnable myRun = new MyRun(); 4 new Thread(myRun).start(); 5 new Thread(myRun).start(); 6 new Thread(myRun).start(); 7 } 8 9 static class MyRun implements Runnable{ 10 private Integer num = 20; 11 private ReentrantLock lock = new ReentrantLock(); 12 public void run() { 13 while (true){ 14 try{ 15 //调用lock() 16 lock.lock(); 17 if (num > 0){ 18 System.out.println(Thread.currentThread().getName()+"["+num+"]"); 19 num --; 20 } 21 }finally { 22 //解锁 23 lock.unlock(); 24 } 25 } 26 } 27 } 28 }
Lock与synchronized异同
同:都是为了解决线程安全问题
异:synchronized,在执行完相应的同步代码后会自动释放锁
Lock,在执行到需要同步的代码时需要手动的调用lock()和unlock()方法加锁和解锁;Lock只有代码块锁没有方法锁;使用Lock,JVM会用较少的时间调度线程,性能更好;区别于synchronized的隐式锁,Lock是显式锁;Lock有更好的扩展性,提供更多子类。
线程通信(只能用在同步代码块或同步方法里即有synchronized修饰,调用者是锁,这三个方法是定义在Object类里的即任意对象都有这三个方法)
wait();使当前线程进入阻塞态,并释放同步锁给其他线程。
notify();唤醒被wait的一个线程,如果有多个wait的线程则唤醒优先级高的那个线程。
notifyAll();唤醒所有被wait的线程。
1 public class test02 { 2 public static void main(String[] args) { 3 Runnable myRun = new MyRun(); 4 new Thread(myRun).start(); 5 new Thread(myRun).start(); 6 new Thread(myRun).start(); 7 } 8 9 static class MyRun implements Runnable{ 10 private Integer num = 20; 11 private ReentrantLock lock = new ReentrantLock(); 12 public void run() { 13 while (true){ 14 synchronized(this){ 15 notify(); //this.notify(); 调用者是锁,与锁保持一致 16 if (num > 0){ 17 System.out.println(Thread.currentThread().getName()+"["+num+"]"); 18 num --; 19 } 20 try { 21 wait(); //this.wait(); 调用者是锁,与锁保持一致 22 } catch (InterruptedException e) { 23 e.printStackTrace(); 24 } 25 } 26 } 27 } 28 } 29 }
sleep()与wait()的异同:
同:都会使线程进入阻塞态。
异:sleep()是定义在Thread类里的方法,wait()是定义在Object类里的方法
sleep()可以随时调用,wait()只能在同步代码块或同步方法中调用
在同步代码块或同步方法中sleep()不会释放同步锁,wait()会释放同步锁。
生产者消费者模式
1 public class test02 { 2 public static void main(String[] args) { 3 SyncStack stack = new SyncStack(); 4 Runnable p=new Producer(stack); 5 Runnable c = new Consumer(stack); 6 Thread p1 = new Thread(p); 7 Thread c1 = new Thread(c); 8 9 p1.start(); 10 c1.start(); 11 12 } 13 } 14 15 class SyncStack{ //支持多线程同步操作的堆栈的实现 16 private int index = 0; 17 private char []data = new char[6]; 18 public synchronized void push(char c){ 19 if(index == data.length){ 20 try{ 21 this.wait(); 22 }catch(InterruptedException e){} 23 } 24 this.notify(); 25 data[index] = c; 26 index++; 27 } 28 public synchronized char pop(){ 29 if(index ==0){ 30 try{ 31 this.wait(); 32 }catch(InterruptedException e){} 33 } 34 this.notify(); 35 index--; 36 return data[index]; 37 } 38 } 39 40 41 class Producer implements Runnable{ 42 SyncStack stack; 43 public Producer(SyncStack s){ 44 stack = s; 45 } 46 public void run(){ 47 for(int i=0; i<20; i++){ 48 char c =(char)(Math.random()*26+\'A\'); 49 stack.push(c); 50 System.out.println("produced:"+c); 51 try{ 52 Thread.sleep((int)(Math.random()*1000)); 53 }catch(InterruptedException e){ 54 } 55 } 56 } 57 } 58 59 60 class Consumer implements Runnable{ 61 SyncStack stack; 62 public Consumer(SyncStack s){ 63 stack = s; 64 } 65 public void run(){ 66 for(int i=0;i<20;i++){ 67 char c = stack.pop(); 68 System.out.println("消费:"+c); 69 try{ 70 Thread.sleep((int)(Math.random()*1000)); 71 }catch(InterruptedException e){ 72 } 73 } 74 } 75 }
JDK5提供了创建线程的新方法
1、实现Callable接口,其中的call()方法类似Runnable接口里的run()方法,call()方法可以返回值、抛异常、支持泛型。
1 public class test02 { 2 public static void main(String[] args) { 3 4 Callable call = new MyCall(); 5 FutureTask future = new FutureTask(call); 6 //FutureTask继承了Runnable可以作为Thread()的参数 7 Thread thread = new Thread(future); 8 thread.start(); 9 10 tryJava多线程与并发库高级应用-工具类介绍