Java当中的线程(一)

多进程和多线程

多进程:操作系统(比如android系统)中能(同时)运行多个任务(程序)
多线程:在同一个应用程序中有多个顺序流(同时)执行

线程是进程中的一个流程。
进程像高速公路,线程像高速公路上的车。

原则上,一个软件一个进程。

多线程执行,其实是cpu在多个线程之间互相切换,速度很快,然后看着就像是同时进行。

创建线程类的2种方法

方式1:

定义一个线程类,继承Thread,并重写方法run(),方法run()成为线程体;(由于Java只支持单继承,用这种方法定义的类不能再继承其他类)

class FirstThread extends Thread{
	public void run(){
		for(int i = 0; i < 100; i++){
			System.out.println("FirstThread-->"+i);
		}
	}
}

class Test{
	public static void main(String args[]){
		//生成线程类的对象
		FirstThread ft = new FirstThread();
		//启动线程
		ft.start();
	}
}


上面简单的程序,实际开启了3个线程:1、main线程 2、ft线程 3、回收线程

下面修改程序验证一下main线程的存在
class Test{
	public static void main(String args[]){
		//生成线程类的对象
		FirstThread ft = new FirstThread();
		//启动线程
		ft.start();
		
		//添加一个循环打印,显示主线程存在
		for(int i = 0; i < 100; i++){
			System.out.println("main-->"+i);
		}
	}
}


从上面可以看出,多线程中,main线程和ft线程,两个线程是互相交替执行的(cpu在两个线程之间交替执行),而且是不规律的。

多线程规律:没谱

有些错误,只在某些执行情况出现,有些时候不出现。

为何不调用run()而是用start()?

如果把上面程序中ft.start()修改成ft.run(),就不是多线程了,而是先执行FirstThread在执行main,结果大家可以自行验证一下。
用start方法,线程是进入就绪状态。进行抢线程的状态。






看不懂图,我也不解释了,看视屏,视频说得更好。

方式二:

实际此方法用得比较多(不用上面方法,原因较多)

提供一个实现接口Runnable的类作为线程的目标对象,在初始化一个Thread类或者Thread子类的线程对象时,把目标对象传递给这个线程实例,由该目标对象提供线程体。

class RunnableImpl implements Runnable{
	public void run(){
		for(int i = 1; i < 100; i++){
			System.out.println("Runnable-->" + i);
		}
	}
}

class Test{
	public static void main(String args[]){
		//生成一个Runnable接口实现类的对象
		RunnableImpl ri = new RunnableImpl();
		//生成一个Thread对象,并将Runnable接口实现类的对象作为参数传递给该Thread对象
		Thread t = new Thread(ri);
		//通知Thread对象,执行start方法
		t.start();
	}
}

看到上面有木有很熟悉的样子?是的,上面其实可以用装饰者模式+匿名内部类,重新写,更方便。
一般我们都不会写RunnableImpl这个类继承Runnable,而是直接用匿名内部类,如下:(当然有些要继承Runnable还是要写的)

Thread t = new Thread(new Runnable(){
			public void run(){
				
				for(int i = 1; i < 100; i++){
					System.out.println("new Runnable-->" + i);
				}
				
			}
		});

		t.start();

或者

new Thread(new Runnable(){
			public void run(){
				
				for(int i = 1; i < 100; i++){
					System.out.println("new Runnable-->" + i);
				}
				
			}
		}).start();	

上面方式在android开发中常见到这样的线程。呵呵


线程的简单控制方法

中断线程

Thread.sleep() ----休眠完成后进入“就绪”状态,抢占CPU,抢到了就运行。
遇到Thread.sleep(),程序进入“阻塞”状态,结束后,进入“就绪”状态。
注意:比如Thread.sleep(2000); ,该线程真正休眠时间,并不一样是2000毫秒,因为他2000毫秒走完后,并不一样马上抢到CPU
真正休眠时间应该是:2000毫秒+等待抢占cpu的时间。

所以认为,线程休眠2000毫秒,就是停了2000毫秒,这种思想是不对的。

Thread.yield()----遇到这行代码,让出CPU。比如A和B线程,A中遇到Thread.yield()让出CPU,然后A和B都重新进入就绪状态,抢CPU。(谁抢得到那是未知数)

class Test{
	public static void main(String args[]){
		
		Thread t1 = new Thread(new Runnable(){
			public void run(){							
				
				for(int i = 1; i < 100; i++){
					System.out.println("t1-->" + i);
					
					if(i == 50){
						try{
							Thread.sleep(2000);
						}catch(Exception e){
							System.out.println(e);
						}
					}
				}
				
			}
		});
		t1.start();				
	}
}

Thread.sleep();需要try...catch

设置线程优先级

getPriority()
setPriority()

class Test{
	public static void main(String args[]){
		
		Thread t1 = new Thread(new Runnable(){
			public void run(){
				
				for(int i = 1; i < 100; i++){
					System.out.println("t1-->" + i);
				}
				
			}
		});
		// 线程的优先级最大是10,最小是1,默认是5(优先级越大,执行概率越大)
		// 可以使用Thread所提供的静态常量来设置线程的优先级
		t1.setPriority(Thread.MAX_PRIORITY);//设置最大优先级
		// t1.setPriority(Thread.MIN_PRIORITY);//设置最小优先级
		// t1.setPriority(2);//亲自测试貌似也可以设置常数
		System.out.println(t1.getPriority());//打印线程优先级
		t1.start();
		
		Thread t2 = new Thread(new Runnable(){
			public void run(){
				
				for(int i = 1; i < 100; i++){
					System.out.println("t2-->" + i);
				}
				
			}
		});

		t2.start();
		
	}
}

用上面程序可以得到的结果,大概就是t1比t2运行靠前,更多一点。抢到CPU的概率概率大了。反正最好亲手写写程序和看看视频,收获会很多。


线程的同步

多线程数据安全

同步线程的方法

线程同步错误,多个线程公用一份数据的时候,就会出错误,共同抢100张火车票问题。

class MyThread implements Runnable{
	int i = 100;
	public void run(){
		while(true){
			//Thread.currentThread()获取当前线程 getName()当前线程的名字
			System.out.println(Thread.currentThread().getName() + i);
			i--;
			//让出cpu,让所有线程重新竞争cpu使用权(在这,为了更容易出现想要的错误而写的)
			Thread.yield();
			if(i < 0){
				break;
			}
		}
	}
}

class Test{
	public static void main(String args []){
		MyThread mt = new MyThread();
		//生成2个Thread对象,但是两个Thread对象公用同一个线程体对象
		//等于t1和t2同时在用MyThread(就和大家一起抢火车票一样)
		Thread t1 = new Thread(mt);
		Thread t2 = new Thread(mt);
		
		t1.setName("t1线程");
		t2.setName("t2线程");
		
		t1.start();
		t2.start();
	}
}
运行结果就呵呵哒了,t1和t2同时用了100.这个就是同步线程安全问题。



如何解决同步线程安全问题?

synchronized(this){...} 

利用同步锁,谁用了就先占用,别人不能用。
线程锁作用:当一个线程在执行某一段代码块的时候,其他线程不能执行该段代码块。
这样就保证了我们数据的安全。

先将上面两个线程,可能会同时执行的代码块,用synchronized包裹起来,当一个线程执行代码块里面的代码的时候,其他线程必须等该线程执行完成才可以执行(就算抢到了cpu,也得让出来)

class MyThread implements Runnable{
	int i = 100;
	public void run(){
		while(true){
			synchronized(this){
				//Thread.currentThread()获取当前线程 getName()当前线程的名字
				System.out.println(Thread.currentThread().getName() + i);
				i--;
				//让出cpu,让所有线程重新竞争cpu使用权(在这,为了更容易出现想要的错误而写的)
				Thread.yield();
				if(i < 1){
					break;
				}
			}
		}
	}
}

但是上面运行后,末尾会出现一个0


如何解决?

深入synchronized关键字

很多人认为这个锁,是锁住了代码,是大错特错的。








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一休日记

csdn打赏扣费20点,这

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值