多线程的学习

来源:互联网 发布:三板股票交易软件 编辑:程序博客网 时间:2024/04/29 09:42

多线程的创建方式有两种:

  1. 继承Thread类,并覆盖run()方法
  2. 实现Runnable接口,并实现run()方法

其中run()方法中存放的是线程运行的代码。其中实现接口的方式避免了单继承的局限性,定义线程时,建议使用实现接口的方式。

学习多线程,要认清多线程的5种运行状态,这5种状态分别是:创建,运行,消亡,冻结,阻塞。

刚创建的线程,调用start()方法转为运行,如果运行中调用sleep()会转为冻结,直到冻结时间到了会转为阻塞(具备运行资格但没执行权),阻塞状态要等到取得执行权才会转为运行状态,线程运行中调用stop()方法或者线程正常运行结束都会转到消亡。

在多线程的运行中,会经常出现一些安全问题。

public class Ticket implements Runnable {private int tickets=100;@Overridepublic void run() {while(true){if(tickets<0) return;try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+" "+tickets--);}}public static void main(String [] args){Ticket t = new Ticket();Thread thread1 = new Thread(t);Thread thread2 = new Thread(t);Thread thread3 = new Thread(t);Thread thread4 = new Thread(t);thread1.start();thread2.start();thread3.start();thread4.start();}}

上面例子是模拟现实多个窗口卖票,但运行后发现票数出现为负的情况,这就是多线程的安全问题。

当一个线程在执行操作共享数据的代码块且只操作其中一部分时,另一个线程参加进来,导致共享数据的错误。

以上代码可以修改如下:

public class Ticket implements Runnable {private int tickets=100;@Overridepublic void run() {// TODO Auto-generated method stubwhile(true){synchronized (this) {if(tickets<0) return;try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName()+" "+tickets--);}}}public static void main(String [] args){Ticket t = new Ticket();Thread thread1 = new Thread(t);Thread thread2 = new Thread(t);Thread thread3 = new Thread(t);Thread thread4 = new Thread(t);thread1.start();thread2.start();thread3.start();thread4.start();}

上面代码中的画线部分就是加上的代码,只要在操作数据的代码块外面加synchronized(object)。这种操作叫作同步代码块,即一个线程在执行期间同步代码块内容的时候,另一个线程是不能访问的,相当于加上了锁,只有当操作的线程执行完时,其他线程才可以执行这块代码,这就避免了上面的错误。同步代码块虽然解决了多线程安全问题,但因为多个线程需要判断锁,较为消耗资源。

解决安全问题除了同步代码块,还有同步函数。即在操作共享数据的方法返回类型前加上synchronized关键字。

单例模式也有关于同步的操作,具体如下:

public class Single {private static Single obj = null;//延迟加载private Single(){}public static Single getInstance(){if(obj==null){//双重加锁 ,为了提高效率synchronized (Single.class) {//锁为Class对象if(obj==null)obj = new Single();}}return obj;}}


多线程还有个比较重要的知识点是线程间通信的等待唤醒机制。

class Resource{private int count;private String name;private boolean flag=false;public Resource(){}public  void setName(String name){this.name = name+":"+count++;System.out.println(Thread.currentThread()+"生产"+this.name);}public  void getName(){System.out.println(Thread.currentThread()+"消费"+name);}public synchronized void put() throws InterruptedException{while(flag)wait();setName("商品");flag=true;notifyAll();}public synchronized void get() throws InterruptedException{while(!flag)wait();getName();flag=false;notifyAll();}}

以上代码是为了实现资源生产一个,消费一个的需求。标志位为真时,生产线程休息,标志位为假时,消费线程休息。一开始标志位为假,当生产一个商品后,将标志位设为真,同时唤醒其他线程,由于标志位为真,接下来肯定时消费线程运行。消费线程消费一个商品后,将标志位为假,这就等于将执行权交给生产线程。

JDK1.5以后有了Lock,Condition上面代码可用如下替换:
class Resource{private int count;private String name;private boolean flag=false;private Lock lock = new ReentrantLock();private Condition condition_put = lock.newCondition();private Condition condition_get = lock.newCondition();public Resource(){}public  void setName(String name){this.name = name+":"+count++;System.out.println(Thread.currentThread()+"生产"+this.name);}public  void getName(){System.out.println(Thread.currentThread()+"消费"+name);}public  void put() throws InterruptedException{lock.lock();try {while(flag)condition_put.await();setName("商品");flag=true;condition_get.signalAll();} finally{lock.unlock();}}public  void get() throws InterruptedException{lock.lock();try {while(!flag)condition_get.await();getName();flag=false;condition_put.signalAll();} finally{lock.unlock();}}}

由上可知,JDK1.5将同步Synchronized替换成现实Lock操作。将Object 中的 wait,notify,notifyAll,替换成了Condition,该对象可通过Lock获取。其中一个Lock可对应多个Condition,可实现本方只唤醒对方操作。
原创粉丝点击