`
段箭*残箫
  • 浏览: 52983 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类

线程知识

阅读更多

1线程:是进程内部运行的一个顺序控制流,是有别于进程的,它是在进程内部运行的,一个进程可能包含多个线程,线程是共享内存地址空间,而一个进程是独占内存地址空间。

2.如何实现多线程

方式1.继承Thread类(实际上Thread也是实现了Runnable接口的,并实现了run()方法)

方式2.实现Runnable接口

以上两种都可以实现多线程,但是都必须重写run()方法,而且启动线程是调用start()方法,而不是run()方法;如果调用run()方法启动线程,只是调用了一个普通的方法而已,并没有产生新的线程。

注意:以上两种方式实现多线程时,只能调用一次start()方法,同一个线程如果重复调用start()方法,会抛出一个异常,这得从Thread的源码中分析:

 public synchronized void start() {
        /**
	 * This method is not invoked for the main method thread or "system"
	 * group threads created/set up by the VM. Any new functionality added 
	 * to this method in the future may have to also be added to the VM.
	 *
	 * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
        group.add(this);
        start0();
        if (stopBeforeStart) {
	    stop0(throwableFromStop);
	}
    }

 

从上面的源码看出,当第一次调用start()方法后,threadStatus 就被设置为非0了,当再次调用start()方法时,会首先判断threadStatus是否为0,如果不为0,则抛出IllegalThreadStateException异常。


我们一般情况是实现Runnable接口来实现多线程的,因为java中只有单继承 ,当一个雷已经继承了另一个类,这个时候无法在继承另一个类了,而接口可以多实现,所以在开发中一般都是使用实现Runable接口这种方式来实现多线程的。

但是,实现Runnable接口的类还不是线程类,要实现多线程,必须先产生实现Runnable接口的对象,再构造一个Thread类,构造时将此对象作为参数传递给Thread类

一个实现Runnable接口的java类:

public class ThreadDemo implements Runnable {

	public ThreadDemo() {

	}

	public void run() {
		doSomething();
	}

	public void doSomething() {
		System.out.println("doSmething");
	}

}

 测试类:

public class ThreadDemoTest {
	
	public static void main(String[] args) {
		ThreadDemo demo = new ThreadDemo();
		Thread thread = new Thread(demo);
		thread.start();
	}
}

 
而使用继承Thread类这种方式实现多线程要相对简单一点。

一个实现继承Thread类的java类:

public class ThreadDemo extends Thread{

	public ThreadDemo() {

	}

	public void run() {
		doSomething();
	}

	public void doSomething() {
		System.out.println("doSmething");
	}

}

 测试类:

public class ThreadDemoTest {

	public static void main(String[] args) {
		ThreadDemo demo = new ThreadDemo();
		demo.start();
	}
}

 

3.线程状态。

1>>新建:新创建了一个线程对象。

2>>就绪:线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

3>>运行:就绪状态的线程获取了CPU控制权,执行程序代码。

4>>死亡:线程执行完了或者因异常退出了run()方法,该线程结束生命周期

线程在运行时,还有几种状态:

sleep:Thread类的方法,线程在到达给定的时间以后自动醒来,进入就绪状态。

wait:Object类的方法,线程在别的线程调用notify,notifyAll之后才醒来,进入就绪状态。

yield:Thread类的方法,显示出让CPU控制权。进入就绪状态。

阻塞:等待IO输入输出

4。线程的优先级

为什么要有优先级呢?因为优先级可以保证重要的线程优先执行,而不重要的线程后执行(相对来说)。

注意,并不是优先级高的线程一定先执行,知只是说有优先级高的线程获取CPU控制权的几率更高。

线程的优先级分10个等级,分别用整数表示。最低为1级,最高为10级,分配给线程的默认优先级,取值为5。

Thread类的setPriority()和getPriority()方法分别用来设置和获取线程的优先级。

每个线程都有一个默认的优先级,主线程的优先级为Thread.NORM_PRIORITY(5)。

5.线程同步

 线程同步的真实意思和字面意思恰好相反。线程同步的真实意思,其实是“排队”:几个线程之间要排队,一个一个对共享资源进行操作,而不是同时进行操作。

当多个线程同时访问同一个资源时,可能会造成数据的不一致,这时为了保持数据的一致性,需要将该资源进行同步处理,也就是说,在同一个时间内,只允许一个线程访问这个资源。

注意,如果多个线程只是读取同一资源,不对该资源的数据进行修改,这时同步是没有必要的,只有当可能对该资源的数据进行修改时才可以同步。

实现线程同步,要使用java关键字synchronized。

实现线程同步有2种方式,一种是同步方法,一种是同步块,下面具体分析。

1>>同步方法

public synchronized void save() {
		//方法体		
}

 问题:

当一个线程进入到一个对象的同步方法后,能否进入该对象的其他同步方法,非同步方法呢?

我们知道,java中的线程同步,是给对象同步,是给对象上锁。像上面的问题,多个线程同时进入同一个对象的同步方法,当第一个线程到达这个对象的方法时,会获取该对象锁,同时进入方法执行方法体,当第二个线程运行到该对象的这个方法时,会查看该对象是否被锁住,如果已经被锁住,则进入就绪状态,等待获取CPU控制权,同理,后面的线程也一样。

一个线程类:

package com.lovo.lis.thread;

public class Thread1 extends Thread {

	private Account account;

	public Thread1(Account account) {
		this.account = account;
		this.start();
	}

	@Override
	public void run() {
		account.save();
	}
}

 

第2个线程类:

package com.lovo.lis.thread;

public class Thread2 extends Thread {

	private Account account;

	public Thread2(Account account) {
		this.account = account;
		this.start();
	}

	@Override
	public void run() {
		account.save();
	}
}

 共享资源类:

package com.lovo.lis.thread;

public class Account {

	public synchronized void save() {
		System.out.println(Thread.currentThread().getName() + "线程开始进入save方法");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + "线程开始退出save方法");
	}

}

  

 测试类:

package com.lovo.lis.thread;

public class Test {
	public static void main(String[] args) {
		Account account = new Account();
		Thread1 thread1 = new Thread1(account);
		Thread2 thread2 = new Thread2(account);
	}
}

 

运行上面的程序:

结果如下:

 

Thread-0线程开始进入save方法
Thread-0线程开始退出save方法
Thread-1线程开始进入save方法
Thread-1线程开始退出save方法

 从结果看出,线程被同步了,原因是对Account对象只有一个,也就是说Account对象是同步锁对象。2个线程都是访问同一个对象,当第一个线程获取这个锁之后,Account对象就被同步了。

那如果现在将代码改为下面这种情况,结果又会是什么样的呢?

package com.lovo.lis.thread;

public class Test {
	public static void main(String[] args) {
		Account account = new Account();
		Thread1 thread1 = new Thread1(new Account());
		Thread2 thread2 = new Thread2(new Account());
	}
}

 

Accout类产生了2个对象,分别对应给2个线程,这时,运行结果如下:

Thread-0线程开始进入save方法
Thread-1线程开始进入save方法
Thread-0线程开始退出save方法
Thread-1线程开始退出save方法

 

可以看出,此时未被同步,原因是产生了2个共享资源对象,不是同一个共享资源,当然起不到同步的作用了。

考虑这种情况:如果其中一个线程进入共享资源的同步方法后,其他线程能否进入该资源的其他同步方法?

还是以上面的代码为例,修改共享资源类:

package com.lovo.lis.thread;

public class Account {

	public synchronized void save() {
		System.out.println(Thread.currentThread().getName() + "线程开始进入save方法");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + "线程开始退出save方法");
	}
	
	public   void getMoney(){
		System.out.println(Thread.currentThread().getName() + "线程开始进入getMoney方法");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + "线程开始退出getMoney方法");
	}
}

 

 

 修改Thread2类:

package com.lovo.lis.thread;

public class Thread2 extends Thread {

	private Account account;

	public Thread2(Account account) {
		this.account = account;
		this.start();
	}

	@Override
	public void run() {
		account.getMoney();
	}
}

 

Thread1类保持不变

package com.lovo.lis.thread;

public class Test {
	public static void main(String[] args) {
		Account account = new Account();
		Thread1 thread1 = new Thread1(account);
		Thread2 thread2 = new Thread2(account);
	}
}

 

结果:

Thread-0线程开始进入save方法
Thread-1线程开始进入getMoney方法
Thread-0线程开始退出save方法
Thread-1线程开始退出getMoney方法

 

 

可以看出,如果其中一个线程进入共享资源的同步方法后,其他线程可以进入该资源的其他非同步方法

那如果是同步方法呢,请注意,这里是同一个共享资源。

修改共享资源类:

package com.lovo.lis.thread;

public class Account {

	public synchronized void save() {
		System.out.println(Thread.currentThread().getName() + "线程开始进入save方法");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + "线程开始退出save方法");
	}
	
	public synchronized  void getMoney(){
		System.out.println(Thread.currentThread().getName() + "线程开始进入getMoney方法");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + "线程开始退出getMoney方法");
	}
}

 

结果:

Thread-0线程开始进入save方法
Thread-0线程开始退出save方法
Thread-1线程开始进入getMoney方法
Thread-1线程开始退出getMoney方法

 

于是可以得出结论:

如果其中一个线程进入共享资源的同步方法后,其他线程不能进入该资源的其他同步方法,而非同步方法则可以。

 

2>>同步块

public void getMoney(){
		synchronized(Account.class){
			//方法体
		}
}

 由于synchronized后面的括号内可以跟任何的数据类型,所以,同步块比同步方法更加灵活,在开发中用的更多。

还是以上面为例,还是那个问题:

如果其中一个线程进入共享资源的同步块后,其他线程能否进入该资源的其他同步块呢?

共享资源类:

 

 

 

package com.lovo.lis.thread;

public class Account {

	public void save() {
		synchronized (Account.class) {
			System.out.println(Thread.currentThread().getName() + "线程开始进入save方法");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + "线程开始退出save方法");
		}
	}

	public void getMoney() {
		synchronized (Account.class) {

			System.out.println(Thread.currentThread().getName()
					+ "线程开始进入getMoney方法");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()
					+ "线程开始退出getMoney方法");
		}
	}
}

 线程类1:

package com.lovo.lis.thread;

public class Thread1 extends Thread {

	private Account account;

	public Thread1(Account account) {
		this.account = account;
		this.start();
	}

	@Override
	public void run() {
		account.save();
	}
}

 

  线程类2:

package com.lovo.lis.thread;

public class Thread2 extends Thread {

	private Account account;

	public Thread2(Account account) {
		this.account = account;
		this.start();
	}

	@Override
	public void run() {
		account.getMoney();
	}
}

 

测试类:

package com.lovo.lis.thread;

public class Test {
	public static void main(String[] args) {
		Account account = new Account();
		Thread1 thread1 = new Thread1(new Account());
		Thread2 thread2 = new Thread2(new Account());
	}
}

运行结果:

Thread-0线程开始进入save方法
Thread-0线程开始退出save方法
Thread-1线程开始进入getMoney方法
Thread-1线程开始退出getMoney方法

  

此时,同步块里面传入的是Account类的Class对象,所以不管new多少个Account类的对象,当一个线程进入到这个account类的同步块之后,不能访问这个类的其他同步方法。

而如果将synchronized (Account.class) {}的Account.class改为new account(),则永远不会被同步。

修改共享资源类:

 

 

package com.lovo.lis.thread;

public class Account {

	public void save() {
		synchronized (new Account()) {
			System.out.println(Thread.currentThread().getName() + "线程开始进入save方法");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + "线程开始退出save方法");
		}
	}

	public void getMoney() {
		synchronized (new Account()) {

			System.out.println(Thread.currentThread().getName()
					+ "线程开始进入getMoney方法");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()
					+ "线程开始退出getMoney方法");
		}
	}
}

 

运行结果如下:

Thread-0线程开始进入save方法
Thread-1线程开始进入getMoney方法
Thread-0线程开始退出save方法
Thread-1线程开始退出getMoney方法

 和上面预料的完全一样。

结束语:

有关线程的知识就先写到此了,如有错误,请指正。

 

0
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics