标签:
一、进程和线程概念
1、进程:是一个正在执行中的程序。
2、线程:就是进程中的一个独立的控制单元,程序的一条执行路径,一个进程中至少有一个线程。
3、.class编译的时候有一个javac.exe,JVM启动也就是程序运行的时候有一个进程java.exe.java.exe进程中至少一个线程负责java程序的执行这个线程运行的代码存在于main方法中,该线程称之为主线程。其实jvm启动不止一个线程,还有负责垃圾回收机制的线程。
4、线程的运行状态图
二、自定义线程第一种方式的步骤
1、定义类继承Thread。
2、复写Thread类中的run方法,将自定义代码存储在run方法,让线程运行,此处有模板方法设计模式的思想。
3、调用线程的start方法,该方法两个作用:启动线程,线程进入就绪状态,调用run方法。
例1:
class Demo extends Thread{
@Override public void run(){ //或者把run方法写在ThreadDemo1中,ThreadDeom1继承Thread. for(int x=0; x<100; x++) System.out.println("demo run----"+x); } } public class ThreadDemo1 { public static void main(String[] args) { Demo d = new Demo();//创建好一个线程。 d.start();//开启线程并执行该线程的run方法。 //d.run();//仅仅是对象调用方法。而线程创建了,并没有运行。 for(int x=0; x<100; x++) System.out.println("Hello World!--"+x); } }
例2:
public class ThreadDemo extends Thread{ @Override public void run() { for(int i=0;i<100;i++) System.out.println("t1---"+i); } public static void main(String[] args) { ThreadDemo t1 = new ThreadDemo(); t1.start(); for(int i=0;i<100;i++) System.out.println("main---"+i); } }
运行结果分析:
发现运行结果每一次都不同,因为多个线程都获取cpu的执行权,cpu执行到谁,谁就运行,明确一点,在某一个时刻,只能有一个程序在运行(多核除外)。cpu在做着快速的切换,以达到看上去是同时运行的效果,我们可以形象把多线程的运行描述为在互相抢夺cpu的执行权。这就是多线程的一个特性:随机性。谁抢到谁执行,至于执行多长,cpu说了算。
例3:
public class ThreadDemo2 extends Thread{ @Override public void run() { for(int i=0;i<100;i++) System.out.println(getName()+"---"+i); } public static void main(String[] args) { ThreadDemo2 t1 = new ThreadDemo2(); ThreadDemo2 t2 = new ThreadDemo2(); t2.setName("t2"); t1.start(); t2.start(); for(int i=0;i<100;i++) System.out.println("main---"+i); } }
例4:
public class ThreadDemo3 extends Thread{ public ThreadDemo3(String name) { super(name); } @Override public void run() { for(int i=0;i<100;i++) System.out.println((Thread.currentThread()==this)+"-"+currentThread().getName()+"-"+i); } public static void main(String[] args) { ThreadDemo3 t1 = new ThreadDemo3("t1"); ThreadDemo3 t2 = new ThreadDemo3("t2"); t1.start(); t2.start(); for(int i=0;i<100;i++) System.out.println("main---"+i); } }
运行结果分析:
1、线程都有自己默认的名称,Thread-编号,该编号从0开始。
2、setName()或者构造函数,设置线程名称,getName()获取线程名称。
3、static Thread currentThread(),获取当前线程对象。
三、自定义线程的第二种方式
例:简单的卖票程序,多个窗口同时卖票。
class Tick extends Thread{ public Tick(String name) { super(name); } private int num=100; @Override public void run() { while(true) if(num>0) System.out.println(getName()+"--sale--"+num--); } } public class TicketDemo2 { public static void main(String[] args) { Tick t2 = new Tick("t2"); Tick t1 = new Tick("t1"); t2.start(); t1.start(); } }
运行结果分析:
无法实现,没有共享num,因为不是同一个线程对象,各自有各自的堆栈空间,引出自定义线程的第二种方式。
第二种方式步骤:
1、定义类实现Runnable接口
2、覆盖Runnable接口中的run方法,将线程要运行的代码存放在该run方法中。
3、通过Thread类建立线程对象。
4、将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
5、调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
第二种方式解决方案:
class Ticket implements Runnable{ private int num = 100; public void run(){ while(true) if(tick>0) System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--); } } public class TicketDemo{ public static void main(String[] args) { Ticket t = new Ticket();//此处并没有创建线程 Thread t1 = new Thread(t);//创建了一个线程 Thread t2 = new Thread(t);//创建了一个线程 Thread t3 = new Thread(t);//创建了一个线程 t1.start(); t2.start(); t3.start(); } }
运行结果分析:
发现实现了num的共享,虽然也不是同一个线程对象,观察源码发现,调用的都是t对象的run方法,只有一个t对象,所以实现了共享,但是打印出0,-1,-2等错票,多线程的运行出现了安全问题。
问题的原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。
解决办法:
对多条操作共享数据的语句,只能保证里面有一个线程执行,在执行过程中,其他线程不可以参与执行。
Java对于多线程的安全问题提供了专业的解决方式,就是同步代码块。
synchronized(对象){
需要被同步的代码
}
此处的对象就是锁(可以是任意对象),持有锁的线程可以在同步中执行,没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁,同步代码块底层其实有一个标志变量,比如0标志有线程,1标志没有线程,每个线程要进入前先检查标志。
好处:解决了多线程的安全问题。
弊端:多个线程需要判断锁,较为消耗资源,但是必须的,为了安全。
6、同步代码块解决方案:
class Ticket3 implements Runnable{ private int tick = 1000; Object obj = new Object(); public void run(){ while(true){ synchronized(obj){ if(tick>0){ try{Thread.sleep(10);}catch(Exception e){} System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--); } } } } } public class TicketDemo3{ public static void main(String[] args) { Ticket3 t = new Ticket3(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); Thread t3 = new Thread(t); t1.start(); t2.start(); t3.start(); } }
7、面向对象的思想模拟火车站买票,结果做到最后发现,也是生产者消费者问题,有购票人是消费者,售票员是生产者,票是生产的产品,售票的窗口是装产品的容器。生活中大部分这种并发操作都是生产者消费者问题。
8、同步函数实例:
银行有一个金库,有两个储户分别存300元,每次存100,存3次。
目的:该程序是否有安全问题,如果有,如何解决?
同步函数,可以将操作共享数据的代码抽出封装到同步函数中,同步函数默认锁匙this,同步函数和同步代码块可以相互转换。
如何找问题:
1,明确哪些代码是多线程运行代码。
2,明确共享数据。
3,明确多线程运行代码中哪些语句是操作共享数据的。
class Bank{ private int sum; //Object obj = new Object(); public synchronized void add(int n){ //synchronized(obj){ sum = sum + n; try{Thread.sleep(10);}catch(Exception e){} System.out.println("sum="+sum); //} } } class Cus implements Runnable{ private Bank b = new Bank(); public void run(){ for(int x=0; x<3; x++) b.add(100); } } class BankDemo{ public static void main(String[] args) { Cus c = new Cus(); Thread t1 = new Thread(c); Thread t2 = new Thread(c); t1.start(); t2.start(); } }
9、同步的前提:
1、必须要有两个或者两个以上的线程。
2、必须是多个线程使用同一个锁,也就是锁定的是同一个对象。
例:
class Ticket3 implements Runnable{ private int tick = 100; Object obj = new Object(); boolean flag = true; public void run(){ if(flag) while(true){ synchronized(obj){ //此处不是同一锁 if(tick>0){ try{Thread.sleep(10);}catch(Exception e){} System.out.println(Thread.currentThread().getName()+"....code : "+ tick--); } } } else while(true) show(); } public synchronized void show(){//this if(tick>0){ try{Thread.sleep(10);}catch(Exception e){} System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--); } } } public class TicketDemo3{ public static void main(String[] args) { Ticket3 t = new Ticket3(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); t1.start(); try{Thread.sleep(10);}catch(Exception e){} t.flag = false; t2.start(); } }
运行结果分析:
Thread-0....code : 4 Thread-0....code : 3 Thread-1....show.... : 2 Thread-1....show.... : 1 Thread-0....code : 0
结尾处出现0的错误票数,表明还是使用了同步还是存在安全问题,是同步函数和同步代码块没有使用同一个锁,将同步代码块中的锁改为this,则运行结果正常。
10、如果同步函数被静态修饰后,使用的锁是什么呢?
通过验证,发现不在是this。因为静态方法中也不可以定义this。
静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。
静态的同步方法,使用的锁是该方法所在类的字节码文件对象。 类名.class
11、回顾之前单例设计模式
1.饿汉式
class Single{ private static final Single s = new Single(); private Single(){} public static Single getInstance(){ return s; } }
2.懒汉式
class Single{ private static Single s = null; private Single(){} public static Single getInstance(){ if(s==null){ synchronized(Single.class){ if(s==null) //--->A; s = new Single(); } } return s; } }
四、死锁
同步中嵌套同步时,会发生死锁。
例1:
class Test implements Runnable{ private boolean flag; Test(boolean flag){ this.flag = flag; } public void run(){ if(flag){ while(true){ synchronized(MyLock.locka){ System.out.println(Thread.currentThread().getName()+"...if locka "); synchronized(MyLock.lockb){ System.out.println(Thread.currentThread().getName()+"..if lockb"); } } } } else{ while(true){ synchronized(MyLock.lockb){ System.out.println(Thread.currentThread().getName()+"..else lockb"); synchronized(MyLock.locka){ System.out.println(Thread.currentThread().getName()+".....else locka"); } } } } } } class MyLock{ static Object locka = new Object(); static Object lockb = new Object(); } public class TicketDemo3{ public static void main(String[] args) { new Thread(new Test(true)).start(); new Thread(new Test(false)).start(); } }
例2:
class Ticket implements Runnable{ private int tick = 1000; Object obj = new Object(); boolean flag = true; public void run(){ if(flag){ while(true){ synchronized(obj){ show(); } } } else while(true) show(); } public synchronized void show(){//this synchronized(obj){ if(tick>0){ try{Thread.sleep(10);}catch(Exception e){} System.out.println(Thread.currentThread().getName()+"....code : "+ tick--); } } } } class DeadLockDemo{ public static void main(String[] args) { Ticket t = new Ticket(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); t1.start(); try{Thread.sleep(10);}catch(Exception e){} t.flag = false; t2.start(); } }
五、生产者消费者问题
例1:
class Product{ private int id; Product(int id){ this.id=id; } public String toString() { return "Product [id=" + id + "]"; } } class Box{ Product[] p=new Product[10]; int index=0; public synchronized void put(Product pro){ while(index==p.length){ try {wait();} catch (InterruptedException e) {} } notifyAll(); p[index]=pro; index++; } public synchronized Product pop(){ while(index==0){ try {wait();} catch (InterruptedException e) {} } notifyAll(); index--; return p[index]; } } class Producter implements Runnable{ Box box=null; Producter(Box box){ this.box=box; } public void run(){ for(int i=0;i<20;i++){ //每个生产者生产20个 Product pro = new Product(i); box.put(pro); System.out.println(Thread.currentThread().getName()+"生产了:"+pro); } } } class Customer implements Runnable{ Box box=null; Customer(Box box){ this.box=box; } public void run(){ while(true){ Product pro = box.pop(); System.out.println(Thread.currentThread().getName()+"消费了:"+pro); } } } public class ProducerAndCustomer { public static void main(String[] args) { Box box = new Box(); Producter p = new Producter(box); Customer c = new Customer(box); new Thread(p).start(); new Thread(p).start(); new Thread(p).start(); new Thread(c).start(); new Thread(c).start(); new Thread(c).start(); } }
运行结果分析:
wait()函数必须拿到对象锁才能调用,wait()以后这个锁不在归当前线程所持有,sleep()的话还持有锁,notify()叫醒一个正在当前对象锁上等待的线程,wait和notify成对出现。
六、其它函数的使用
1、join
public class TestJoin { public static void main(String args[]){ Runner r = new Runner(); Thread t = new Thread(r); t.start(); try{t.join();}catch(InterruptedException e){} //等待t线程执行完毕再执行主线程,也就是把当前线程合并到主线程 for(int i=0;i<50;i++) System.out.println("主线程:" + i); } } class Runner implements Runnable { public void run() { for(int i=0;i<50;i++) System.out.println("SubThread: " + i); } }
2、线程优先级
public class TestPriority { public static void main(String[] args) { Thread t1 = new Thread(new T1()); Thread t2 = new Thread(new T2()); t1.setPriority(Thread.NORM_PRIORITY + 3); //Thread类有且仅有三个关于线程优先级的字段 t1.start(); t2.start(); } } class T1 implements Runnable { public void run() { for(int i=0; i<1000; i++) { System.out.println("T1: " + i); } } } class T2 implements Runnable { public void run() { for(int i=0; i<1000; i++) { System.out.println("------T2: " + i); } } }
3、yield
public class TestYield { public static void main(String[] args) { MyThread3 t1 = new MyThread3("t1"); MyThread3 t2 = new MyThread3("t2"); t1.start(); t2.start(); } } class MyThread3 extends Thread { MyThread3(String s){ super(s); } public void run(){ for(int i =1;i<=100;i++){ System.out.println(getName()+": "+i); if(i%10==0) yield(); } } }
4、sleep,interrupt
如果线程在调用Object类的wait(),或者该类的join()或sleep()方法过程中受阻,则其中断状态将被清除,它还将收到一个InterruptException。
public class TestInterrupt { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); try {Thread.sleep(10000);} catch (InterruptedException e) {} thread.interrupt(); } } class MyThread extends Thread { boolean flag = true; public void run(){ while(flag){ System.out.println("==="+new Date()+"==="); try { sleep(1000); } catch (InterruptedException e) { System.out.println("xxs"); } } } }
七、JDK5.0对Java并发编程提供了新的实现方式用来取代synchronized。 **掌握**
参考如下:
http://www.cnblogs.com/dolphin0520/p/3923167.html
ps:匿名内部类在多线程代码中使用
class ThreadTest { public static void main(String[] args) { new Thread(){ public void run(){ for(int x=0; x<100; x++) System.out.println(Thread.currentThread().getName()+"....."+x); } }.start(); for(int x=0; x<100; x++) System.out.println(Thread.currentThread().getName()+"....."+x); Runnable r = new Runnable(){ public void run(){ for(int x=0; x<100; x++) System.out.println(Thread.currentThread().getName()+"....."+x); } }; new Thread(r).start(); } }
标签:
原文地址:http://www.cnblogs.com/wangweiNB/p/4784270.html