分类目录归档:程序生活

Program Life – Web

Java线程:新特征-有返回值的线程

Java线程:新特征-有返回值的线程
 
在Java5之前,线程是没有返回值的,常常为了“有”返回值,破费周折,而且代码很不好写。或者干脆绕过这道坎,走别的路了。
 
现在Java终于有可返回值的任务(也可以叫做线程)了。
 
可返回值的任务必须实现Callable接口,类似的,无返回值的任务必须Runnable接口。
 
执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了。
 
下面是个很简单的例子:
 
import java.util.concurrent.*;

/**
* Java线程:有返回值的线程
*
* @author Administrator 2009-11-5 0:41:50
*/

public class Test {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
                //创建一个线程池
                ExecutorService pool = Executors.newFixedThreadPool(2);
                //创建两个有返回值的任务
                Callable c1 = new MyCallable("A");
                Callable c2 = new MyCallable("B");
                //执行任务并获取Future对象
                Future f1 = pool.submit(c1);
                Future f2 = pool.submit(c2);
                //从Future对象上获取任务的返回值,并输出到控制台
                System.out.println(">>>"+f1.get().toString());
                System.out.println(">>>"+f2.get().toString());
                //关闭线程池
                pool.shutdown();
        }
}

class MyCallable implements Callable{
        private String oid;

        MyCallable(String oid) {
                this.oid = oid;
        }

        @Override
        public Object call() throws Exception {
                return oid+"任务返回的内容";
        }
}

 
>>>A任务返回的内容
>>>B任务返回的内容

Process finished with exit code 0

 
非常的简单,要深入了解还需要看Callable和Future接口的API啊。

本文出自 “熔 岩” 博客,请务必保留此出处http://lavasoft.blog.51cto.com/62575/222082

Java线程:新特征-线程池

Java线程:新特征-线程池
 
Sun在Java5中,对Java线程的类库做了大量的扩展,其中线程池就是Java5的新特征之一,除了线程池之外,还有很多多线程相关的内容,为多线程的编程带来了极大便利。为了编写高效稳定可靠的多线程程序,线程部分的新增内容显得尤为重要。
 
有关Java5线程新特征的内容全部在java.util.concurrent下面,里面包含数目众多的接口和类,熟悉这部分API特征是一项艰难的学习过程。目前有关这方面的资料和书籍都少之又少,大所属介绍线程方面书籍还停留在java5之前的知识层面上。
 
当然新特征对做多线程程序没有必须的关系,在java5之前通用可以写出很优秀的多线程程序。只是代价不一样而已。
 
线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。
 
在Java5之前,要实现一个线程池是相当有难度的,现在Java5为我们做好了一切,我们只需要按照提供的API来使用,即可享受线程池带来的极大便利。
 
Java5的线程池分好多种:固定尺寸的线程池、可变尺寸连接池、。
 
在使用线程池之前,必须知道如何去创建一个线程池,在Java5中,需要了解的是java.util.concurrent.Executors类的API,这个类提供大量创建连接池的静态方法,是必须掌握的。
 
一、固定大小的线程池
 
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;

/**
* Java线程:线程池-
*
* @author Administrator 2009-11-4 23:30:44
*/

public class Test {
        public static void main(String[] args) {
                //创建一个可重用固定线程数的线程池
                ExecutorService pool = Executors.newFixedThreadPool(2);
                //创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口
                Thread t1 = new MyThread();
                Thread t2 = new MyThread();
                Thread t3 = new MyThread();
                Thread t4 = new MyThread();
                Thread t5 = new MyThread();
                //将线程放入池中进行执行
                pool.execute(t1);
                pool.execute(t2);
                pool.execute(t3);
                pool.execute(t4);
                pool.execute(t5);
                //关闭线程池
                pool.shutdown();
        }
}

class MyThread extends Thread{
        @Override
        public void run() {
                System.out.println(Thread.currentThread().getName()+"正在执行。。。");
        }
}

 
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-2正在执行。。。

Process finished with exit code 0

 
二、单任务线程池
 
在上例的基础上改一行创建pool对象的代码为:
                //创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。
                ExecutorService pool = Executors.newSingleThreadExecutor();

 
输出结果为:
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。

Process finished with exit code 0

 
对于以上两种连接池,大小都是固定的,当要加入的池的线程(或者任务)超过池最大尺寸时候,则入此线程池需要排队等待。
一旦池中有线程完毕,则排队等待的某个线程会入池执行。
 
三、可变尺寸的线程池
 
与上面的类似,只是改动下pool的创建方式:
                //创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。
                ExecutorService pool = Executors.newCachedThreadPool();

 
pool-1-thread-5正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-4正在执行。。。
pool-1-thread-3正在执行。。。
pool-1-thread-2正在执行。。。

Process finished with exit code 0

 
四、延迟连接池
 
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
* Java线程:线程池-
*
* @author Administrator 2009-11-4 23:30:44
*/

public class Test {
        public static void main(String[] args) {
                //创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
                ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
                //创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口
                Thread t1 = new MyThread();
                Thread t2 = new MyThread();
                Thread t3 = new MyThread();
                Thread t4 = new MyThread();
                Thread t5 = new MyThread();
                //将线程放入池中进行执行
                pool.execute(t1);
                pool.execute(t2);
                pool.execute(t3);
                //使用延迟执行风格的方法
                pool.schedule(t4, 10, TimeUnit.MILLISECONDS);
                pool.schedule(t5, 10, TimeUnit.MILLISECONDS);
                //关闭线程池
                pool.shutdown();
        }
}

class MyThread extends Thread {
        @Override
        public void run() {
                System.out.println(Thread.currentThread().getName() + "正在执行。。。");
        }
}

 
pool-1-thread-1正在执行。。。
pool-1-thread-2正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-2正在执行。。。

Process finished with exit code 0

 
五、单任务延迟连接池
 
在四代码基础上,做改动
                //创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。
                ScheduledExecutorService pool = Executors.newSingleThreadScheduledExecutor();

 
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。

Process finished with exit code 0

 
六、自定义线程池
 
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
* Java线程:线程池-自定义线程池
*
* @author Administrator 2009-11-4 23:30:44
*/

public class Test {
        public static void main(String[] args) {
                //创建等待队列
                BlockingQueue<Runnable> bqueue = new ArrayBlockingQueue<Runnable>(20);
                //创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。
                ThreadPoolExecutor pool = new ThreadPoolExecutor(2,3,2,TimeUnit.MILLISECONDS,bqueue);
                //创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口
                Thread t1 = new MyThread();
                Thread t2 = new MyThread();
                Thread t3 = new MyThread();
                Thread t4 = new MyThread();
                Thread t5 = new MyThread();
                Thread t6 = new MyThread();
                Thread t7 = new MyThread();
                //将线程放入池中进行执行
                pool.execute(t1);
                pool.execute(t2);
                pool.execute(t3);
                pool.execute(t4);
                pool.execute(t5);
                pool.execute(t6);
                pool.execute(t7);
                //关闭线程池
                pool.shutdown();
        }
}

class MyThread extends Thread {
        @Override
        public void run() {
                System.out.println(Thread.currentThread().getName() + "正在执行。。。");
                try {
                        Thread.sleep(100L);
                } catch (InterruptedException e) {
                        e.printStackTrace();
                }
        }
}

 
pool-1-thread-1正在执行。。。
pool-1-thread-2正在执行。。。
pool-1-thread-2正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-2正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-2正在执行。。。

Process finished with exit code 0

 
创建自定义线程池的构造方法很多,本例中参数的含义如下:

ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue)
用给定的初始参数和默认的线程工厂及处理程序创建新的 ThreadPoolExecutor。使用 Executors 工厂方法之一比使用此通用构造方法方便得多。

参数:
corePoolSize – 池中所保存的线程数,包括空闲线程。
maximumPoolSize – 池中允许的最大线程数。
keepAliveTime – 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
unit – keepAliveTime 参数的时间单位。
workQueue – 执行前用于保持任务的队列。此队列仅保持由 execute 方法提交的 Runnable 任务。
抛出:
IllegalArgumentException – 如果 corePoolSize 或 keepAliveTime 小于零,或者 maximumPoolSize 小于或等于零,或者 corePoolSize 大于 maximumPoolSize。
NullPointerException – 如果 workQueue 为 null

 
自定义连接池稍微麻烦些,不过通过创建的ThreadPoolExecutor线程池对象,可以获取到当前线程池的尺寸、正在执行任务的线程数、工作队列等等。
 
有关Java5线程池的内容到此就没有了,更多的内容还需要研读API来获取。

本文出自 “熔 岩” 博客,请务必保留此出处http://lavasoft.blog.51cto.com/62575/222078

Java线程:volatile关键字

Java线程:volatile关键字
 
Java™ 语言包含两种内在的同步机制:同步块(或方法)和 volatile 变量。这两种机制的提出都是为了实现代码线程的安全性。其中 Volatile 变量的同步性较差(但有时它更简单并且开销更低),而且其使用也更容易出错。
 
谈及到volatile关键字,不得不提的一篇文章是:《Java 理论与实践: 正确使用 Volatile 变量》,这篇文章对volatile关键字的用法做了相当精辟的阐述。
 
之所以要单独提出volatile这个不常用的关键字原因是这个关键字在高性能的多线程程序中也有很重要的用途,只是这个关键字用不好会出很多问题。
 
首先考虑一个问题,为什么变量需要volatile来修饰呢?
要搞清楚这个问题,首先应该明白计算机内部都做什么了。比如做了一个i++操作,计算机内部做了三次处理:读取-修改-写入。
同样,对于一个long型数据,做了个赋值操作,在32系统下需要经过两步才能完成,先修改低32位,然后修改高32位。
 
假想一下,当将以上的操作放到一个多线程环境下操作时候,有可能出现的问题,是这些步骤执行了一部分,而另外一个线程就已经引用了变量值,这样就导致了读取脏数据的问题。
 
通过这个设想,就不难理解volatile关键字了。
 
volatile可以用在任何变量前面,但不能用于final变量前面,因为final型的变量是禁止修改的。也不存在线程安全的问题。
 
更多的内容,请参看::《Java 理论与实践: 正确使用 Volatile 变量》一文,写得很好。
 
 

本文出自 “熔 岩” 博客,请务必保留此出处http://lavasoft.blog.51cto.com/62575/222076

Java线程:并发协作-死锁

Java线程:并发协作-死锁
 
线程发生死锁可能性很小,即使看似可能发生死锁的代码,在运行时发生死锁的可能性也是小之又小。
 
发生死锁的原因一般是两个对象的锁相互等待造成的。
 
在《Java线程:线程的同步与锁》一文中,简述死锁的概念与简单例子,但是所给的例子是不完整的,这里给出一个完整的例子。
 
/**
* Java线程:并发协作-死锁
*
* @author Administrator 2009-11-4 22:06:13
*/

public class Test {
        public static void main(String[] args) {
                DeadlockRisk dead = new DeadlockRisk();
                MyThread t1 = new MyThread(dead, 1, 2);
                MyThread t2 = new MyThread(dead, 3, 4);
                MyThread t3 = new MyThread(dead, 5, 6);
                MyThread t4 = new MyThread(dead, 7, 8);

                t1.start();
                t2.start();
                t3.start();
                t4.start();
        }

}

class MyThread extends Thread {
        private DeadlockRisk dead;
        private int a, b;

        MyThread(DeadlockRisk dead, int a, int b) {
                this.dead = dead;
                this.a = a;
                this.b = b;
        }

        @Override
        public void run() {
                dead.read();
                dead.write(a, b);
        }
}

class DeadlockRisk {
        private static class Resource {
                public int value;
        }

        private Resource resourceA = new Resource();
        private Resource resourceB = new Resource();

        public int read() {
                synchronized (resourceA) {
                        System.out.println("read():" + Thread.currentThread().getName() + "获取了resourceA的锁!");
                        synchronized (resourceB) {
                                System.out.println("read():" + Thread.currentThread().getName() + "获取了resourceB的锁!");
                                return resourceB.value + resourceA.value;
                        }
                }
        }

        public void write(int a, int b) {
                synchronized (resourceB) {
                        System.out.println("write():" + Thread.currentThread().getName() + "获取了resourceA的锁!");
                        synchronized (resourceA) {
                                System.out.println("write():" + Thread.currentThread().getName() + "获取了resourceB的锁!");
                                resourceA.value = a;
                                resourceB.value = b;
                        }
                }
        }
}

 
下面死锁的情况发生了,真是难得一见啊:
 
 

本文出自 “熔 岩” 博客,请务必保留此出处http://lavasoft.blog.51cto.com/62575/222074

Java线程:并发协作-生产者消费者模型

Java线程:并发协作-生产者消费者模型
 
对于多线程程序来说,不管任何编程语言,生产者和消费者模型都是最经典的。就像学习每一门编程语言一样,Hello World!都是最经典的例子。
 
实际上,准确说应该是“生产者-消费者-仓储”模型,离开了仓储,生产者消费者模型就显得没有说服力了。
对于此模型,应该明确一下几点:
1、生产者仅仅在仓储未满时候生产,仓满则停止生产。
2、消费者仅仅在仓储有产品时候才能消费,仓空则等待。
3、当消费者发现仓储没产品可消费时候会通知生产者生产。
4、生产者在生产出可消费产品时候,应该通知等待的消费者去消费。
 
此模型将要结合java.lang.Object的wait与notify、notifyAll方法来实现以上的需求。这是非常重要的。
 
/**
* Java线程:并发协作-生产者消费者模型
*
* @author leizhimin 2009-11-4 14:54:36
*/

public class Test {
        public static void main(String[] args) {
                Godown godown = new Godown(30);
                Consumer c1 = new Consumer(50, godown);
                Consumer c2 = new Consumer(20, godown);
                Consumer c3 = new Consumer(30, godown);
                Producer p1 = new Producer(10, godown);
                Producer p2 = new Producer(10, godown);
                Producer p3 = new Producer(10, godown);
                Producer p4 = new Producer(10, godown);
                Producer p5 = new Producer(10, godown);
                Producer p6 = new Producer(10, godown);
                Producer p7 = new Producer(80, godown);

                c1.start();
                c2.start();
                c3.start();
                p1.start();
                p2.start();
                p3.start();
                p4.start();
                p5.start();
                p6.start();
                p7.start();
        }
}

/**
* 仓库
*/

class Godown {
        public static final int max_size = 100; //最大库存量
        public int curnum;     //当前库存量

        Godown() {
        }

        Godown(int curnum) {
                this.curnum = curnum;
        }

        /**
         * 生产指定数量的产品
         *
         * @param neednum
         */

        public synchronized void produce(int neednum) {
                //测试是否需要生产
                while (neednum + curnum > max_size) {
                        System.out.println("要生产的产品数量" + neednum + "超过剩余库存量" + (max_size – curnum) + ",暂时不能执行生产任务!");
                        try {
                                //当前的生产线程等待
                                wait();
                        } catch (InterruptedException e) {
                                e.printStackTrace();
                        }
                }
                //满足生产条件,则进行生产,这里简单的更改当前库存量
                curnum += neednum;
                System.out.println("已经生产了" + neednum + "个产品,现仓储量为" + curnum);
                //唤醒在此对象监视器上等待的所有线程
                notifyAll();
        }

        /**
         * 消费指定数量的产品
         *
         * @param neednum
         */

        public synchronized void consume(int neednum) {
                //测试是否可消费
                while (curnum < neednum) {
                        try {
                                //当前的生产线程等待
                                wait();
                        } catch (InterruptedException e) {
                                e.printStackTrace();
                        }
                }
                //满足消费条件,则进行消费,这里简单的更改当前库存量
                curnum -= neednum;
                System.out.println("已经消费了" + neednum + "个产品,现仓储量为" + curnum);
                //唤醒在此对象监视器上等待的所有线程
                notifyAll();
        }
}

/**
* 生产者
*/

class Producer extends Thread {
        private int neednum;                //生产产品的数量
        private Godown godown;            //仓库

        Producer(int neednum, Godown godown) {
                this.neednum = neednum;
                this.godown = godown;
        }

        public void run() {
                //生产指定数量的产品
                godown.produce(neednum);
        }
}

/**
* 消费者
*/

class Consumer extends Thread {
        private int neednum;                //生产产品的数量
        private Godown godown;            //仓库

        Consumer(int neednum, Godown godown) {
                this.neednum = neednum;
                this.godown = godown;
        }

        public void run() {
                //消费指定数量的产品
                godown.consume(neednum);
        }
}

 
已经生产了10个产品,现仓储量为40
已经生产了10个产品,现仓储量为50
已经消费了50个产品,现仓储量为0
已经生产了80个产品,现仓储量为80
已经消费了30个产品,现仓储量为50
已经生产了10个产品,现仓储量为60
已经消费了20个产品,现仓储量为40
已经生产了10个产品,现仓储量为50
已经生产了10个产品,现仓储量为60
已经生产了10个产品,现仓储量为70

Process finished with exit code 0

 
说明:
对于本例,要说明的是当发现不能满足生产或者消费条件的时候,调用对象的wait方法,wait方法的作用是释放当前线程的所获得的锁,并调用 对象的notifyAll() 方法,通知(唤醒)该对象上其他等待线程,使得其继续执行。这样,整个生产者、消费者线程得以正确的协作执行。
notifyAll() 方法,起到的是一个通知作用,不释放锁,也不获取锁。只是告诉该对象上等待的线程“可以竞争执行了,都醒来去执行吧”。
 
本例仅仅是生产者消费者模型中最简单的一种表示,本例中,如果消费者消费的仓储量达不到满足,而又没有生产者,则程序会一直处于等待状态,这当 然是不对的。实际上可以将此例进行修改,修改为,根据消费驱动生产,同时生产兼顾仓库,如果仓不满就生产,并对每次最大消费量做个限制,这样就不存在此问 题了,当然这样的例子更复杂,更难以说明这样一个简单模型。
 
我喜欢简单的例子。
 
 

本文出自 “熔 岩” 博客,请务必保留此出处http://lavasoft.blog.51cto.com/62575/221932

Java线程:线程的同步-同步块

Java线程:线程的同步-同步块
 
对于同步,除了同步方法外,还可以使用同步代码块,有时候同步代码块会带来比同步方法更好的效果。
 
追其同步的根本的目的,是控制竞争资源的正确的访问,因此只要在访问竞争资源的时候保证同一时刻只能一个线程访问即可,因此Java引入了同步代码快的策略,以提高性能。
 
在上个例子的基础上,对oper方法做了改动,由同步方法改为同步代码块模式,程序的执行逻辑并没有问题。
 
 
/**
* Java线程:线程的同步-同步代码块
*
* @author leizhimin 2009-11-4 11:23:32
*/

public class Test {
        public static void main(String[] args) {
                User u = new User("张三", 100);
                MyThread t1 = new MyThread("线程A", u, 20);
                MyThread t2 = new MyThread("线程B", u, -60);
                MyThread t3 = new MyThread("线程C", u, -80);
                MyThread t4 = new MyThread("线程D", u, -30);
                MyThread t5 = new MyThread("线程E", u, 32);
                MyThread t6 = new MyThread("线程F", u, 21);

                t1.start();
                t2.start();
                t3.start();
                t4.start();
                t5.start();
                t6.start();
        }
}

class MyThread extends Thread {
        private User u;
        private int y = 0;

        MyThread(String name, User u, int y) {
                super(name);
                this.u = u;
                this.y = y;
        }

        public void run() {
                u.oper(y);
        }
}

class User {
        private String code;
        private int cash;

        User(String code, int cash) {
                this.code = code;
                this.cash = cash;
        }

        public String getCode() {
                return code;
        }

        public void setCode(String code) {
                this.code = code;
        }

        /**
         * 业务方法
         *
         * @param x 添加x万元
         */

        public void oper(int x) {
                try {
                        Thread.sleep(10L);
                        synchronized (this) {
                                this.cash += x;
                                System.out.println(Thread.currentThread().getName() + "运行结束,增加“" + x + "”,当前用户账户余额为:" + cash);
                        }
                        Thread.sleep(10L);
                } catch (InterruptedException e) {
                        e.printStackTrace();
                }
        }

        @Override
        public String toString() {
                return "User{" +
                                "code='" + code + '\'' +
                                ", cash=" + cash +
                                '}';
        }
}

 
线程E运行结束,增加“32”,当前用户账户余额为:132
线程B运行结束,增加“-60”,当前用户账户余额为:72
线程D运行结束,增加“-30”,当前用户账户余额为:42
线程F运行结束,增加“21”,当前用户账户余额为:63
线程C运行结束,增加“-80”,当前用户账户余额为:-17
线程A运行结束,增加“20”,当前用户账户余额为:3

Process finished with exit code 0

 
注意:
在使用synchronized关键字时候,应该尽可能避免在synchronized方法或synchronized块中使用sleep或者 yield方法,因为synchronized程序块占有着对象锁,你休息那么其他的线程只能一边等着你醒来执行完了才能执行。不但严重影响效率,也不合 逻辑。
同样,在同步程序块内调用yeild方法让出CPU资源也没有意义,因为你占用着锁,其他互斥线程还是无法访问同步程序块。当然与同步程序块无关的线程可以获得更多的执行时间。
 
 

本文出自 “熔 岩” 博客,请务必保留此出处http://lavasoft.blog.51cto.com/62575/221922

Java线程:线程的同步-同步方法

Java线程:线程的同步-同步方法
 
线程的同步是保证多线程安全访问竞争资源的一种手段。
线程的同步是Java多线程编程的难点,往往开发者搞不清楚什么是竞争资源、什么时候需要考虑同步,怎么同步等等问题,当然,这些问题没有很明确的答案,但有些原则问题需要考虑,是否有竞争资源被同时改动的问题?
 
在本文之前,请参阅《Java线程:线程的同步与锁》,本文是在此基础上所写的。
 
对于同步,在具体的Java代码中需要完成一下两个操作:
把竞争访问的资源标识为private;
同步哪些修改变量的代码,使用synchronized关键字同步方法或代码。
当然这不是唯一控制并发安全的途径。
 
synchronized关键字使用说明
synchronized只能标记非抽象的方法,不能标识成员变量。
 
为了演示同步方法的使用,构建了一个信用卡账户,起初信用额为100w,然后模拟透支、存款等多个操作。显然银行账户User对象是个竞争资源,而多个并发操作的是账户方法oper(int x),当然应该在此方法上加上同步,并将账户的余额设为私有变量,禁止直接访问。
 
 
/**
* Java线程:线程的同步
*
* @author leizhimin 2009-11-4 11:23:32
*/

public class Test {
        public static void main(String[] args) {
                User u = new User("张三", 100);
                MyThread t1 = new MyThread("线程A", u, 20);
                MyThread t2 = new MyThread("线程B", u, -60);
                MyThread t3 = new MyThread("线程C", u, -80);
                MyThread t4 = new MyThread("线程D", u, -30);
                MyThread t5 = new MyThread("线程E", u, 32);
                MyThread t6 = new MyThread("线程F", u, 21);

                t1.start();
                t2.start();
                t3.start();
                t4.start();
                t5.start();
                t6.start();
        }
}

class MyThread extends Thread {
        private User u;
        private int y = 0;

        MyThread(String name, User u, int y) {
                super(name);
                this.u = u;
                this.y = y;
        }

        public void run() {
                u.oper(y);
        }
}

class User {
        private String code;
        private int cash;

        User(String code, int cash) {
                this.code = code;
                this.cash = cash;
        }

        public String getCode() {
                return code;
        }

        public void setCode(String code) {
                this.code = code;
        }

        /**
         * 业务方法
         * @param x 添加x万元
         */

        public synchronized void oper(int x) {
                try {
                        Thread.sleep(10L);
                        this.cash += x;
                        System.out.println(Thread.currentThread().getName() + "运行结束,增加“" + x + "”,当前用户账户余额为:" + cash);
                        Thread.sleep(10L);
                } catch (InterruptedException e) {
                        e.printStackTrace();
                }
        }

        @Override
        public String toString() {
                return "User{" +
                                "code='" + code + '\'' +
                                ", cash=" + cash +
                                '}';
        }
}

 
输出结果:
线程A运行结束,增加“20”,当前用户账户余额为:120
线程F运行结束,增加“21”,当前用户账户余额为:141
线程E运行结束,增加“32”,当前用户账户余额为:173
线程C运行结束,增加“-80”,当前用户账户余额为:93
线程B运行结束,增加“-60”,当前用户账户余额为:33
线程D运行结束,增加“-30”,当前用户账户余额为:3

Process finished with exit code 0

 
 
反面教材,不同步的情况,也就是去掉oper(int x)方法的synchronized修饰符,然后运行程序,结果如下:
线程A运行结束,增加“20”,当前用户账户余额为:61
线程D运行结束,增加“-30”,当前用户账户余额为:63
线程B运行结束,增加“-60”,当前用户账户余额为:3
线程F运行结束,增加“21”,当前用户账户余额为:61
线程E运行结束,增加“32”,当前用户账户余额为:93
线程C运行结束,增加“-80”,当前用户账户余额为:61

Process finished with exit code 0

 
很显然,上面的结果是错误的,导致错误的原因是多个线程并发访问了竞争资源u,并对u的属性做了改动。
 
可见同步的重要性。
 
 
注意:
通过前文可知,线程退出同步方法时将释放掉方法所属对象的锁,但还应该注意的是,同步方法中还可以使用特定的方法对线程进行调度。这些方法来自于java.lang.Object类。
 
void notify()    
                    唤醒在此对象监视器上等待的单个线程。    
void notifyAll()    
                    唤醒在此对象监视器上等待的所有线程。    
void wait()    
                    导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。    
void wait(long timeout)    
                    导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量。    
void wait(long timeout, int nanos)    
                    导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量。

 
结合以上方法,处理多线程同步与互斥问题非常重要,著名的生产者-消费者例子就是一个经典的例子,任何语言多线程必学的例子。

Java线程:线程的调度-守护线程

Java线程:线程的调度-守护线程
 
守护线程与普通线程写法上基本么啥区别,调用线程对象的方法setDaemon(true),则可以将其设置为守护线程。
 
守护线程使用的情况较少,但并非无用,举例来说,JVM的垃圾回收、内存管理等线程都是守护线程。还有就是在做数据库应用时候,使用的数据库连接池,连接池本身也包含着很多后台线程,监控连接个数、超时时间、状态等等。
 
setDaemon方法的详细说明:
public final void setDaemon(boolean on)将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。    
  该方法必须在启动线程前调用。    

  该方法首先调用该线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException(在当前线程中)。    

  参数
    on – 如果为 true,则将该线程标记为守护线程。    
  抛出:    
    IllegalThreadStateException – 如果该线程处于活动状态。    
    SecurityException – 如果当前线程无法修改该线程。
  另请参见
    isDaemon(), checkAccess()

 
/**
* Java线程:线程的调度-守护线程
*
* @author leizhimin 2009-11-4 9:02:40
*/

public class Test {
        public static void main(String[] args) {
                Thread t1 = new MyCommon();
                Thread t2 = new Thread(new MyDaemon());
                t2.setDaemon(true);        //设置为守护线程

                t2.start();
                t1.start();
        }
}

class MyCommon extends Thread {
        public void run() {
                for (int i = 0; i < 5; i++) {
                        System.out.println("线程1第" + i + "次执行!");
                        try {
                                Thread.sleep(7);
                        } catch (InterruptedException e) {
                                e.printStackTrace();
                        }
                }
        }
}

class MyDaemon implements Runnable {
        public void run() {
                for (long i = 0; i < 9999999L; i++) {
                        System.out.println("后台线程第" + i + "次执行!");
                        try {
                                Thread.sleep(7);
                        } catch (InterruptedException e) {
                                e.printStackTrace();
                        }
                }
        }
}

 
后台线程第0次执行!
线程1第0次执行!
线程1第1次执行!
后台线程第1次执行!
后台线程第2次执行!
线程1第2次执行!
线程1第3次执行!
后台线程第3次执行!
线程1第4次执行!
后台线程第4次执行!
后台线程第5次执行!
后台线程第6次执行!
后台线程第7次执行!

Process finished with exit code 0

 
从上面的执行结果可以看出:
前台线程是保证执行完毕的,后台线程还没有执行完毕就退出了。
 
实际上:JRE判断程序是否执行结束的标准是所有的前台执线程行完毕了,而不管后台线程的状态,因此,在使用后台县城时候一定要注意这个问题。

本文出自 “熔 岩” 博客,请务必保留此出处http://lavasoft.blog.51cto.com/62575/221845

Java线程:线程的调度-合并

Java线程:线程的调度-合并
 
线程的合并的含义就是将几个并行线程的线程合并为一个单线程执行,应用场景是当一个线程必须等待另一个线程执行完毕才能执行时可以使用join方法。
 
join为非静态方法,定义如下:
void join()    
    等待该线程终止。    
void join(long millis)    
    等待该线程终止的时间最长为 millis 毫秒。    
void join(long millis, int nanos)    
    等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。

 
/**
* Java线程:线程的调度-合并
*
* @author leizhimin 2009-11-4 9:02:40
*/

public class Test {
        public static void main(String[] args) {
                Thread t1 = new MyThread1();
                t1.start();

                for (int i = 0; i < 20; i++) {
                        System.out.println("主线程第" + i + "次执行!");
                        if (i > 2) try {
                                //t1线程合并到主线程中,主线程停止执行过程,转而执行t1线程,直到t1执行完毕后继续。
                                t1.join();
                        } catch (InterruptedException e) {
                                e.printStackTrace();
                        }
                }
        }
}

class MyThread1 extends Thread {
        public void run() {
                for (int i = 0; i < 10; i++) {
                        System.out.println("线程1第" + i + "次执行!");
                }
        }
}

 
主线程第0次执行!
主线程第1次执行!
主线程第2次执行!
线程1第0次执行!
主线程第3次执行!
线程1第1次执行!
线程1第2次执行!
线程1第3次执行!
线程1第4次执行!
线程1第5次执行!
线程1第6次执行!
线程1第7次执行!
线程1第8次执行!
线程1第9次执行!
主线程第4次执行!
主线程第5次执行!
主线程第6次执行!
主线程第7次执行!
主线程第8次执行!
主线程第9次执行!
主线程第10次执行!
主线程第11次执行!
主线程第12次执行!
主线程第13次执行!
主线程第14次执行!
主线程第15次执行!
主线程第16次执行!
主线程第17次执行!
主线程第18次执行!
主线程第19次执行!

Process finished with exit code 0

本文出自 “熔 岩” 博客,请务必保留此出处http://lavasoft.blog.51cto.com/62575/221817

Java线程:线程的调度-让步

Java线程:线程的调度-让步
 
线程的让步含义就是使当前运行着线程让出CPU资源,但是然给谁不知道,仅仅是让出,线程状态回到可运行状态。
 
线程的让步使用Thread.yield()方法,yield() 为静态方法,功能是暂停当前正在执行的线程对象,并执行其他线程。
 
/**
* Java线程:线程的调度-让步
*
* @author leizhimin 2009-11-4 9:02:40
*/

public class Test {
        public static void main(String[] args) {
                Thread t1 = new MyThread1();
                Thread t2 = new Thread(new MyRunnable());

                t2.start();
                t1.start();
        }
}

class MyThread1 extends Thread {
        public void run() {
                for (int i = 0; i < 10; i++) {
                        System.out.println("线程1第" + i + "次执行!");
                }
        }
}

class MyRunnable implements Runnable {
        public void run() {
                for (int i = 0; i < 10; i++) {
                        System.out.println("线程2第" + i + "次执行!");
                        Thread.yield();
                }
        }
}

 
线程2第0次执行!
线程2第1次执行!
线程2第2次执行!
线程2第3次执行!
线程1第0次执行!
线程1第1次执行!
线程1第2次执行!
线程1第3次执行!
线程1第4次执行!
线程1第5次执行!
线程1第6次执行!
线程1第7次执行!
线程1第8次执行!
线程1第9次执行!
线程2第4次执行!
线程2第5次执行!
线程2第6次执行!
线程2第7次执行!
线程2第8次执行!
线程2第9次执行!

Process finished with exit code 0

本文出自 “熔 岩” 博客,请务必保留此出处http://lavasoft.blog.51cto.com/62575/221811