java线程之基础学习总结(二)
首先解释第一节中提到“不要调用Thread类或Runnable对象的run方法。直接调用run方法,只会执行同一个线程中的任务,而不会启动新线程。应该调用Thread.start()方法。这个方法将创建一个执行run方法的新线程。”因为run方法驱动的是main线程,不会启动其他新线程。但是如果使用start()方法就可以启动新线程,且默认优先级与main线程一样。都是Thread.NORM_PRIORITY=5。就可出现抢占资源的现象了!!!!!
实例:
- public class RunStart implements Runnable {
- public void run() {
- for (int i = 0; i < 5; i++) {
- System.out.println(Thread.currentThread().getName() + "线程正运行 i="+ i);
- }
- }
- }
编写TestRunStart类调用:
- public class TestRunStart {
- public static void main(String[] args) {
- RunStart runStart = new RunStart();
- Thread thread1 = new Thread(runStart, "thread2");
- Thread thread2 = new Thread(runStart, "thread2");
- thread1.run();
- thread2.run();
- }
- }
运行结果:
- main线程正运行 i=0
- main线程正运行 i=1
- main线程正运行 i=2
- main线程正运行 i=3
- main线程正运行 i=4
- main线程正运行 i=0
- main线程正运行 i=1
- main线程正运行 i=2
- main线程正运行 i=3
- main线程正运行 i=4
把TestRunStart类改一下如下:
- public class TestRunStart {
- public static void main(String[] args) {
- RunStart runStart = new RunStart();
- Thread thread1 = new Thread(runStart);
- Thread thread2 = new Thread(runStart);
- thread1.start();
- thread2.start();
- }
- }
运行结果:
- Thread-0线程正运行 i=0
- Thread-0线程正运行 i=1
- Thread-0线程正运行 i=2
- Thread-0线程正运行 i=3
- Thread-0线程正运行 i=4
- Thread-1线程正运行 i=0
- Thread-1线程正运行 i=1
- Thread-1线程正运行 i=2
- Thread-1线程正运行 i=3
- Thread-1线程正运行 i=4
就是系统默认的名字了!!!
线程优先级:
- public class PriorityTest {
- public static void main(String[] args) {
- System.out.println("Thread.MAX_PRIORITY : " + Thread.MAX_PRIORITY);
- System.out.println("Thread.NORM_PRIORITY: " + Thread.NORM_PRIORITY);
- System.out.println("Thread.MIN_PRIORITY : " + Thread.MIN_PRIORITY);
- }
- }
运行结果:
- Thread.MAX_PRIORITY : 10
- Thread.NORM_PRIORITY: 5
- Thread.MIN_PRIORITY : 1
看看main线程的优先级:
- public class RunStart implements Runnable {
- public void run() {
- switch (Thread.currentThread().getPriority()) {
- case 1:
- System.out.println(Thread.currentThread().getName()
- + "线程优先级:Thread.MIN_PRIORITY="
- + Thread.currentThread().getPriority());
- break;
- case 5:
- System.out.println(Thread.currentThread().getName()
- + "线程优先级:Thread.NORM_PRIORITY="
- + Thread.currentThread().getPriority());
- break;
- case 10:
- System.out.println(Thread.currentThread().getName()
- + "线程优先级:Thread.MAX_PRIORITY="
- + Thread.currentThread().getPriority());
- break;
- default:
- System.out
- .println(Thread.currentThread().getName() + "线程优先级:ERORR");
- break;
- }
- }
- }
调用:
- public class TestRunStart {
- public static void main(String[] args) {
- RunStart runStart = new RunStart();
- Thread thread1 = new Thread(runStart, "thread1");
- thread1.run();
- }
- }
运行结果:
- main线程优先级:Thread.NORM_PRIORITY=5
看看创建新的线程的默认优先级:
- public class TestRunStart {
- public static void main(String[] args) {
- RunStart runStart = new RunStart();
- Thread thread1 = new Thread(runStart, "singsong");
- thread1.start();
- }
- }
运行结果:
- singsong线程优先级:Thread.NORM_PRIORITY=5
在优先级相同的情况下,就出现了CPU资源的抢占现象了
从以上的运行结果可以知道main方法其实也是一个线程。在java中所有的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。
在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个jVM实习在就是在操作系统中启动了一个进程。
线程的中断
实例:
- public class SleepInterrupt implements Runnable {
- public void run() {
- System.out.println("1、进入run方法体线程正常启动!!");
- try {
- System.out.println("2、线程休眠3秒");
- Thread.sleep(3000);
- System.out.println("3、线程正常休眠3秒");
- } catch (InterruptedException e) {
- System.out.println("4、线程被中断了!!!");
- }
- System.out.println("5、正常结束run方法");
- System.out.println(Thread.currentThread().getName() + "线程正在运行");
- }
- }
TestSleepInterrupt类调用:
- public class TestSleepInterrupt {
- public static void main(String[] args) {
- SleepInterrupt sleepInterrupt = new SleepInterrupt();
- Thread thread = new Thread(sleepInterrupt, "singsong");
- thread.start();
- try {
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- thread.interrupt();
- }
- }
运行结果:
- 1、进入run方法体线程正常启动!!
- 2、线程休眠3秒
- 3、线程正常休眠3秒
- 5、正常结束run方法
- singsong线程正在运行
在启动线程时,要若不加此句“Thread.sleep(3000);”程序进入执行中断操作,
执行结果:
- 1、进入run方法体线程正常启动!!
- 2、线程休眠3秒
- 4、线程被中断了!!!
- 5、正常结束run方法
- singsong线程正在运行
执行线程睡眠操作,抛出中断异常,因此要有异常处理
public static void sleep(long millis, int nanos) throws InterruptedException
线程的强制执行jion()方法:
- public class JoinDemo implements Runnable {
- public void run() {
- for (int i = 0; i < 10; i++) {
- System.out.println(Thread.currentThread().getName() + i);
- }
- }
- public static void main(String[] args) {
- Thread thread = new Thread(new JoinDemo());
- thread.setName("singsong");
- thread.start();
- for (int i = 0; i < 10; i++) {
- if (i == 5) {
- try {
- thread.join();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- System.out.println(Thread.currentThread().getName() + i);
- }
- }
- }
运行结果:
- main0
- main1
- main2
- main3
- main4
- singsong0
- singsong1
- singsong2
- singsong3
- singsong4
- singsong5
- singsong6
- singsong7
- singsong8
- singsong9
- main5
- main6
- main7
- main8
- main9
从运行结果中:本程序启动了两个线程:一个是main线程,另一个是singsong线程
当main线程中循环执行到第五次时,就强制执行singsong线程。Join()方法就是用来强制某一线程的运行。
后台线程与setDaemon()方法
对java 程序来说,只要还有一个前台线程在运行,这个进程就不还结束,如果一个进程中只有后台线程在运行,这个进程就会结束。前台进程是相对后台线程而言的。如果某个线程对象在启动(调用start()方法)之前调用了setDaemon(true)方法,这个线程就变成了后台线程。
实例:
- public class SetNameDemo implements Runnable {
- public void run() {
- while (true) {
- System.out
- .println(Thread.currentThread().getName() + " is running");
- }
- }
- public static void main(String[] args) {
- Thread thread = new Thread(new SetNameDemo());
- thread.setName("singsong");
- thread.setDaemon(true);
- thread.start();
- }
- }
运行结果:
- singsong is running
- singsong is running
- singsong is running
- singsong is running
- singsong is running
从运行结果可以看到:虽然创建了一个无限循环的线程,因为它是后台线程,因此整个进程在主线程结束时就随之终止运行了。
同步与死锁
多个线程共享同一个资源时需要进行同步,但是过多的同步会造成死锁。
实例:
- public class Synchronized implements Runnable {
- private int ticket = 5;
- public void run() {
- for (int i = 0; i < 10; i++) {
- if (ticket > 0) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(Thread.currentThread().getName() + "在售出第"
- + ticket-- + "票");
- }
- }
- }
- }
SynchronizedTest类调用:
- public class SynchronizedTest {
- public static void main(String[] args) {
- Synchronized syn=new Synchronized();
- Thread thread =new Thread(syn ,"singsong");
- Thread thread1=new Thread(syn,"singsong1");
- thread.start();
- thread1.start();
- }
- }
运行结果:
- singsong1在售出第5票
- singsong在售出第4票
- singsong1在售出第3票
- singsong在售出第2票
- singsong1在售出第1票
- singsong在售出第0票
出现这个问题,是在程序中判断剩余票数与修改票数之间加入了睡眠语句(类似于网络不好,有延迟时)。
如果想要解决这样的问题,就必须使用同步,所谓的同步就是指多个操作在同一个时间段内只能有一个线程进行,其他线程要等待此线程完成之后才可以继续执行。
在java中可以通过同步代码的方式进行代码的加锁操作,通过加锁可以保证同一个时间段内只能有一个线程进行。同步的实现有两种方式。
同步代码块
语法格式:
synchronized(同步对象){//但是一般都把当前对象this作为同步对象。
//需要同步的代码
}
同步方法:
语法格式:
synchronized 方法返回类型方法名(参数列表){
// 其他代码
}
实例:
同步代码块
- public void run() {
- for (int i = 0; i < 10; i++) {
- synchronized (this) {//同步代码块
- if (ticket > 0) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(Thread.currentThread().getName()
- + "在售出第" + ticket-- + "票");
- }
- }
- }
- }
同步方法
- public class Synchronized implements Runnable {
- private int ticket = 5;
- public void run() {
- for (int i = 0; i < 10; i++) {
- sale();
- }
- }
- //同步方法sale()
- public synchronized void sale() {
- if (ticket > 0) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(Thread.currentThread().getName() + "在售出第"
- + ticket-- + "票");
- }
- }
- }
运行结果:
- singsong在售出第5票
- singsong在售出第4票
- singsong在售出第3票
- singsong在售出第2票
- singsong1在售出第1票