java多线程详解
来源:互联网 发布:云计算应用实例 编辑:程序博客网 时间:2024/05/17 01:08
一、简单线程的实现
1、Runnable接口实现多线陈
1.1、将任务代码移到实现了Runnable接口的类的run方法中。这个接口非常简单,只有一个方法
public interface Runnable
{
void run();
}
可以如下所示实现一个类:
class MyRunnable implements Runnable
{
public void run(){
do something
}
}
1.2、创建一个类对象:
Runnable r = new MyRunnable();
1.3、由Runnable创建一个Thread对象:
Thread t = new Thread(r);
1.4、启动线程
t.start();
2.继承Thread类实现多线程
2.1、继承Thread类并重写run方法,将任务代码写进run方法中
- public class MyThread extends Thread {
- public void run() {
- System.out.println("MyThread.run()");
- }
- }
2.2、启动线程
- MyThread myThread1 = new MyThread();
- MyThread myThread2 = new MyThread();
- myThread1.start();
- myThread2.start();
二、Thread和Runnable的区别
其实Thread的run方法也是调用的Runnable的run方法,但是他们有什么区别呢?
1、 如果一个类继承了Thread,ze不适合资源共享。但如果实现了Runnable接口的话,则容易实现资源共享。
class hello extends Thread {
public void run() {
for (int i = 0; i < 7; i++) {
if (count > 0) {
System.out.println("count= " + count--);
}
}
}
public static void main(String[] args) {
hello h1 = new hello();
hello h2 = new hello();
hello h3 = new hello();
h1.start();
h2.start();
h3.start();
}
private int count = 5;
}
【运行结果】:
count= 5
count= 4
count= 3
count= 2
count= 1
count= 5
count= 4
count= 3
count= 2
count= 1
count= 5
count= 4
count= 3
count= 2
count= 1
如果这是一个买票系统的话,count为剩余的车票数,上面的h1,h2,h3为三个人同时买票,那么就会出现问题了。
下面我们把Thread换为Runnable
class MyThread implements Runnable{
private int ticket = 5; //5张票
public void run() {
for (int i=0; i<=20; i++) {
if (this.ticket > 0) {
System.out.println(Thread.currentThread().getName()+ "正在卖票"+this.ticket--);
}
}
}
}
public class lzwCode {
public static void main(String [] args) {
MyThread my = new MyThread();
new Thread(my, "1号窗口").start();
new Thread(my, "2号窗口").start();
new Thread(my, "3号窗口").start();
}
}
【运行结果】:
count= 5
count= 4
count= 3
count= 2
count= 1
2、实现Runnble接口比继承Thread类所具有的优势
2.1 适合多个相同的代码线程去处理同一个资源
2.1可以避免java中的多线程限制
2.3 增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。
所以建议尽量用接口来实现多线程。
注:
提醒一下大家:main方法其实也是一个线程。在java中所以的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。
在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个jVM实习在就是在操作系统中启动了一个进程。
三、线程的一些常用方法
3.1 设置线程的优先级:线程默认的优先级是创建它的执行线程的优先级。可以通过setPriority(int newPriority)更改线程的优先级。优先级代表的是概率,并不是绝对的优先级。例如:
Thread t = new MyThread();
t.setPriority(8);
t.start();
线程优先级为1~10之间的正整数,JVM从不会改变一个线程的优先级。然而,1~10之间的值是没有保证的。一些JVM可能不能识别10个不同的值,而将这些优先级进行每两个或多个合并,变成少于10个的优先级,则两个或多个优先级的线程可能被映射为一个优先级。
线程默认优先级是5,Thread类中有三个常量,定义线程优先级范围:
static int MAX_PRIORITY
线程可以具有的最高优先级。
static int MIN_PRIORITY
线程可以具有的最低优先级。
static int NORM_PRIORITY
分配给线程的默认优先级。
3.2、Thread.yield()方法
Thread.yield()方法作用是:暂停当前正在执行的线程对象,并执行其他线程。
yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。
结论:yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。
3.3、join()方法
Thread的非静态方法join()让一个线程B“加入”到另外一个线程A的尾部。在A执行完毕之前,B不能工作。例如:
Thread t = new MyThread();
t.start();
t.join();
另外,join()方法还有带超时限制的重载版本。例如t.join(5000);则让线程等待5000毫秒,如果超过这个时间,则停止等待,变为可运行状态。
四、同步
同步又称为并发,即多个线程访问同一个资源,要确保资源安全,即线程安全。
多个线程同时访问一个资源,会造成一些问题。比如还是买车票的问题,如果在一个时间点上,两个线程同时使用这个资源,那他们取出的火车票是一样的(座位号一样),这样就会给乘客造成麻烦。
class MyThread implements Runnable{
private int ticket = 10; //5张票
public void run() {
for (int i =0; i<=20; i++) {
if (this .ticket > 0) {
System. out .println(Thread.currentThread().getName()+ "正在卖票" + this. ticket--);
}
}
}
public static void main(String [] args) {
MyThread my = new MyThread();
new Thread(my , "1号窗口" ).start();
new Thread(my , "2号窗口" ).start();
new Thread(my , "3号窗口" ).start();
}
}
运行结果
可以看出上面的代码出现了问题,那么怎么解决这个问题呢?且看下面分解:
1、同步块
即在run方法中假如synchronized块,来保证每一次只有一个线程进来。
class MyThread implements Runnable{
private int ticket = 10; //5张票
public void run() {
for (int i =0; i<=20; i++) {
synchronized (this ){
if (this .ticket > 0) {
try {
Thread. sleep(500);
} catch (Exception e ) {
e.printStackTrace();
}
System. out .println(Thread.currentThread().getName()+ "正在卖票" + this. ticket--);
}
}
}
}
public static void main(String [] args) {
MyThread my = new MyThread();
new Thread(my , "1号窗口" ).start();
new Thread(my , "2号窗口" ).start();
new Thread(my , "3号窗口" ).start();
}
}
运行结果:可以看出结果正常。
2、同步方法
从1.0版本开始,java的每个对象都有一个内部锁。如果一个方法用synchronized关键字声明,那么对象的锁将保护整个方法。
public synchronized void method(){
//do something
}
买票代码改进如下
class MyThread implements Runnable{
private int ticket = 10; //10张票
public void run() {
for (int i =0; i<=20; i++) {
if (this .ticket > 0) {
try {
Thread. sleep(500);
} catch (Exception e ) {
e.printStackTrace();
}
}
sale();
}
}
public synchronized void sale(){
if (this .ticket > 0) {
System. out .println(Thread.currentThread().getName()+ "正在卖票" + this. ticket--);
}
}
public static void main(String [] args) {
MyThread my = new MyThread();
new Thread(my , "1号窗口" ).start();
new Thread(my , "2号窗口" ).start();
new Thread(my , "3号窗口" ).start();
}
}
显示结果
3、死锁
过多的同步容易造成死锁
是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
死锁是因为多线程访问共享资源,由于访问的顺序不当所造成的,通常是一个线程锁定了一个资源A,而又想去锁定资源B;在另一个线程中,锁定了资源B,而又想去锁定资源A以完成自身的操作,两个线程都想得到对方的资源,而不愿释放自己的资源,造成两个线程都在等待,而无法执行的情况。
下面从网上找了一个图,很形象的说明了此问题
package com.slowly.tongbu;
class Zhangsan{ // 定义张三类
public void say(){
System. out .println("张三对李四说:“你给我画,我就把书给你。”" ) ;
}
public void get(){
System. out .println("张三得到画了。" ) ;
}
}
class Lisi{ // 定义李四类
public void say(){
System. out .println("李四对张三说:“你给我书,我就把画给你”" ) ;
}
public void get(){
System. out .println("李四得到书了。" ) ;
}
}
public class ThreadDeadLock implements Runnable{
private static Zhangsan zs = new Zhangsan() ; // 实例化static型对象
private static Lisi ls = new Lisi() ; // 实例化static型对象
private boolean flag = false ; // 声明标志位,判断那个先说话
public void run(){ // 覆写run()方法
if (flag ){
synchronized (zs ){ // 同步张三
zs .say() ;
try {
Thread. sleep(500) ;
} catch (InterruptedException e ){
e.printStackTrace() ;
}
synchronized (ls ){
zs .get() ;
}
}
}
else {
synchronized (ls ){
ls .say() ;
try {
Thread. sleep(500) ;
} catch (InterruptedException e ){
e.printStackTrace() ;
}
synchronized (zs ){
ls .get() ;
}
}
}
}
public static void main(String args[]){
ThreadDeadLock t1 = new ThreadDeadLock() ; // 控制张三
ThreadDeadLock t2 = new ThreadDeadLock() ; // 控制李四
t1. flag = true ;
t2. flag = false ;
Thread thA = new Thread( t1) ;
Thread thB = new Thread( t2) ;
//两个线程启动,各自调用run方法,这样的话各自需要的资源被占用,死锁发生
thA .start() ;
thB .start() ;
}
}
声明:此文参考了很多博文,包括书籍,因为比较杂,本人已经记不住出处了,所以有些引用就不注明出处了,还请见谅。
0 0
- Java多线程编程详解
- Java多线程编程详解
- Java多线程编程详解
- Java多线程编程详解
- Java多线程编程详解
- Java多线程编程详解
- Java多线程编程详解
- Java多线程编程详解
- Java多线程编程详解
- Java多线程编程详解
- Java多线程编程详解
- Java多线程编程详解
- JAVA多线程编程详解
- Java多线程编程详解
- Java多线程编程详解
- Java多线程编程详解
- Java多线程编程详解
- Java多线程编程详解
- 关系型数据库性能优化总结
- 远程服务器桌面不显示解决方法
- $REPLY变量
- PHP服务器信息探针可以检测网络流量,CPU,硬盘,内存使用情况,网站管理员必备
- 个人学习-java-springmvc-转换器
- java多线程详解
- Oracle SQL Plus常见技巧
- Hibernate三种状态的区分,以及save,update,saveOrUpdate,merge等的使用
- Linux指令学习(CentOs6.5)tail指令
- Swift-Charts 多种样式图表演示
- CGImageCreateWithImageInRect图片裁剪问题
- Android 数据存储(三)之数据库存储
- 应用系统之间数据传输的几种方式(转载)
- Hibernate深入学习(四):类级别的检索策略