@XQF
2017-01-03T02:54:37.000000Z
字数 9848
阅读 2083
java
程序是一段静态代码,是人们解决问题的思维方式在计算机中的描述,是应用软件执行的蓝本,是一个静态的概念
是程序的一个运行例程,是应用程序的一次动态执行过程。进程由若干个代码和数据块组成。每个进程还拥有其他资源,例如文件,动态内存地址,线程等。进程是计算机进行资源分配的独立单位。
是进程中相对独立的一个执行单元,是操作系统调度的基本单位,,一个进程可以包含若干个线程。同一个进程中的各个线程对应于一组CPU指令,一组CPU寄存器以及一个堆栈。进程并不执行代码,只是代码存放的地址空间。进程地址空间中存放的代码由线程来执行。线程的执行由操作系统调度。
JVM的很多任务都依赖于线程调度,执行程序代码的任务是由线程来完成的。在Java中,每一个线程都有一个独立的程序计数器和方法调用栈。
程序计数器,也就是一个记录线程当前执行程序代码位置的寄存器,当线程在执行过程中,程序计数器指向的是下一条要执行的指令。
方法调用栈用来描述线程在执行一系列的方法调用过程。栈中的每一个元素称为一个栈帧。每一个栈帧对应一个方法调用,帧中保存了方法调用的参数,局部变量和程序执行过程中的临时数据。
当JVM进程被启动时,同一个JVM进程中有且只有一个进程,就是自己。在这个JVM环境中,所有程序的运行都是以线程来运行的。JVM会最先产生一个主线程,由它来运行指定程序的入口。在这个程序中,主线程从main方法开始中执行。main()执行结束,JVM也跟着退出。
线程只有在创建之后才会存在,创建后的线程,要想获得处理执行,还要再经过启动。
首先看看Thread类的构造方法

常用的方法:
void run() //线程运行时所执行的代码都在这个方法中,是Runnable接口声明的唯一方法void start() //使该线程开始执行,java虚拟机调用该线程的run方法static int activeCount() //返回当前线程的线程组中活动线程的数目static Thread currentThread() //返回当前正在执行线程的引用static int enumerate(Thread []t) //将当前线程组中的每一个活动线程复制到指定的数组中String getName() //返回该线程的名称int getPriority() //返回该线程的优先级Thread.State getState() //返回线程的状态Thread Group getThreadGroup() //返回线程所在的线程组final boolean isAlive() //测试线程是否处于活动状态void setDaemon(boolean on) //将该线程标记为守护线程(true)或者用户线程void setName(String name) //改变线程名称,使之与参数name相同void interrupt() //中断线程void join() //等待线程终止,有多个重载static void yield() //暂停当前正在执行的线程对象,并执行其他线程
实例:
/*** Created by XQF on 2016/11/30.*/class MyThread extends Thread {int count = 1, number;public MyThread(int number) {this.number = number;System.out.println("创建线程 " + number);}public void run() {while (true) {System.out.println("线程 " + number + ":计数 " + count);if (++count == 6) {return;}}}}public class TestThread {public static void main(String[] args) {for (int i = 0; i < 3; i++) {new MyThread(i + 1).start();}}}
实验结果:
创建线程 1创建线程 2创建线程 3线程 1:计数 1线程 1:计数 2线程 1:计数 3线程 1:计数 4线程 1:计数 5线程 2:计数 1线程 3:计数 1线程 2:计数 2线程 2:计数 3线程 2:计数 4线程 2:计数 5线程 3:计数 2线程 3:计数 3线程 3:计数 4线程 3:计数 5
这个例子深刻的说明了多线程的并发问题,线程执行的顺序是杂乱无章的,执行顺序的不确定性。线程1执行一下后换B,或者C。额,主要是他们的优先级是差不多的,而且这个也没有设置同步执行。
老实说这个接口编程思想还是有一定道理。这个应该叫做一个任务,先定义一个任务,然后再给任务绑定一个线程去执行任务。
/*** Created by XQF on 2016/11/30.*/class MyRunnable implements Runnable {private int count = 1, number;public MyRunnable(int number) {this.number = number;System.out.println("创建第" + number + "个任务!");}public void run() {while (true) {System.out.println("任务 " + number + ":计数 " + count);if (++count == 6) {return;}}}}public class TestRunnable {public static void main(String[] args) {for (int i = 0; i < 3; i++) {MyRunnable myRunnable = new MyRunnable(i + 1);Thread thread = new Thread(myRunnable);thread.start();}}}
结果:
创建第1个任务!创建第2个任务!创建第3个任务!任务 1:计数 1任务 2:计数 1任务 1:计数 2任务 1:计数 3任务 3:计数 1任务 2:计数 2任务 3:计数 2任务 1:计数 4任务 3:计数 3任务 2:计数 3任务 3:计数 4任务 1:计数 5任务 3:计数 5任务 2:计数 4任务 2:计数 5
使用线程对象的start()方法来启动线程,有两方面的功能:
java线程的生命周期分为七种状态:
要是创建了一个线程而没有启动,那么线程就是处于创建状态
如果对一个处于创建状态的线程调用start()方法,则此线程就进入了可运行状态。可运行状态相当于是有可能运行也有可能就绪等待。因为假如有很多的可运行线程就需要操作系统的调度。
这两个状态是由运行状态转换过来的。处于这个状态就暂时放弃了获得处理机的权利,直到线程调试器重新激活它。
处于阻塞和等待状态的原因
线程在其他任何状态下调用stop()方法。
三种方式进入死亡:
在生命周期内,状态经常发生变化,由于系统有多个处于活动状态的线程,我们往往通过控制线程的状态变化来协调多个线程并发执行。
调用sleep方法,有两个重载:
(sleep方法要抛出异常)

yield()方法
sleep()和yield()的区别
就是几个线程分工交替执行,当然执行调度就是使用前面的sleep(),yield()或者join()方法来实现。
主线程终止时后台线程自动终止。当然可以在主线程终止之前手动终止
public class DaemonTest {public static void main(String[] args) {Thread thread = new Thread(new Runnable() {public void run() {System.out.println("这是一个后台线程");}});thread.setDaemon(true);//设置为后台线程thread.start();}}
void setPriority(int newPriority);//设置新的优先级int getPriority(); //获取线程的优先级
优先级通常是1-10这之间的整数,JVM不会改变一个线程的优先级,然而这个值是没有保证的,一些JVM可能识别不了10个不同的值。
线程默认优先级为5,Thread类有三个常量:
设计多线程应用程序的时候,一定不要依赖优先级,因为调度优先级的操作是没有保障的,只能把优先级的作用作为一种提高程序效率的办法,但是不要依赖
线程之间竞争CPU资源,使得线程无序访问这些资源,最终无法得到正确的结果。
Java虚拟机为每一个对象配备了一把锁和一个等候集,这个对象可以使实例对象,也可以是类对象。
通过new创建一个实例对象,从而获得对象的引用,通过java.lang.Class类的forName成员方法获得类对象的引用。
一个类的静态成员方法和静态成员属于类对象,而非静态的东西属于实例对象
同步块的核心代码:
synchronized(synObject){//关键代码}
在定义类的时候对里面的关键代码进行控制。其中的关键代码必须获得对象synObject的锁才能执行。当一个线程欲进入该对象的关键代码时,JVM将检查该对象的锁是不是已经被其他线程获得,如果没有,则JVM将把该对象的锁交给当前请求锁的线程,该线程获得锁之后就可以进入关键代码区域。
大致就是这么个意思了,我有一个公共资源的对象,明显,这个对象被给予了一把锁。多个线程访问该对象的时候就要按照规矩来获得锁才能进入访问。
/*** Created by XQF on 2016/11/30.*/class User {private String code;private int cash;public User(String code, int cash) {this.code = code;this.cash = cash;}public String getCode() {return code;}public void setCode(String code) {this.code = code;}public void oper(int x) {synchronized (this) {//这里this.cash += x;System.out.println(Thread.currentThread().getName() + "运行结束,增加 " + x + " ,当前用户余额为: " + cash);}}}class HerThread extends Thread {private User u;private int y;HerThread(String name, User u, int y) {super(name);this.u = u;this.y = y;}public void run() {u.oper(y);}}public class SynchronizedBlock {public static void main(String[] args) {User u = new User("hh", 100);HerThread t1 = new HerThread("线程a", u, 20);HerThread t2 = new HerThread("线程b", u, -60);HerThread t3 = new HerThread("线程c", u, -80);HerThread t4 = new HerThread("线程d", u, -30);HerThread t5 = new HerThread("线程e", u, 32);HerThread t6 = new HerThread("线程f", u, 21);t1.start();t2.start();t3.start();t4.start();t5.start();t6.start();}}
这个实例也算是深刻理解同步块儿了,总是觉得这个例子很是巧妙。让线程与用户独立,设计方法真是不错。
/*** Created by XQF on 2016/11/30.*/class UUUUUuser {private String code;private int cash;public UUUUUuser(String code, int cash) {this.code = code;this.cash = cash;}public String getCode() {return code;}public void setCode(String code) {this.code = code;}public synchronized void oper(int x) {//这里this.cash += x;System.out.println(Thread.currentThread().getName() + "运行结束,增加 " + x + " ,当前用户余额为: " + cash);}}class HisThread extends Thread {private UUUUUuser u;private int y;HisThread(String name, UUUUUuser u, int y) {super(name);this.u = u;this.y = y;}public void run() {u.oper(y);}}public class SynchronizedMethod {public static void main(String[] args) {UUUUUuser u = new UUUUUuser("hh", 100);HisThread t1 = new HisThread("线程a", u, 20);HisThread t2 = new HisThread("线程b", u, -60);HisThread t3 = new HisThread("线程c", u, -80);HisThread t4 = new HisThread("线程d", u, -30);HisThread t5 = new HisThread("线程e", u, 32);HisThread t6 = new HisThread("线程f", u, 21);t1.start();t2.start();t3.start();t4.start();t5.start();t6.start();}}
回到类引用上,我们也可以把类的静态方法设为synchronied,以控制其对类静态成员变量饿的访问
java提供三个方法来支持线程通信,者三个方法都是Object类的final方法:
这三个方法只能在synchronized关键字的作用范围内使用,并且是在同一个同步问题中搭配使用这三个方法才有实际意义。
notify()和notifyAll()都能唤醒线程,但notify()只能唤醒一个,究竟是哪一个也不确定,但是notifyAll()能唤醒这个对象上等待队列中的所有线程。为了安全起见,我们大多数的时候调用notifyAll(),除非你明确的知道要唤醒哪一个线程
/*** Created by XQF on 2016/12/1.*///在共享资源里面进行同步设置class ShareData {private char c;private boolean writeAble = true;//通知变量,这里是通知是否可写//将setter设为同步方法,一次只能一个线程获得对象锁public synchronized void setShareData(char c) {//拿到对象锁,但是通知变量声明此时不可写,于是wait()释放锁,本线程进入等待序列,(不可生产)if (!writeAble) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}//要是通知变量表示此时可以写(可生产)就生产this.c = c;//writeAble由初始的true变false,表示已经有产品,可以通知消费者消费了,有了产品了就不用再生产了writeAble = false;//通知刚进入等待序列要消费的那个线程notify();}public synchronized char getShareData() {//要是没有产品(这里表示可写),于是wait()释放锁,本线程进入等待序列,(没有产品,不可消费)if (writeAble) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}//要是通知变量表示此时可以写(可消费)//消费了,通知生产者可以继续生产了writeAble = true;//通知生产者线程生产了,没饭吃了notify();return c;}}class Producer extends Thread {private ShareData s;Producer(ShareData s) {this.s = s;}public void run() {for (char c = 'A'; c <= 'Z'; c++) {try {Thread.sleep((int) Math.random() * 400);} catch (InterruptedException e) {e.printStackTrace();}s.setShareData(c);System.out.println(c + " produced by Producer!");}}}class Consumer extends Thread {ShareData s;Consumer(ShareData s) {this.s = s;}public void run() {char c;do {try {Thread.sleep((int) Math.random() * 400);} catch (InterruptedException e) {e.printStackTrace();}c = s.getShareData();System.out.println(c + " consumed by Consumer!");} while (c == 'Z');}}public class ThreadCommunicationTest {public static void main(String[] args) {ShareData s = new ShareData();new Consumer(s).start();new Producer(s).start();}}
死锁就是多个线程同时被阻塞,他们中的一个或全部都在等待某个资源的释放。由于被无限期的阻塞,因此程序不能正常执行。
死锁时,一个线程等待另一个线程释放资源,而同时另一个线程又在等待第一个线程释放资源。
下面这个实例是绝对的经典:
程序中有两个共享对象,每一个线程都需要获得这两个对象的锁后才可以执行。于是我们让线程1拿到obj1的锁后睡一会儿让线程2拿到了obj2的锁。于是线程1等待线程2释放obj2的锁,而线程2也在等待线程1释放obj1的锁,。,。,形成了死锁。
/*** Created by XQF on 2016/12/1.*/class MyThread1 implements Runnable {public void run() {synchronized (DeadLockExample.obj1) {System.out.println("线程1进入obj1同步代码块");//进入之后让他休眠10毫秒,将运行权力交给其他线程,例如线程2,,。专门放水的try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}synchronized (DeadLockExample.obj2) {System.out.println("线程1进入obj2同步代码块");}}}}class MyThread2 implements Runnable {public void run() {synchronized (DeadLockExample.obj2) {System.out.println("线程2进入obj2同步代码块");//进入之后让他休眠10毫秒,将运行权力交给其他线程,例如线程2,,。专门放水的try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}synchronized (DeadLockExample.obj1) {System.out.println("线程2进入obj1同步代码块");}}}}public class DeadLockExample {public static Object obj1 = new Object();public static Object obj2 = new Object();public static void main(String[] args) {Thread t1 = new Thread(new MyThread1());Thread t2 = new Thread(new MyThread2());t1.start();t2.start();}}
知识点应该是都理解了,设计模式还是不是很懂。这里主要是将我们的共享资源与线程进行了绑定,在线程内部访问资源的模式。由于之前对共享资源内部的设定。就会按照我们的希望执行。