Java 多线程,
分享于 点击 45168 次 点评:251
Java 多线程,
线程生命周期创建: 创建了线程对象
就绪:创建对象后调用了start方法 ,此时线程还没有得到CPU资源 ,只是有运行的条件
终止:线程执行完了run方法,或者使用stop方法停止了线程
阻塞:线程正在进行,但由于某些原因让出了CPU资源,暂停了自己的运行,进入了阻塞状态,如调用了sleep方法
常用方法:
sleep():
使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁。也就是说如果有synchronized同步快,其他线程仍然不能访问共享数据。注意该方法要捕捉异常。
例如有两个线程同时执行(没有synchronized)一个线程优先级为MAX_PRIORITY,另一个为MIN_PRIORITY,如果没有Sleep()方法,只有高优先级的线程执行完毕后,低优先级的线程才能够执行;但是高优先级的线程sleep(500)后,低优先级就有机会执行了。
总之,sleep()可以使低优先级的线程得到执行的机会,当然也可以让同优先级、高优先级的线程有执行的机会。
yield():
该方法与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会
join():
调用此方法的线程立即执行,之后再执行其他线程
wait(),notify(),notifyAll():
这三个方法用于协调多个线程对共享数据的存取,所以必须在synchronized语句块内使用。synchronized关键字用于保护共享数据,阻止其他线程对共享数据的存取,但是这样程序的流程就很不灵活了,如何才能在当前线程还没退出synchronized数据块时让其他线程也有机会访问共享数据呢?此时就用这三个方法来灵活控制。
wait()方法使当前线程暂停执行并释放对象锁标示,让其他线程可以进入synchronized数据块,当前线程被放入对象等待池中。当调用notify()方法后,将从对象的等待池中移走一个任意的线程并放到锁标志等待池中,只有锁标志等待池中线程能够获取锁标志;如果锁标志等待池中没有线程,则notify()不起作用。
notifyAll()则从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中。
注意 这三个方法都是Java.lang.Object的方法。
共享可见性实现原理:
线程一对共享变量的修改要想被线程二看见:
1:把线程一的工作内存更新过的共享变量刷行到主内存中
2:把主内存中更新的共享变量的值更新到线程二的工作内存中
关键字synchronized:
该关键字用于保护共享数据,当然前提条件是要分清哪些数据是共享数据。
每个对象都有一个锁标志,当一个线程访问到该对象,被Synchronized修饰的数据将被"上锁",阻止其他线程访问
。当前线程访问完这部分数据后释放锁标志,其他线程就可以访问了
synchronized可以实现原子性和可见性:
如何实现可见性:
线程解锁前必须把最新的共享变量的值刷行到主内存中
线程加锁前将清空工作内存中的共享变量的值,从而使工作内存必须在主内存中获取最新的共享变量的值
JMM(java memory model):
描述了java内存中各种变量(线程共享变量)的访问规则,以及在JVM中将变量
存储到内存和从内存中读取出变量这样的底层实现
多线程中每个线程都有一个工作内存,以及一个主内存
所有的变量都存储在主内存中;
线程对共享变量的所有操作都必须自自己的工作内存中进行
重排序:
重排序:代码书写的顺序与实际执行的顺序不同,
指令重排序是编译器或处理器为了提高程序性能而做的优化.
书写顺序: int number=1;
int id=2;
执行顺序:
int id=2;
int number=1;
as-if-serial:无论如何重排序,程序执行的结果应该与代码顺序执行的结果一致.
Java保证单线程下遵循as-if-serial语义.
单线程重排序不会给程序带来可见性问题
但是多线程就会给程序带来可见性问题
可见性问题:
不可见的原因: synchronized解决方法:
1:线程的交叉执行 原子性
2:重排序结合线程交叉执行 原子性
3:共享变量未及时更新 可见性
A.volatile是保证被修饰变量的可见性,但不能保证原子操作
BJava中没有提供检测与避免死锁的专门机制,但应用程序员可以采用某些策略防止死锁的发生
CJAVA中对共享数据操作的并发控制是采用加锁技术
D共享数据的访问权限都必须定义为private
实例:
package scanner;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class VolatileDemo {
private Lock lock = new ReentrantLock();
private int number = 0;
public int getNumber(){
return this.number;
}
public void increase(){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.number++;
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
final VolatileDemo volDemo = new VolatileDemo();
for(int i = 0 ; i < 500 ; i++){
//new Runnable是一个匿名内部类
//new Thread:创建线程的实例 实例里的参数为Runnale接口的构造参数
new Thread(new Runnable() {
@Override
public void run() {
volDemo.increase();
}
}).start();
}
//如果还有子线程在运行,主线程就让出CPU资源,
//直到所有的子线程都运行完了,主线程再继续往下执行
while(Thread.activeCount()>1){
Thread.yield();
}
System.out.println("number : " + volDemo.getNumber());
}
}
上述例子多运行几遍会发现并不是理想的500
如何实现同步:
(加同步锁)synchronized
ublic class VolatileDemo {
//private Lock lock = new ReentrantLock();
private int number = 0;
public int getNumber(){
return this.number;
}
public void increase(){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (this) {
this.number++;
}
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
final VolatileDemo volDemo = new VolatileDemo();
for(int i = 0 ; i < 500 ; i++){
//new Runnable是一个匿名内部类
//new Thread:创建线程的实例 实例里的参数为Runnale接口的构造参数
new Thread(new Runnable() {
@Override
public void run() {
volDemo.increase();
}
}).start();
}
//如果还有子线程在运行,主线程就让出CPU资源,
//直到所有的子线程都运行完了,主线程再继续往下执行
while(Thread.activeCount()>1){
Thread.yield();
}
System.out.println("number : " + volDemo.getNumber());
}
}
使用 ReentrantLock
package scanner;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class VolatileDemo {
private Lock lock = new ReentrantLock();
private int number = 0;
public int getNumber(){
return this.number;
}
public void increase(){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
lock.lock();
this.number++;
lock.unlock();
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
final VolatileDemo volDemo = new VolatileDemo();
for(int i = 0 ; i < 500 ; i++){
//new Runnable是一个匿名内部类
//new Thread:创建线程的实例 实例里的参数为Runnale接口的构造参数
new Thread(new Runnable() {
@Override
public void run() {
volDemo.increase();
}
}).start();
}
//如果还有子线程在运行,主线程就让出CPU资源,
//直到所有的子线程都运行完了,主线程再继续往下执行
while(Thread.activeCount()>1){
Thread.yield();
}
System.out.println("number : " + volDemo.getNumber());
}
}
在多线程中出现重排序 有可能对程序运行结果产生影响:
public class SynchronizedDemo {
//共享变量
private boolean ready = false;
private int result = 0;
private int number = 1;
//写操作
public void write(){
ready = true; //1.1
number = 2; //1.2
}
//读操作
public void read(){
if(ready){ //2.1
result = number*3; //2.2
}
System.out.println("result的值为:" + result);
}
//内部线程类
public class ReadWriteThread extends Thread {
//根据构造方法中传入的flag参数,确定线程执行读操作还是写操作
private boolean flag;
public ReadWriteThread(boolean flag){
this.flag = flag;
}
@Override
public void run() {
if(flag){
//构造方法中传入true,执行写操作
write();
}else{
//构造方法中传入false,执行读操作
read();
}
}
}
public static void main(String[] args) {
SynchronizedDemo synDemo = new SynchronizedDemo();
//启动线程执行写操作
synDemo .new ReadWriteThread(true).start();
//启动线程执行读操作
synDemo.new ReadWriteThread(false).start();
}
}
多运行几次 就会发现结果有时为0,或者是6 或者其他的什么结果 造成这样的原因是因为程序发生了重排序
执行结果可能是:
2.1--》2.2 number:0
1.2--》1.1--》2.1--》2.2 number:6
只有数据之间的约束才不会发生重排序 ,就算是if语句也不是只约重排序的限制条件
如何解决此实例的同步问题:
1:使用synchronized方法
public class SynchronizedDemo {
//共享变量
private boolean ready = false;
private int result = 0;
private int number = 1;
//写操作
public synchronized void write(){
ready = true; //1.1
number = 2; //1.2
}
//读操作
public synchronized void read(){
if(ready){ //2.1
result = number*3; //2.2
}
System.out.println("result的值为:" + result);
}
//内部线程类
public class ReadWriteThread extends Thread {
//根据构造方法中传入的flag参数,确定线程执行读操作还是写操作
private boolean flag;
public ReadWriteThread(boolean flag){
this.flag = flag;
}
@Override
public void run() {
if(flag){
//构造方法中传入true,执行写操作
write();
}else{
//构造方法中传入false,执行读操作
read();
}
}
}
public static void main(String[] args) {
SynchronizedDemo synDemo = new SynchronizedDemo();
//启动线程执行写操作
synDemo .new ReadWriteThread(true).start();
//启动线程执行读操作
synDemo.new ReadWriteThread(false).start();
}
}
2:使用休眠的方式让出CPU资源 等读操作执行 一秒后读操作已经执行完了
package scanner;
public class SynchronizedDemo {
// 共享变量
private boolean ready = false;
private int result = 0;
private int number = 1;
// 写操作
public void write() {
ready = true; // 1.1
number = 2; // 1.2
}
// 读操作
public void read() {
if (ready) { // 2.1
result = number * 3; // 2.2
}
System.out.println("result的值为:" + result);
}
// 内部线程类
public class ReadWriteThread extends Thread {
// 根据构造方法中传入的flag参数,确定线程执行读操作还是写操作
private boolean flag;
public ReadWriteThread(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if (flag) {
// 构造方法中传入true,执行写操作
write();
} else {
// 构造方法中传入false,执行读操作
read();
}
}
}
public static void main(String[] args) {
SynchronizedDemo synDemo = new SynchronizedDemo();
// 启动线程执行写操作
synDemo.new ReadWriteThread(true).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 启动线程执行读操作
synDemo.new ReadWriteThread(false).start();
}
}
相关文章
- 暂无相关文章
用户点评