一、多线程意义
多线程也是为了实现多个任务同时运行。是一个进程中的某一个顺序控制流。
多任务系统的实现方式有2种:
一种是将任务下放到程序单位。进程上面;不同程序完成不同任务。
另一种是将任务下放到线程单位,在一个进程中,将不同的任务分离,交给不同行的线程来执行。可以提高程序的运行效率,解决单线程程序的只有一个运行路径的问题。
在同一个任务中,多个线程可以共享该程序的内存空间。CPU在线程间的切换开销比在进程间的切换小得多,提高程序的运行效率和节省内容空间。
二、多线程实现
java中多线程的实现,很方便。有下面2种方式:
只要继承Thread类,重写里面的run方法;或者实现Runnable接口,重写里面的run方法。
这2中里面,最常用的又是Runnable接口,它将可以解决单继承的问题,并能实现线程任务与类的分离。
三、synchronized同步及死锁
1、同步synchronized
在多线程程序中,当多个线程同时操作同一资源时,特别是修改数据时,会造成数据的紊乱。使得程序出现意想不到的情况。
同步就是为了解决数据紊乱的问题,在资源上面加把锁,
(只有取得锁那个线程才能访问同步所在的代码)
使用synchronized可选修饰符实现同步。
分为同步方法和同步块:
public synchronized void method (){
//方法实现代码
}
public void method (){
synchronized(锁定对象//一般为该对象自身){
//数据操作代码
}
//方法实现
}
即使有A线程正在使用对象的(同步方法a), 线程B也可以访问该对象的(其他非同步方法)。
同一时间,如果有线程C想访问对象的(同步方法b),由于一个对象只有一个锁,C线程将无法访问对象的(同步方法b)。
所以,只有synchronized中的代码才能真正实现同步,对象的其他部分无影响。对于对象的其他非同步方法,任意线程可以随时访问。
2、死锁
只有同步中,才会出现死锁
当有2个线程循环依赖于一对同步对象时,形成一个需求循环,而无法打破时就是死锁了。
解决死锁:
线程运行需要访问多个同步资源时:
先获取低优先级的资源,再获取优先级高的资源,如果能够获得,则继续执行。
一旦不能获得高优先级资源的锁时,就放弃已获得的资源的锁,用wait方法
四、线程的运行机制
线程的生命周期为,new产生之后到 run方法结束。
1),普通线程(无同步)
线程产生之后调用start方法启动run方法后,线程就进入runnable状态(就绪状态)
在runnable状态(就绪状态)等待JVM分配资源,占用虚拟cpu时,开始运行running(运行状态)
在running(运行状态)如果线程调用sleep方法,或者join方法时,线程就进入到了blocked(阻塞状态)
在blocked(阻塞状态)如果线程一段时间后,可以恢复到runnable状态(就绪状态)循环直到running(运行状态)将run方法执行完成。死亡。
2),同步线程(同步资源)
如果线程有访问同步资源时,线程会多出下面的状态。
在running(运行状态),如果需要用到同步对象。那么就会进入到该对象的锁池当中,等待获取锁。
一旦得到锁(只会有个线程获得),那么线程就会进入runnable状态(就绪状态)
在running(运行状态)如果线程自己调用wait方法时,会放弃已获得的同步对象的锁,线程进入wait等待池,同时JVM通知这个对象的锁池当中的某个线程,将锁给它,这个线程就可以进入runnable状态(就绪状态)
对于在某个对象的wait等待池中的线程,使用这个对象的线程调用notify方法时,就会唤醒对象等待wait池中的某个线程;
使用这个对象的线程调用notifyAll方法时,就会唤醒对象等待wait池中的所有线程。被唤醒的线程就会进入该对象的锁池,继续等待 锁。
1、sleep
方法不会使得线程丢失当前对象锁,相当于是自我休眠
2、wait
线程会失去对象锁。自我放弃
3、join
使用于业务或者操作(任务)的依赖时。
(1)、使得当前进行的任务暂停,
(2)、运行join调用者的线程,直到调用者结束或者等待指定的时间。
(3)、最后继续运行被暂停的任务线程
列子:
import java.util.ArrayList;
public class TestThread {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
// Test test = new Test();
// new MyThread(test,"boy").start();
// new MyThread(test,"girl").start();
// new MyThread(test,"rabbit").start();
// //测试哲学家问题代码
// ArrayList<KuaiZi> kuaiZi = new ArrayList<KuaiZi>();
// for(int i = 0 ; i < 5 ; i ++){
// kuaiZi.add(new KuaiZi("筷子"+i));
// }
// for(int i = 0 ; i < 5 ; i ++){
// new Peple(i,"哲学家"+i,kuaiZi).start();
// }
// //测试,同一对象,2个线程使用它,其中一个调用同步方法,另一个调用
// //非同步方法,是否能同时访问?
//结果:多个线程可以访问对象的非同步方法。同步方法不锁定对象。
// 同步块锁定对象也可以访问,同步块也无效
KuaiZi kz1 = new KuaiZi("铁的");
KuaiZi kz2 = new KuaiZi("银的");
// new MyThread1(kz1).start();
new MyThread2(kz1,kz2).start();
//
}
}
public class MyThread1 implements Runnable {
private KuaiZi kz;
public MyThread1(KuaiZi kz) {
super();
this.kz = kz;
}
public void start(){
new Thread(this).start();
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try {
// this.kz.setGetAble(false);
System.out.println(Thread.currentThread().getName()+"开始");
System.out.println("非同步访问名字:"+this.kz.getName());
// this.kz.setGetAble(true);
Thread.sleep(500);
// this.kz.setGetAble(true);
System.out.println(Thread.currentThread().getName()+"结束\n");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class MyThread2 implements Runnable {
private KuaiZi kz1;
private KuaiZi kz2;
public MyThread2(KuaiZi kz1, KuaiZi kz2) {
super();
this.kz1 = kz1;
this.kz2 = kz2;
}
public void start(){
new Thread(this).start();
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
System.out.println(Thread.currentThread().getName()+"*************************** 同步方法开始");
{
this.kz1.setGetAble(false);
this.kz2.getName();
System.out.println("!!!!!!!!!!!!!!!!!!!!!!");
// if(Thread.currentThread().holdsLock(kz1) ){
// System.out.println("11111111111111");
// }else{
// System.out.println("00000000000000000");
// }
// if(Thread.currentThread().holdsLock(kz2)){
// System.out.println("22222222222222222");
// }else{
// System.out.println("00000000000000000");
// }
// if(Thread.currentThread().holdsLock(kz2) && Thread.currentThread().holdsLock(kz1) ){
// System.out.println("333333333333333");
// }else{
// System.out.println("00000000000000000");
// }
}
System.out.println(Thread.currentThread().getName()+"*************************** 同步方法结束\n");
}
}
}
public class KuaiZi {
private boolean getAble;
private String name;
public KuaiZi(String name) {
super();
this.name = name;
this.getAble = true;
}
public KuaiZi(){
}
public String getName() {
// if(Thread.currentThread().holdsLock(this)){
// System.out.println(Thread.currentThread().getName()+"------------------------------------------持有锁:"+this.name);
// }else{
// System.out.println(Thread.currentThread().getName()+"*************没有锁:"+this.name);
// }
synchronized(this){
return name;
}
}
public int setGetAble(boolean getAble) {
synchronized(this){
this.getAble = getAble;
if(this.getAble){
System.out.println("同步方法 "+this.name+"被释放了*********");
}else{
System.out.println("同步方法 "+this.name+"被锁定了!!!!!!!!!!!");
}
//测试同步锁代码,平时注释掉
// while(true)
// {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(Thread.currentThread().holdsLock(this)){
System.out.println(Thread.currentThread().getName()+"------------------------------------------持有锁:"+this.name);
}else{
System.out.println(Thread.currentThread().getName()+"------------------------------------------丢失锁:"+this.name);
}
this.getName();
// }
}
return 1;
}
public boolean isGetAble() {
return getAble;
}
}
import java.util.ArrayList;
public class Peple implements Runnable {
private int position;
private String name;
private ArrayList<KuaiZi> al;
public Peple(int position, String name, ArrayList al) {
super();
this.position = position;
this.name = name;
this.al = al;
}
public Peple(){
}
public Peple(int position, String name) {
super();
this.position = position;
this.name = name;
}
public void start(){
new Thread(this,this.name).start();
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
this.take();
this.reset();
}
}
public void take(){
// synchronized(this.al.get(this.position)){
if(this.al.get(this.position).isGetAble()){
//先锁定筷子,再使用
this.al.get(this.position).setGetAble(false);
System.out.println(this.name+"得到了:"+this.al.get(this.position).getName()+"!!!!");
if(!this.al.get((this.position+1)%5).isGetAble()){
// try {
// //如果下一根筷子无法得到,就放弃现在拿到的筷子 的锁
// wait();
// } catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println(this.name+"获取 :"+this.al.get((this.position+1)%5).getName()+"失败!等待唤醒…………");
// }
}//如果拿到了,则继续使用,并吃饭
else{
// synchronized(this.al.get((this.position+1)%5)){
//先锁定筷子,再使用
this.al.get((this.position+1)%5).setGetAble(false);
System.out.println(this.name+"得到了:"+this.al.get((this.position+1)%5).getName()+"!!!!");
this.eat();
//吃完就释放二根筷子
this.al.get((this.position+1)%5).setGetAble(true);
//通知第二根根筷子等待的线程
// notifyAll();
// }
}
//通知第一根筷子等待的线程
this.al.get(this.position).setGetAble(true);
// notifyAll();
}
else{
System.out.println(this.name+"获取 :"+this.al.get(this.position).getName()+"失败!等待唤醒…………");
}
}
public void eat(){
System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!----------------------"+this.name+":正在吃饭***");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("----------------------------------"+this.name+":吃完了");
}
public void reset(){
System.out.println(this.name+":正在休息…………");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(this.name+":休息完了…………");
}
public String getName() {
return name;
}
}