@XQF
2017-01-03T10:54:37.000000Z
字数 9848
阅读 1949
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();
}
}
知识点应该是都理解了,设计模式还是不是很懂。这里主要是将我们的共享资源与线程进行了绑定,在线程内部访问资源的模式。由于之前对共享资源内部的设定。就会按照我们的希望执行。