JAVAOOP-18 线程

JAVAOOP-18 线程

思考: 如何是 实现一边播放歌曲,同时调节音量

public class Test6 {

	public static void main(String[] args) {
		play();

	}
	//实现一边播放歌曲,同时调节音量
	public static void play(){
		//播放歌曲
		for (int i = 1; i <=20; i++) {
			System.out.print(i+" ");
		}
		//调节音量
		System.out.println();
		for (char c= 'a'; c<='z'; c++) {
			System.out.print(c+" ");
		}
	}
}
//程序是顺序执行的,不能同时进行,传统的方法不能解决,用线程

程序是顺序执行的,不能同时进行,传统的方法不能解决,用线程

一. 线程进程概念

进程:进程指正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能

线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。

什么是多线程呢?即就是一个程序中有多个线程在同时执行。

进程与线程的区别

\1. 线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位;

\2. 一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线

\3. 进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段,数据集,堆等)及一些进程级的资源(如打开文件和信

号等),某进程内的线程在其他进程不可见;

\4. 调度和切换:线程上下文切换比进程上下文切换要快得多

二. 多线程实现方式

Thread类:

构造方法说明
Thread()分配新的 Thread 对象。
Thread(Runnable target)分配新的 Thread 对象。
Thread(String name)分配新的 Thread 对象。
普通方法说明
static Thread currentThread()返回对当前正在执行的线程对象的引用。
long getId()返回该线程的标识符。
String getName()返回该线程的名称。
int getPriority()返回线程的优先级。
Thread.State getState()返回该线程的状态。
static boolean interrupted()测试当前线程是否已经中断。
boolean isAlive()测试线程是否处于活动状态。
boolean isDaemon()测试该线程是否为守护线程。
boolean isInterrupted()测试线程是否已经中断。
void join()等待该线程终止。
void join(long millis)等待该线程终止的时间最长为 millis 毫秒。
void run()如果该线程是使用独立的 Runnable 运行对象构造的,
则调用该 Runnable 对象的 run 方法;
否则,该方法不执行任何操作并返回。
void setDaemon(boolean on)将该线程标记为守护线程或用户线程。
void setName(String name)改变线程名称,使之与参数 name 相同。
void setPriority(int newPriority)更改线程的优先级。
static void sleep(long millis)在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),
此操作受到系统计时器和调度程序精度和准确性的影响。
void start()使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
String toString()返回该线程的字符串表示形式,包括线程名称、优先级和线程组。

创建线程方式一继承Thread类

创建线程的步骤:

1 定义一个类继承Thread。

2 重写run方法。

3 创建子类对象,就是创建线程对象。

4 调用start方法,开启线程并让线程执行,同时还会告诉jvm去调用run方法。

创建线程方式二实现Runnable接口

创建线程的另一种方法是声明实现 Runnable 接口的类。该类然后实现 run 方法。然后创建Runnable的子类对象,传入到某个线程的构造方法中,开启线程。

创建线程的步骤:

1 定义一个类实现Runnable接口。

2 重写run方法。

3 创建子类对象,就是创建线程对象。

4 调用start方法,开启线程并让线程执行,同时还会告诉jvm去调用run方法。

方法:

void run()
​ 使用实现接口 Runnable 的对象创建一个线程时,启动该线程将导致在独立执行的线程中调用对象的 run 方法。

l 测试类

public class Test7 {

	public static void main(String[] args) {
		//启动新线程专门播放歌曲
		PlayMusic pm=new PlayMusic();
		pm.start();
		//启动新线程来调节音量
		Voice v=new Voice();
	    new	Thread(v).start();//实现Runnable接口的线程有参的构造方法

	}

}

//创建线程方式一继承Thread类
class PlayMusic extends Thread{
	//重写Thread里的run方法
	public void run(){
		for (int i = 1; i <=20; i++) {
			System.out.print(i+" ");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
}

//创建线程方式二实现Runnable接口
class Voice implements Runnable{
	//重写接口里的run方法
	@Override
	public void run() {
		for (char i = 'a'; i <='z'; i++) {
			System.out.print(i+" ");
			try {
				Thread.sleep(700);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
	}
	
}

思考:线程对象调用 run方法和调用start方法区别?

线程对象调用run方法不开启线程。仅是对象调用方法。线程对象调用start开启线程,并让jvm调用run方法在开启的线程中执行。

三.线程常用方法

常用方法说明
static Thread currentThread()返回对当前正在执行的线程对象的引用。
long getId()返回该线程的标识符。
String getName()返回该线程的名称。
void setName(String name)改变线程名称,使之与参数 name 相同。
public class Test8 {

	public static void main(String[] args) {
		ThreadA t=new ThreadA();
		t.setName("猫线程");
		t.start();

	}

}

class ThreadA extends Thread{
	public void run(){
		System.out.println("该线程的名称是: "+Thread.currentThread().getName()+"Id是: "+Thread.currentThread().getId());
		System.out.println("优先级是: "+Thread.currentThread().getPriority());
	
	}
}
java.lang.Thread类里的常量
public static final int MAX_PRIORITY10
public static final int MIN_PRIORITY1
public static final int NORM_PRIORITY5

优先级1-10 最大优先级最先执行

优先级方法说明
void setPriority(int newPriority)更改线程的优先级。

public class Test9 {

	public static void main(String[] args) {
		ThreadB t1=new ThreadB();
		t1.setName("猫");
		t1.setPriority(Thread.MIN_PRIORITY);//1
		t1.start();
		
		ThreadB t2=new ThreadB();
		t2.setName("狗");
		t2.setPriority(Thread.MAX_PRIORITY);//10
		t2.start();
		
		ThreadB t3=new ThreadB();
		t3.setName("猪");
		t3.setPriority(Thread.NORM_PRIORITY);//5
		t3.start();
		

	}

}
class ThreadB extends Thread{
	public void run(){
		for (int i = 0; i <=5; i++) {
			System.out.println(Thread.currentThread().getName()+" : "+i);
		}
	}
}
线程休眠方法说明
static void sleep(long millis)在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。

public class Test10 {

	public static void main(String[] args) {
		System.out.println("1");
		try {
			Thread.currentThread().sleep(2000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("2");

	}

}

四. 线程同步机制

由于同一进程内的多个线程共享内存空间,在Java中,就是共享实例,当多个线程试图同时修改某个实例的内容时,就会造成冲突,因此,线程必须实现共享互斥,使多线程同步。

最简单的同步是将一个方法标记为synchronized,对同一个实例来说,任一时刻只能有一个synchronized方法在执行。

synchronized 是用来处理多个线程访问同一个代码块或者是方法,或者是一个类。

  • 方法锁:

    每个类的对象对应一个锁,当对象中的某个方法被synchronized修饰后,调用该方法的时候必须获得该对象的“锁”
    。该方法一旦执行就会占有该锁,别的线程使用该对象调用这个方法的时候就会被阻塞直到这个方法执行完后释放锁,被阻塞的线程才能获得锁,从而进入执行状态。

    这种机制确保了在同一时刻,对于每一个对象的实例,其所有声明为synchronized方法中最多只有一个处于可执行状态。

    1. 当使用同一个对象调用加了synchronized修饰的方法,如下所示。
public class TestLock {
    public static void main(String[] args) {
        TestLock l=new TestLock();
        //l表示对象 此时一个锁

        new Thread(){
            @Override
            public void run() {
                l.method1();
            }
        }.start();

        new Thread(){
            @Override
            public void run() {
                l.method2();
            }
        }.start();





    }
    public synchronized void method1(){
        System.out.println("method1------开始");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("method1------结束");
    }

    public synchronized void method2(){
        System.out.println("method2------开始");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("method2------结束");
    }
}

运行结果:

method1------开始
method1------结束
method2------开始
method2------结束
  1. 当使用不同的对象调用加了synchronized修饰的方法,如下所示。
public class TestLock2 {
    public static void main(String[] args) {
        TestLock2 l1=new TestLock2();
        TestLock2 l2=new TestLock2();
        //l1 l2表示对象 此时两个锁 各自访问各自的

        new Thread(){
            @Override
            public void run() {
                l1.method1();
            }
        }.start();

        new Thread(){
            @Override
            public void run() {
                l2.method2();
            }
        }.start();

    }
    public synchronized void method1(){
        System.out.println("method1------开始");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("method1------结束");
    }

    public synchronized void method2(){
        System.out.println("method2------开始");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("method2------结束");
    }
}

运行结果:

method1------开始
method2------开始
method1------结束
method2------结束

由结果可以得出:使用不同的对象调用这两个方法,可以看到这两个方法是同时执行的。因为调用方法的对象不同,所以两个方法获取到的锁也不同,结果就是这两个方法分别获取了这两个对象的锁。然后两个方法会同时执行。

  • 对象锁:

当一个对象中有synchronized方法或者sychronized代码块的时候。调用此对象方法的时就必须获取对象锁,如果此对象的锁已被别的线程获取,那么就必须等待别的线程释放锁后才可以 执行该方法。

public class TestLock3 {
    //方法锁和对象锁都差不多,方法锁针对的是一个方法。对象锁则是一个代码块,针对的是一部分代码。
    public static void main(String[] args) {
        TestLock3 l=new TestLock3();

        //一个对象锁 
        new Thread(){
            @Override
            public void run() {
                l.method1();
            }
        }.start();

        new Thread(){
            @Override
            public void run() {
                l.method2();
            }
        }.start();
    }
    public  void method1(){
        synchronized (this) {
            System.out.println("method1------开始");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("method1------结束");
        }
    }

    public  void method2(){
        synchronized (this) {
            System.out.println("method2------开始");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("method2------结束");
        }
    }
}

运行结果:

method1------开始
method1------结束
method2------开始
method2------结束

五.死锁现象

当两个线程互相等待对方释放锁的时候就会发生死锁
当出现死锁后,不会出现异常,不会出现提示,所有线程处于阻塞状态,无法继续
编程的时候要避免死锁的发生
解决方式: 按顺序访问;依靠线程间的通信

public class TestDeadLock {

	public static void main(String[] args) {
		new Thread1().start();
		new Thread2().start();

	}

}
class Lock{
	public static final Object lockA=new Object();
	public static final Object lockB=new Object();
}
class Thread1 extends Thread{
	public void run(){
		synchronized (Lock.lockA) {
			System.out.println("1");
			synchronized (Lock.lockB) {
				System.out.println("2");
			}
			
		}
	}
}
class Thread2 extends Thread{
	public void run(){
		synchronized (Lock.lockB) {
			System.out.println("a");
			synchronized (Lock.lockA) {
				System.out.println("b");
				
			}
			
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值