java 多线程详解
来源:互联网 发布:ubuntu添加vga 788 编辑:程序博客网 时间:2024/06/03 17:07
关于线程的概念
1、程序一般是指一段静态代码,静态对象;进程是程序的一次执行过程或正在运行的程序动态的的过程,有它的产生、存在和消亡的过程;线程是指一个程序内部的一条执行路径,也就是说每一个任务就代表一个线程。进程与线程相互联系然而又有区别,从本质上来说每个进程都有自己的一整套变量,而线程间可以共享变量,共享变量使得线程之间的通信比进行之间通信更有效、更容易。
2、并不是每种情况都需要多线程,当程序需要同时执行两个或多个任务;当程序需要实现一些等待任务,如用户的输入,文件读写,网络操作,搜索;当程序需要运行一些后台程序。在这些情况下我们需要使用多线程。
3、线程的生命周期:每个线程都有新建、就绪、运行、阻塞、死亡这五种状态
创建线程的两种方式
1、继承Thread类:
a.创建一个类继承Thread;b.重写run()方法,将要执行的业务逻辑代码放在run()方法体中;c.在主类中创建子线程类的实例;d.调用线程方法start(),将此线程变为就绪状态。
package com.thread;public class TestThread {public static void main(String [] args){ //3、创建一个子类对象SubThread st=new SubThread();//4、调用线程的start()方法,开始启动线程;调用相应的run()方法st.start();//这个是主线程的方法 for(int i=1;i<=100;i++){System.out.println(Thread.currentThread().getName()+":"+i);}}}//1、创建一个继承Thread的子类class SubThread extends Thread{//2、重写run()方法@Overridepublic void run() { for(int i=1;i<=100;i=i+2){ System.out.println(Thread.currentThread().getName()+":"+i); }}}
2、实现Runnable接口
a.创建一个类实现Runnable接口;b.重写run()方法,将业务逻辑代码放入在run()方法中;c.创建一个Runbable接口实现类的对象的实例 d.将这个对象作为形参传递给Thread类的构造器中,创建Thread类对象,然后再启动线程start()方法。
package com.thread;public class ThreadTest { public static void main(String[] args) throws Exception{ MyRunnable r=new MyRunnable();//c.创建一个Runbable接口实现类的对象的实例 Thread t=new Thread(r);//d.将这个对象作为形参传递给Thread类的构造器中,创建Thread类对象,然后再启动线程start()方法。 t.setName("自定义线程:"); t.start(); System.out.println(Thread.currentThread().getName());}}class MyRunnable implements Runnable{//a.创建一个类实现Runnable接口@Overridepublic void run() {//b.重写run()方法,将业务逻辑代码放入在run()方法中 for(int i=0;i<10;i++){ System.out.println(Thread.currentThread().getName()+i); }}}
3、这两种方式创建线程的异同在于a.Thread类其实也是实现Runnable接口;b.实现Runnable接口的方式要优于继承Thread类,首先实现的方式避免java中单继承的缺点,其次如果多个线程操作同一份数据资源,更加适合用实现的方式
线程中Thread类常用的方法
1、start():启动线程并执行相应的run方法
2、Thread.currentThread() Thread.currentThread().getName()、Thread.currentThread().setName("设置的name") 获取当前线程/获取当前线程的名字/设置当前线程的名字
3、sleep(long time):显式的让线程休眠,其中time以毫秒为单位
4、yield():让调用这个方法的线程释放当前cpu的执行权利
5、线程通信的方法:wait():另当前线程挂起并放弃cpu、同步资源使别的线程可以访问并修改共享资源,而当前线程排队等待再一前次对资源的访问;
notify():唤醒正在排队等待同步资源的线程中优先级别最高者结束等待;
notifyAll():唤醒所有正在排队等待资源的线程结束等待。
6、join():在A线程中调用B线程的Join()方法,表示强制执行B线程,这个时候A线程停止执行,直到B执行完毕A才可以继续执行。
线程的安全问题
一、线程安全问题的原因:由于一个线程正在操作共享数据,未执行完毕的情况下,另外一个线程参与进来,导致共享数据在安全上出了问题。如银行的转账存款业务,卖票业务等等。
二、解决线程安全的办法
1、同步块synchronized:将需要同步的业务逻辑代码放在synchronized(this){ ...}这个同步块中
2、同步方法synchronized:将需要同步的方法加上关键字:synchronized如publicsynchronized void set(String name){...}
package com.thread;public class Demo {public static void main(String[] args) {sell_Window sell=new sell_Window();Thread t1=new Thread(sell);//开启三个窗口同时卖票Thread t2=new Thread(sell);Thread t3=new Thread(sell);t1.start();t2.start();t3.start();}}class sell_Window implements Runnable{//实现Runnable接口,把买票的业务逻辑放在run()方法中 int num=100; @Override public void run(){ while(true){ synchronized(this) {//利用同步块来实现线程安全,this表示当前类,我们也可以使用一个对象来做锁对象,其中利用</span><span style="font-size:12px;">Demo.class字节码也是比较好的 if(num>0){ try{Thread.sleep(100); System.out.println("窗口 "+Thread.currentThread().getName()+":售第"+(num--)+"张票"); }catch (Exception e) {e.printStackTrace(); } }else{ break; } } } }}
2、同步方法synchronized:将需要同步的方法加上关键字:synchronized如publicsynchronized void set(String name){...}
package com.thread;public class Demo {public static void main(String[] args) {sell_Window sell=new sell_Window(new SellTicket());Thread t1=new Thread(sell);//开启三个窗口同时卖票Thread t2=new Thread(sell);Thread t3=new Thread(sell);t1.start();t2.start();t3.start();}}class SellTicket{//共享数据区,以及买票的方法int num=100;public synchronized void sell(){//同步方法if(num>0){System.out.println("窗口 "+Thread.currentThread().getName()+":售第"+(num--)+"张票");}else{Thread.currentThread().stop();}}}class sell_Window implements Runnable{//实现Runnable接口,把买票的业务逻辑放在run()方法中SellTicket sellTicket;public sell_Window(SellTicket sellTicket) {this.sellTicket=sellTicket;}@Overridepublic void run(){ while(true){ try{ Thread.sleep(100); sellTicket.sell(); }catch (Exception e) {e.printStackTrace();} }}}
3、锁Lock,在线程同步中的Lock其实功能上和synchronized有着异曲同工的效果,不过Lock可以提供更加细度的控制,在java中使用Lock锁的方式如下:
设计一个简易的cache系统:
package com.thread;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;/*线程同步中的锁Lock */public class ThreadLock {public static void main(String[] args) { new ThreadLock().init();}public void init(){ final Outputer outputer=new Outputer(); new Thread(new Runnable() {//匿名线程 @Overridepublic void run() {//run()方法 while(true){ try { Thread.sleep(10); outputer.output("zhangxiaoxiang");//执行的任务 } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); new Thread(new Runnable() { @Override public void run() { while(true){ try { Thread.sleep(10); outputer.output("admin"); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } static class Outputer{ Lock lock=new ReentrantLock();//获取锁 public void output(String name) { int len=name.length(); lock.lock();//加锁 try{ for(int i=0;i<len;i++){ System.out.print(name.charAt(i)); } System.out.println(); }catch(Exception e){ e.printStackTrace(); }finally{ lock.unlock();//释放锁 } } } }在Lock中存在比较有用的两种锁,读写锁(ReadWriteLock),使用这样的锁可以保证读文件的时候可以并发,读写和写文件不可以并发。下面用一个比较实用的例子来说明读写锁的优点。
设计一个简易的cache系统:
package com.thread;import java.util.HashMap;import java.util.Map;import java.util.concurrent.locks.ReadWriteLock;import java.util.concurrent.locks.ReentrantReadWriteLock;/*设置缓存系统: * 多个人进行读取数据时,可以并行,所以这时候只需要读锁;当有人发现没有数据时,这时候需释放掉 * 读锁加上一个写锁,在写的时候不能被读取,写完之后就还原成读锁让其它人可以读取 * */public class Thread_CacheDemo {private Map<String,Object> cache=new HashMap<String,Object>();public static void main(String[] args) {}//得到数据private ReadWriteLock rwl=new ReentrantReadWriteLock();public Object getData(String key){//多线程读取时rwl.readLock().lock();//读锁Object value=null;try{value=cache.get(key);if(value == null){rwl.readLock().unlock();//释放读锁rwl.writeLock().lock();//写锁try { if(value == null){ value="aaaa";//这里实际上是查询数据库 }} catch (Exception e) {}finally{rwl.writeLock().unlock();//释放写锁rwl.readLock().lock();//加上读锁}}}catch (Exception e) {e.printStackTrace();}finally{rwl.readLock().unlock();//释放读锁} return value;}}
线程间通信
1、线程通信的概念:线程通信是指线程间相互交流的过程,一个线程可以释放自己的cpu权利来给其它被唤醒的线程使用。
2、线程通信的方法,线程通信主要用到了wait(),notif(),notifyAll(),线程通信的一个经典案例就是生产者和消费者调度算法。下面是生产者和消费者调度算法的实现过程:
package com.thread;/*生产者与消费者的问题 * 生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品 * ,店员一次只能持有固定数量的产品(比如20),如果生产者试图生 产更多的产品,店员会叫生产者停一下, * 如果店中有空位放产品了再通知生产者继续生产;如果店中没产品了,店员会告诉消费者等一下,如果店中有了产品再 * 通知消费者。 * 1、多线程:生产者,消费者 * 2、共享数据(资源):产品的数量 * 3、需要线程安全 * 4、需要线程通信:生产者与消费者通信 * */public class Produce_consume {public static void main(String[] args) {Clerk clerk=new Clerk();//共享数据Producer p=new Producer(clerk);//生产者Consumer c=new Consumer(clerk);//消费者Thread t1=new Thread(p);//生产者线程Thread t3=new Thread(p);//生产者线程Thread t2=new Thread(c);//消费者线程t1.setName("生产者1");t2.setName("消费者");t3.setName("生产者2");t1.start();t2.start();t3.start();}}//生产者class Producer implements Runnable{Clerk clerk;public Producer(Clerk clerk) {this.clerk=clerk;}@Overridepublic void run() {System.out.println("生产者开始生产产品");while(true){ try{Thread.currentThread().sleep(10); clerk.addProduct();//生产者生产产品 } catch (InterruptedException e) { e.printStackTrace(); } } } }//消费者class Consumer implements Runnable{ Clerk clerk; public Consumer(Clerk clerk){ this.clerk=clerk; } @Override public void run(){ System.out.println("消费者消费产品"); while(true){ try { Thread.currentThread().sleep(10); clerk.consumeProduct(); //消费者消费产品 } catch(InterruptedException e) { e.printStackTrace(); } } }}//店员,这是一个共享的数据,供生产者和消费者使用class Clerk{ int product; public synchronized void addProduct(){ //生产产品 if(product>=20){ try{ //产品数大于或等于20,暂停生产 wait();//释放cpu资源 } catch (InterruptedException e){ e.printStackTrace(); } }else{ product++; System.out.println(Thread.currentThread().getName()+":生产了第"+product+"个产品"); notifyAll();//一旦有产品生产,去唤醒消费者进行消费,notifyAll()唤醒其它线程 } } public synchronized void consumeProduct(){ //消费产品 if(product<=0){ try { wait();//产品数少于或等于0,停止消费 } catch (InterruptedException e) { e.printStackTrace(); } }else{ System.out.println(Thread.currentThread().getName()+":消费了第"+product+"个产品"); product--; notifyAll();//有消费,就去唤醒生产者进行生产 } }}
线程范围的共享数据ThreadLocal类
1、ThreadLocal类表示可以存放当前线程的变量,同时这个变量可以让这个线程中的所有对象共享。这个类的底层实现方式和HashMap()大致相同,它分别有get(),set(),remove(),setInitialValue()四个方法,这四个方法分别表示得到当前线程相关的变量、设置当前线程的相关变量、把与当前线程有关的变量全部remve、设置初始化的值。当我们遇到需要在一个线程中放置多个变量的情况时,可以把这些变量封装成一个对象来存储。
package com.thread;import java.util.Random;public class ThreadLocal_fun { private static ThreadLocal<Integer> x=new ThreadLocal<Integer>();//表示放置一个变量 private static ThreadLocal<MyThreadScopeDate> xObj=new ThreadLocal<MyThreadScopeDate>();//放置一个变量对象 public static void main(String[] args) { for(int i=0;i<2;i++){//开启两个线程 new Thread(new Runnable() { @Override public void run() { int data=new Random().nextInt(); System.out.println(Thread.currentThread().getName()+" has put data:"+data); x.set(data); MyThreadScopeDate.getThreadInstance().setName("admin");//为每个线程放置变量 MyThreadScopeDate.getThreadInstance().setAge(data); new A().get(); new B().get(); } }).start();} }static class A{public void get(){ int data=x.get(); System.out.println("A:"+Thread.currentThread().getName()+" has put data:"+data); MyThreadScopeDate obj=MyThreadScopeDate.getThreadInstance(); System.out.println("A"+Thread.currentThread().getName()+":"+obj);}}static class B{public void get(){ int data=x.get(); System.out.println("B:"+Thread.currentThread().getName()+" has put data:"+data); MyThreadScopeDate obj=MyThreadScopeDate.getThreadInstance(); System.out.println("A:"+Thread.currentThread().getName()+":"+obj);}}}//封装的变量对象 ,单例class MyThreadScopeDate{private MyThreadScopeDate(){}//私有化的构造函数不允许实例化private static ThreadLocal<MyThreadScopeDate> map=new ThreadLocal<MyThreadScopeDate>();public static MyThreadScopeDate getThreadInstance(){MyThreadScopeDate instance=map.get();if(instance == null){instance=new MyThreadScopeDate();map.set(instance);}return instance;}private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "MyThreadScopeDate [name=" + name + ", age=" + age + "]";}}
线程池
多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力;一般来说可以创建三种不同类型的线程池,同时线程池也为我们提供了关闭的方法:
a.创建固定大小 的线程池:一次只能执行三个任务,其他任务等待。 Executors.newFixedThreadPool(3)
b.创建缓存线程池:线程数动态量化,空闲线程自动回收。 Executors.newCachedThreadPool()
c.创建单一线程池:池中只有一个线程,如果线程意外终止就新产生一个。Executors.newSingleThreadExecutor()
d.shutdown();//当所有的任务都运行完后,就关闭线程池
e.shutdownNow();//立刻关闭线程池
package com.thread;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class ThreadPool {public static void main(String[] args) { //ExecutorService threadPool=Executors.newFixedThreadPool(3);//创建固定大小的线程池,这里表明只能同时运行3个线程。 //ExecutorService threadPool=Executors.newCachedThreadPool();//创建缓存线程池,为所有的任务都分配一个线程,自动关闭空闲的线程 ExecutorService threadPool=Executors.newSingleThreadExecutor();//创建单一线程池。 for(int i=1;i<10;i++){//循环往线程池中放10个任务 final int task=i; threadPool.execute(new Runnable() {//在线程池中放任务 @Override public void run() { for(int j=5;j<10;j++){ try {Thread.sleep(20);} catch (InterruptedException e) {e.printStackTrace();} System.out.println("线程"+Thread.currentThread().getName()+" loop of"+j+"次,在执行第 "+task+"个任务!"); } } }); } System.out.println("提交了10个任务"); //threadPool.shutdown();//当所有的任务都运行完后,就关闭线程池 //threadPool.shutdownNow();//立刻关闭线程池}}
0 0
- Java多线程编程详解
- Java多线程编程详解
- Java多线程编程详解
- Java多线程编程详解
- Java多线程编程详解
- Java多线程编程详解
- Java多线程编程详解
- Java多线程编程详解
- Java多线程编程详解
- Java多线程编程详解
- Java多线程编程详解
- Java多线程编程详解
- JAVA多线程编程详解
- Java多线程编程详解
- Java多线程编程详解
- Java多线程编程详解
- Java多线程编程详解
- Java多线程编程详解
- Android listview下拉刷新学习
- android获取com.android.internal.R
- [wordpress搬家]马来西亚 你好
- Windows CE6.0下使用IAMStreamConfig接口更改拍摄图像的分辨率
- Python -Eclipse + Pydev 开发Python
- java 多线程详解
- unity3d 解决animation NullReferenceException: GetRef
- hbase 的配置过程
- struts2运行流程分析
- [水题][第一阶段-回归水题][HDOJ-2022]海选女主角
- xml 节点与元素问题
- 理解Android系统(一)
- mysql 性能优化点记录
- 林奇与“三体世界”:打造一段大IP下的传