JAVA线程
笔者主要从以下三个角度,总结了对java线程基本内容的学习心得,希望对大家有所帮助。才疏学浅,如有纰漏,还望大家指出。
***1.什么是线程?
2.为什么要使用线程?
3.怎么实现线程?*
什么是线程?
不去细讲线程的定义,就拿一个形象的例子来比喻一下什么是线程。假设你现在只有要听写单词,听写单词需要用你的大脑控制你的耳朵去听、眼睛去看、手去写、大脑皮层去找单词的拼写,这个时候就相当于启动了多线程。只有这个“组合动作”帮助你的完成你的听写的整个过程。线程就是这样的存在,简单的说,就是多线并发操作。
官方一点的解释: 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。。
为什么要使用线程?
这个问题问的好,还拿上文那个例子来看,如果你只能进行单线程,那你就麻烦了,你只能控制你的手或者眼,不能同时工作,你甚至连听写单词都难以完成,这就体现了多线程的重要之处了,它之所以存在,之所以要用它,就是因为它很重要。
举个数据库的例子,比如说你现在要先写入一个数据,同时读出它,如果不使用线程,你就不能实现这个同步进行的操作,可见使用线程的重要性。
怎么实现线程?
方法一:继承Thread类
1.创建一个类继承Thread。
2.重写Thread中的run方法,这里注意,线程启动时运行的是run里面的代码,所以要把代码写在run方法里去。
3.创建子类对象,由于子类继承了Thread,所以就是在创建线程。
4.启动线程start()或者run(),这两个并无区别,第一个是相当于使用start方法来启动这个线程,其实就是取运行run中的代码,而run就是直接调用run方法的代码了。
/**
* All rights Reserved, Designed By DZF
* @Title: $P1
* @Description: $x使用继承实现线程
* @author: DZF
* @version V1.0
* @Copyright: $DZF. All rights reserved.
*/
//P1类继承了Thread类
class P1 extends Thread
{
public static void main(String args[])
{
P1 t= new P1(); //创建线程
t.start(); //使用start()启动线程
t.run(); //直接调用run()
System.out.println("主程序结束");
}
public void run()
// 覆盖run() 这是产生线程对象后就自动执行
{
System.out.println("线程"+getName()+"在运行");
}
}
注意,由于计算机运行速度的不同,可能出现顺序的print的顺序有所不同,这也是在下文会讲到的问题。同时这种方式的缺陷是线程任务和线程是绑定在一起的。
方法二:使用Runnable接口
1.创建实现了Runnable接口的子类
2.重写Runnable接口中的run方法
3.创建实现了Runnable接口的子类的对象
4.创建Thread类的对象,也就是在创建线程
5.把实现了Runnable接口的子类对象作为参数传递给Thread类的构造方法
/**
* All rights Reserved, Designed By DZF
* @Title: $P2
* @Description: $x使用接口实现线程
* @author: DZF
* @version V1.0
* @Copyright: $DZF. All rights reserved.
*/
class Runthread implements Runnable{
public void run() {
System.out.println("DZF最帅"); //在使用了Runnable接口的类中重写run方法
}
}
//P2将创建线程
public class P2{
public static void main(String[] args){
Runthread r = new Runthread(); //创建一个对象
Thread t = new Thread(r); //创建一个线程,把对象r传入其中,启动时运行r中的run方法
t.start();
System.out.println("没错,DZF就是最帅");
}
}
这个程序似乎并不能看出Runnable有啥好处,那我就再写一个代码来体现一波Runnable的优点吧。
假装我很自恋,想让DZF最帅输出四次,如果继承Thread需要写四个类,很划不来,不如用Runnable接口实现,只需把这个Runnable类的对象传进四个Thread就好了。
/**
* All rights Reserved, Designed By DZF
* @Title: $P2
* @Description: $x使用接口实现线程
* @author: DZF
* @version V1.0
* @Copyright: $DZF. All rights reserved.
*/
class Runthread implements Runnable{
public void run() {
System.out.println("DZF最帅"); //在使用了Runnable接口的类中重写run方法
}
}
//P2将创建线程
public class P2{
public static void main(String[] args){
Runthread r = new Runthread(); //创建一个对象
/* 我现在想运行四个线程输出DZF最帅
* 创建四个线程,把对象r传入其中,启动时运行r中的run方法*/
Thread t = new Thread(r);
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
Thread t3 = new Thread(r);
t.start();
t1.start();
t2.start();
t3.start();
}
}
这种方法,把线程任务进行了描述,也就是面向对象,从而实现了线程任务和线程对象的分离。线程执行什么任务不再重要,只要是实现了Runnable接口的子类对象都可以作为参数传递给Thread的构造方法,此方式很灵活,大大减少了代码量。还有一个好处是实现接口了,还不影响继承其他父类,不想extends Thread一样,重写父类的run方法那么简单粗暴。
为了更加深入的了解上述两种方法,下文将举一些例子
1.使用继承Thread构造器传线程名及线程的sleep
/**
* All rights Reserved, Designed By DZF
* @Title: $P3
* @Description: $x1.使用继承Thread构造器传线程名及线程的sleep
* @author: DZF
* @version V1.0
* @Copyright: $DZF. All rights reserved.
*/
class pThread extends Thread{
pThread(String str){
//super()特指父类构造器,将String对象传入父类构造器中
super(str);
}
public void run(){
//覆盖父类的run方法
for( int i = 1; i <= 3; i++ ){
System.out.println( getName() + "运行第" + i + "次" );
try{//在使用sleep时,需要使用try catch异常
sleep(2000);
}catch(InterruptedException e){}
System.out.println( getName() + "已结束" );
}
}
}
/*主类将test上述类*/
public class P3{
public static void main(String []args){
pThread t1 = new pThread( "线程1" );
pThread t2 = new pThread( "线程2" );
t1.start();
t2.start();
}
}
2.使用Runnable接口的功能
/**
* All rights Reserved, Designed By DZF
* @Title: $P3
* @Description: $x使用Runnable接口得功能
* @author: DZF
* @version V1.0
* @Copyright: $DZF. All rights reserved.
*/
class P4 implements Runnable{
public void run(){
//覆盖run(),这是产生线程对象后就自动执行
//由于使用Runnable接口,所以该类若想获取当前线程名,需要使用Thread.currentThread().getName()
System.out.println("线程"+Thread.currentThread().getName()+"在运行");
}
//主函数将实现上述的线程
public static void main(String args[]){
P4 my = new P4(); // 创建对象
Thread t= new Thread(my); // 为对象创建线程
t.start();
System.out.println("主程序结束");
}
}
3.线程的同步
我的目的是通过线程锁实现输出一个字母,然后输出一个数字,以此类推,这个时候就需要用synchronized实现了
/**
* All rights Reserved, Designed By DZF
* @Title: $P6
* @Description: $使用同步代码块进行线程同步
* @author: DZF
* @version V1.0
* @Copyright: $DZF. All rights reserved.
*/
class Share {
private int data;
private boolean available = false;//初始标记为false
/*使用synchronized标记的代码块实现同步方法*/
/*该方法判断数据是否写入,已写入则唤醒,否则继续锁死*/
public synchronized int get() {
/*使用该方法,会通过判断标记选择是被唤醒还是继续等待*/
while (available == false) {
//如果是false,则线程一直wait,相当于被锁死
try {
wait();
}
catch(Exception e1){}
}
//true的时候,进行唤醒操作,同时将标记还原false
available = false;
notifyAll();
return data;
}
/*该方法用于写入数据,同时改变标记*/
public synchronized void put( int value ) {
while (available == true) {
try {
wait();
}
catch(Exception e2){}
}
data = value;
available = true;
notifyAll();
}
}
class Dispn extends Thread{
private Share s1;
public Dispn(Share s)
{
s1 = s;
}
public void run(){
for(int i = 1 ; i <= 26; i++) {
System.out.print(i + " ");
s1.put(i);
try {
sleep( (int)(Math.random()*100));
}catch(Exception e1){}
}
}
}
class Dispch extends Thread{
private Share s2;
public Dispch(Share s2) {
this.s2 = s2;
}
public void run(){
char a = 'A';
for(int j = 0; j < 26; j++ ){
s2.get();
System.out.print((char)(a + j) + " ");
}
}
}
public class P6{
public static void main(String[] args){
Share s = new Share();
Dispn p = new Dispn(s);
Dispch c = new Dispch(s);
c.start();
p.start();
}
}