java多线程
一:买票的案例:通过Thread 和Runale实现多线程
//通过Thread实现 package mypack; class Ticket extends Thread{ private static int ticket=100; public void run() { // TODO Auto-generated method stub while(true){ if(ticket>0){ System.out.println(Thread.currentThread().getName()+"...sale..."+ticket--); }else{ break; } } } } public class TicketDemo { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Ticket t1 = new Ticket(); Ticket t2 = new Ticket(); t1.start(); t2.start(); } } //通过继承Runnable实现 package mypack; class Ticket implements Runnable{ private int ticket = 100; public void run() { // TODO Auto-generated method stub while(true){ if(ticket>0){ try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"...sale..."+ticket--); }else{ break; } } } } public class TicketDemo2 { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Ticket t = new Ticket(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); t1.start(); t2.start(); } } //上面两种方法功能一样,不过Thread
二:多线程异常
package mypack; class Ticket extends Thread{ private static int ticket=100; public void run() { // TODO Auto-generated method stub while(true){ if(ticket>0){ try { Thread.sleep(100); //睡眠,转让执行权 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"...sale..."+ticket--); }else{ break; } } } } public class TicketDemo { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Ticket t1 = new Ticket(); Ticket t2 = new Ticket(); t1.start(); t2.start(); } }
//上面就是让线程睡眠,然后在苏醒时候,别的进程已经改变了原变量的内容
多线程运行安全问题:
问题原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没执行完,另一个线程参与进来执行,导致共享数据的错误.
解决办法;对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行.
解决多线程问题
synchronize(对象){
}
通过同步代码块解决线程安全问题
解决多线程问题:
1,明确那些代码是多线程运行代码.
2,明确共享数据.
3,明确多线程运行代码中那些语句是操作共享数据.
package mypack; class Ticket extends Thread { Object obj = new Object();//创建上帝对象 private static int ticket = 100; public void run() { // TODO Auto-generated method stub while (true) { synchronized (obj) { if (ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "...sale..." + ticket--); } else { break; } } } } } public class TicketDemo { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Ticket t1 = new Ticket(); Ticket t2 = new Ticket(); t1.start(); t2.start(); } }
通过同步函数解决安全问题
package mypack; class Bank { private int sum; Object obj = new Object(); // 同步函数 public synchronized void add(int n) { sum = sum + n; System.out.println("sum = " + sum); } } class Cus implements Runnable { private Bank b = new Bank(); public void run() { // TODO Auto-generated method stub for (int i = 0; i < 3; i++) { b.add(100); } } } public class BankDemo { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Cus c = new Cus(); Thread t1 = new Thread(c); Thread t2 = new Thread(c); t1.start(); //每个线程和每个线程的run方法是分开的,run方法是独立运行的, t2.start(); } }
同步函数用的锁是那一个?
函数需要被对象调用,那么函数都有一个所属对象引用,就是this.
所以同步函数使用的锁是shis.
同步函数和同步代码块共同操作共享数据
package mypack; class Ticket implements Runnable { private int ticket = 100; Object obj = new Object(); boolean flag = true; public void run() { // TODO Auto-generated method stub if (flag) { while (true) { synchronized (this) { //这里要确保操作共享数据使用的synchronized 对象是一样 if (ticket > 0) { try { Thread.sleep(10); } catch (InterruptedException e) { } System.out.println(Thread.currentThread().getName() + "...code..." + ticket--); } else { return; } } } } else { while (true) { show(); } } } public synchronized void show() { if (ticket > 0) { try { Thread.sleep(10); } catch (InterruptedException e) { } System.out.println(Thread.currentThread().getName() + "...show..." + ticket--); } else { return; } } } public class TicketDemo2 { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Ticket t = new Ticket(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); t1.start(); //当启动main函数时候,相当于有3个线程,t1,t2,main t1.start()时候 可能拥有执行权但是还没有执行,一闪而过,t.flag=false执行完了, //所以要睡眠一下 try{Thread.sleep(10);}catch(Exception e){}; t.flag = false; t2.start(); } }
静态同步函数的锁是Class
package mypack; /** * 静态进内存,内存中没有本类对象,但是一定有该类对相应的字节码文件对象 * 类名.class 该对象的类型是class * * 静态的同步方法,使用的锁是该方法所在类的字节码文件对象. 类名.class */ class Ticket implements Runnable { //由于静态synchronized里面调用ticket,所以ticket要设置成静态 private static int ticket = 100; Object obj = new Object(); boolean flag = true; public void run() { // TODO Auto-generated method stub if (flag) { while (true) { synchronized (Ticket.class) { //这里要确保操作共享数据使用的synchronized 对象是一样 if (ticket > 0) { try { Thread.sleep(10); } catch (InterruptedException e) { } System.out.println(Thread.currentThread().getName() + "...code..." + ticket--); } else { return; } } } } else { while (true) { show(); } } } public static synchronized void show() { if (ticket > 0) { try { Thread.sleep(10); } catch (InterruptedException e) { } System.out.println(Thread.currentThread().getName() + "...show..." + ticket--); } else { return; } } } public class TicketDemo2 { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Ticket t = new Ticket(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); t1.start(); //当启动main函数时候,相当于有3个线程,t1,t2,main t1.start()时候 可能拥有执行权但是还没有执行,一闪而过,t.flag=false执行完了, //所以要睡眠一下 try{Thread.sleep(10);}catch(Exception e){}; t.flag = false; t2.start(); } }
懒汉式上锁的应用
class lanhanshi{ private lanhanshi(){} private static lanhanshi instance; //如果锁放在这里的话,每次都要去判断,比较低效 public static synchronized lanhanshi getSingleInstance(){ if(instance == null){ //如果在多线程中这里可能线程挂住,A挂住,B进来把下面执行,A又执行 //要保证对象的唯一性,加锁. instance = new lanhanshi(); } return instance; } } class ehanshi{ //注意这里要用final修饰,变成常量,不可修改, private static final ehanshi instance = new ehanshi(); private ehanshi(){} public ehanshi getSingleInstance(){ return instance; } }
优化懒汉式锁
class lanhanshi { private lanhanshi() { } private static lanhanshi instance; // 如果锁放在这里的话,每次都要去判断,比较低效 public static lanhanshi getSingleInstance() { if (instance == null) { synchronized (lanhanshi.class) { if (instance == null) { // 如果在多线程中这里可能线程挂住,A挂住,B进来把下面执行,A又执行 // 要保证对象的唯一性,加锁. instance = new lanhanshi(); } } } return instance; } }
懒汉式和饿汉式的差别,懒汉式是延迟加载,懒汉式有问题吗? 有.多线程访问时会出现安全问题,可以加同步,用同步代码块,和同步函数都可以,有些低效,用双重判断可以解决效率问题
package mypack; class Tick implements Runnable { int ticket = 100; public void run() { // TODO Auto-generated method stub while (true) { if (ticket > 0) { System.out.println(Thread.currentThread().getName() + "...A..." + ticket--); } } } } public class Test2 { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub //这2个tick操作的是不同的对象,ticket 不是共享 Tick tick1 = new Tick(); Tick tick2 = new Tick(); Thread t1 = new Thread(tick1); Thread t2 = new Thread(tick2); t1.start(); t2.start(); } }
写一个死锁程序
package mypack; class Test implements Runnable{ private boolean flag; Test(boolean flag){ this.flag = flag; } public void run() { // TODO Auto-generated method stub if(flag){ synchronized (MyLock.locka) { System.out.println("if locka"); synchronized (MyLock.lockb) { System.out.println("if lockb"); } } }else{ synchronized (MyLock.lockb) { System.out.println("else lockb"); synchronized (MyLock.locka) { System.out.println("else locka"); } } } } } //线程运行时候用的锁 class MyLock{ static Object locka = new Object(); static Object lockb = new Object(); } public class DeadLockDemo { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Test test1 = new Test(true); Test test2 = new Test(false); Thread t1 = new Thread(test1); Thread t2 = new Thread(test2); t1.start(); t2.start(); } }
解决多线程安全问题事例
package pack; /** * @Description TODO * @author WiKi * @date 2014-11-30 上午8:20:38 */ class Input implements Runnable { Res r; Input(Res r) { this.r = r; } public void run() { // TODO Auto-generated method stub int x = 0; while (true) { synchronized (r) { if (x == 0) { r.name = "男人"; r.sex = "boy"; } else { r.name = "女人"; r.sex = "girl"; } x = (x + 1) % 2; } } } } class Output implements Runnable { Res r; Output(Res r) { this.r = r; } public void run() { // TODO Auto-generated method stub while (true) { synchronized (r) { System.out.println(r.name + "::" + r.sex); } } } } class Res { String name; String sex; } public class Demo { public static void main(String[] args) { Res r = new Res(); Input input = new Input(r); Output output = new Output(r); Thread t1 = new Thread(input); Thread t2 = new Thread(output); t1.start(); t2.start(); } }
等待唤醒机制
只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒.
package pack; /** * @Description TODO * @author WiKi * @date 2014-11-30 上午8:20:38 */ class Input implements Runnable { Res r; Input(Res r) { this.r = r; } public void run() { // TODO Auto-generated method stub int x = 0; while (true) { synchronized (r) { if(r.flag) try { r.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (x == 0) { r.name = "男人"; r.sex = "boy"; } else { r.name = "女人"; r.sex = "girl"; } x = (x + 1) % 2; r.flag = true; r.notify(); } } } } class Output implements Runnable { Res r; Output(Res r) { this.r = r; } public void run() { // TODO Auto-generated method stub while (true) { synchronized (r) { if(!r.flag) try { r.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(r.name + "::" + r.sex); r.flag = false; r.notify(); } } } } class Res { String name; String sex; boolean flag; } public class Demo { public static void main(String[] args) { Res r = new Res(); Input input = new Input(r); Output output = new Output(r); Thread t1 = new Thread(input); Thread t2 = new Thread(output); t1.start(); t2.start(); } }
只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒.
等待唤醒机制代码优化
package pack; /** * @Description TODO * @author WiKi * @date 2014-11-30 上午8:20:38 */ class Input implements Runnable { Res r; Input(Res r) { this.r = r; } public void run() { // TODO Auto-generated method stub int x = 0; while (true) { if (x == 0) { r.set("男人", "man"); } else { r.set("女人", "girl"); } x = (x + 1) % 2; } } } class Output implements Runnable { Res r; Output(Res r) { this.r = r; } public void run() { // TODO Auto-generated method stub while (true) { r.get(); } } } class Res { String name; String sex; boolean flag; public synchronized void set(String name, String sex) { if (flag) try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } this.name = name; this.sex = sex; flag = true; this.notify(); } public synchronized void get() { if (!flag) try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(name + "::" + sex); flag = false; this.notify(); } } public class Demo { public static void main(String[] args) { Res r = new Res(); Input input = new Input(r); Output output = new Output(r); Thread t1 = new Thread(input); Thread t2 = new Thread(output); t1.start(); t2.start(); } }
notifyAll while修饰
package pack; class Resource{ private String name; private int count = 1; private boolean flag = false; public synchronized void set(String name){ if(flag) //加了while会造成程序死掉 try{this.wait();}catch(Exception e){}; this.name = name+count++; System.out.println(Thread.currentThread().getName()+"...生产者"+this.name); flag = true; this.notifyAll(); //普通的notify可能唤醒本方线程,没唤醒对方线程.唤醒了本方while修饰的直接死掉 } public synchronized void out(){ if(!flag) try{this.wait();}catch(Exception e){}; System.out.println(Thread.currentThread().getName()+"...消费者..."+name); flag = false; this.notifyAll(); } } class Producer implements Runnable{ Resource res; Producer(Resource res){ this.res = res; } public void run() { // TODO Auto-generated method stub while(true) res.set("+商品+"); } } class Consumer implements Runnable{ Resource res; Consumer(Resource res){ this.res = res; } public void run() { // TODO Auto-generated method stub while(true) res.out(); } } public class Demo { public static void main(String[] args) { Resource res = new Resource(); Producer p = new Producer(res); Consumer c = new Consumer(res); Thread t1 = new Thread(p); Thread t2 = new Thread(p); Thread t3 = new Thread(c); Thread t4 = new Thread(c); t1.start(); t2.start(); t3.start(); t4.start(); } }
lock新特性替换上面方法
package pack; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class Resource { private String name; private int count = 1; private boolean flag = false; Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); public void set(String name) { lock.lock(); try { while (flag) // 加了while会造成程序死掉 condition.await(); this.name = name + count++; System.out.println(Thread.currentThread().getName() + "...生产者" + this.name); flag = true; condition.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void out() { lock.lock(); try { while (!flag) condition.await(); System.out.println(Thread.currentThread().getName() + "...消费者..." + name); flag = false; condition.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } } class Producer implements Runnable { Resource res; Producer(Resource res) { this.res = res; } public void run() { // TODO Auto-generated method stub while (true) res.set("+商品+"); } } class Consumer implements Runnable { Resource res; Consumer(Resource res) { this.res = res; } public void run() { // TODO Auto-generated method stub while (true) res.out(); } } public class Demo { public static void main(String[] args) { Resource res = new Resource(); Producer p = new Producer(res); Consumer c = new Consumer(res); Thread t1 = new Thread(p); Thread t2 = new Thread(p); Thread t3 = new Thread(c); Thread t4 = new Thread(c); t1.start(); t2.start(); t3.start(); t4.start(); } }
lock锁的优化 生产者消费者,线程间通信
package pack; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 这个flag判断语句依然要设置成while判断 * 如果设置成if t1 t2 线程是生产者 t3 t4 线程是消费者 * t1 t2 失去执行权 t3执行,然后producer_con.signal(); 这时t4 获取线程运行权,但是flag是false,t4睡眠 * t1 t2 由于一开始就执行过 不需要判断if语句,直接执行,,这时候 就会造成 生产2次 * @Description TODO * @author WiKi * @date 2014-11-30 下午2:07:41 */ class Resource { private String name; private int count = 1; private boolean flag = false; Lock lock = new ReentrantLock(); Condition producer_con = lock.newCondition(); Condition consumer_con = lock.newCondition(); public void set(String name) throws InterruptedException { lock.lock(); try { while (flag) // 加了while会造成程序死掉 producer_con.await(); this.name = name + count++; System.out.println(Thread.currentThread().getName() + "...生产者" + this.name); flag = true; consumer_con.signal(); } finally { lock.unlock(); } } public void out() throws InterruptedException { lock.lock(); try { while (!flag) consumer_con.await(); System.out.println(Thread.currentThread().getName() + "...消费者..." + name); flag = false; producer_con.signal(); } finally { lock.unlock(); } } } class Producer implements Runnable { Resource res; Producer(Resource res) { this.res = res; } public void run() { // TODO Auto-generated method stub while (true) try { res.set("+商品+"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } class Consumer implements Runnable { Resource res; Consumer(Resource res) { this.res = res; } public void run() { // TODO Auto-generated method stub while (true) try { res.out(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public class Demo { public static void main(String[] args) { Resource res = new Resource(); Producer p = new Producer(res); Consumer c = new Consumer(res); Thread t1 = new Thread(p); Thread t2 = new Thread(p); Thread t3 = new Thread(c); Thread t4 = new Thread(c); t1.start(); t2.start(); t3.start(); t4.start(); } }
多线程控制线程循环:就是控制while()的标记
package pack; /** * 控制线程循环 */ class StopThread implements Runnable{ private boolean flag = true; public void run() { // TODO Auto-generated method stub while(flag){ System.out.println("wiki"); } } public void changeflag(){ flag = !flag; } } public class Demo{ public static void main(String[] args) { StopThread st = new StopThread(); Thread t1 = new Thread(st); Thread t2 = new Thread(st); t1.start(); t2.start(); int count = 0; while(true){ if(count++ == 60){ st.changeflag(); break; } System.out.println(Thread.currentThread().getName()+"::"+count); } } }
停止冻结状态的线程
wait notify notifyAll 这些只能存在于synchronized 代码块里面,如果出现在其他地方会报 IllegalMonitorStateException 异常
如何停止线程?
只有一种,run方法结束.
开启多线程运行,运行代码通常是循环结构.
只要控制住循环,就可以让run方法结束,也就是线程结束.
特殊情况:
当线程处于了冻结状态, wait状态,
就不会读取到标记,那么线程就不会结束.
interrupt 中断线程. 将处于冻结状态的线程,强制恢复到运行状态,
当没有指定的方式让冻结的线程恢复到运行状态,这时需要对冻结进行清除.
强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束.
package pack; /** * 控制线程循环 */ class StopThread implements Runnable{ private boolean flag = true; public void run() { // TODO Auto-generated method stub while(flag){ try { System.out.println("11111111"); wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block System.out.println(Thread.currentThread().getName()+"....Exception"); } System.out.println(Thread.currentThread().getName()+"...run"); } } public void changeflag(){ flag = false; } } public class Demo{ public static void main(String[] args) { StopThread st = new StopThread(); Thread t1 = new Thread(st); Thread t2 = new Thread(st); t1.start(); t2.start(); int count = 0; while(true){ if(count++ == 60){ st.changeflag(); t1.interrupt(); t2.interrupt(); break; } System.out.println(Thread.currentThread().getName()+"::"+count); } System.out.println("over"); } }
后台线程:,处于后台运行,任务是为其他线程提供服务
。也称为“守护线程”或“精灵线程”。JVM的垃圾回收就是典型的后台线程。
特点:若所有的前台线程都死亡,后台线程自动死亡。
设置后台线程:Thread对象setDaemon(true);
setDaemon(true)必须在start()调用前。否则出现IllegalThreadStateException异常;
前台线程创建的线程默认是前台线程;
判断是否是后台线程:使用Thread对象的isDaemon()方法;
并且当且仅当创建线程是后台线程时,新线程才是后台线程。
创建一个后台线程
package pack; class Ticket implements Runnable{ public void run() { // TODO Auto-generated method stub while(true){ System.out.println(Thread.currentThread().getName()); } } } public class Demo { public static void main(String[] args) { Ticket ticket = new Ticket(); Thread t1 = new Thread(ticket); Thread t2 = new Thread(ticket); System.out.println("main thread"); t1.setDaemon(true); t1.start(); t2.start(); //如果t2不运行的话,t1会运行几次,发现main线程 结束了,他也就结束了,不过有个时间间隔 //t2运行的话, t1和t2会交互执行 } }
join 等待线程结束/死亡
package pack; class JoinDemo implements Runnable { public void run() { // TODO Auto-generated method stub for (int i = 0; i < 70; i++) { System.out.println(Thread.currentThread().getName() + "...." + i); } } } public class Demo { public static void main(String[] args) throws InterruptedException { JoinDemo jd = new JoinDemo(); Thread t1 = new Thread(jd); Thread t2 = new Thread(jd); t1.start(); // t1.join(); 当join放在这里,当执行到这里,main释放执行权,然后让t1线程死掉,main才恢复,才会继续执行后面 t2.start(); // t1.join(); 当join放这里,执行到这里,main释放执行权,t1,t2,2线程抢执行权,只有在t1执行完,main才恢复执行 //这是如果t2还没执行完, main和t2抢执行权 for (int i = 0; i < 60; i++) { System.out.println(Thread.currentThread().getName() + "...main..." + "..." + i); } System.out.println("over"); } }
线程的优先级
static int MAX_PRIORITY 线程可以具有的最高优先级。 10
static int MIN_PRIORITY 线程可以具有的最低优先级。 1
static int NORM_PRIORITY 分配给线程的默认优先级。 5
//t1.setPriority(Thread.MIN_PRIORITY);
yield用法:yield 暂停当前正在执行的线程对象,并执行其他线程。这个只是稍微减缓线程运行,可能那个线程还是自身
package pack; class Ticket implements Runnable{ public void run() { // TODO Auto-generated method stub for(int i=0;i<30;i++){ System.out.println(Thread.currentThread().toString()); Thread.yield(); } } } public class Demo { public static void main(String[] args) throws InterruptedException { Ticket ticket = new Ticket(); Thread t1 = new Thread(ticket); Thread t2 = new Thread(ticket); // t1.setPriority(Thread.MIN_PRIORITY); t1.start(); t2.start(); System.out.println("over"); } }
多线程的运用
package pack; public class Demo { public static void main(String[] args) { new Thread() { public void run() { for (int i = 0; i < 30; i++) { System.out.println(Thread.currentThread().getName() + "..." + i); } }; }.start(); Runnable r = new Runnable() { public void run() { for (int i = 0; i < 30; i++) { System.out.println(Thread.currentThread().getName() + "..." + i); } } }; new Thread(r).start(); for (int i = 0; i < 30; i++) { System.out.println(Thread.currentThread().getName() + "..." + i); } } }