java线程

进程与线程

进程

  • 进程就是内存中运行的程序
  • 并发:并发就是同一时间段cpu执行多个进程,执行完一个在执行下一个
  • 并行:并行就是同一时刻cpu执行多个进程

线程

线程是进程的一部分,是程序进入cpu的一条路径,一个进程中可以并发多个线程,每条线程并行执行不同的任务,多线程值得就是就是在同一时刻能够执行多个线程,单线程指的是同一时刻只能执行一个线程,线程示例如下:

- - 守护线程 守护线程就是一类类似于垃圾回收机制的线程,因为进程会在所有线程结束后才消失,但是垃圾回收机制线程不会结束就会导致进程也不会结束,所以需要将它们标志了守护线程,Java 虚拟机会在所有非守护线程终止后结束进程。守护线程地创建如下:
1
2
3
Thread thread = new WatchingThread();
thread.setDaemon(true);
thread.start();

线程调度和线程状态

线程调度值的是cpu执行线程的实际情况,有以下两种:

  • 分时调度
    分时调度是线程轮流平均使用cpu,每个线程占用cpu时间相同
  • 抢占式调度
    抢占式调度是按照线程的优先级,来决定cpu先执行哪个进程,如果优先级相同则会随机选择,即线程随机性

线程状态
每个Java程序都有一个缺省的主线程,Application的主线程是main()方法的执行过程;Applet的主线程是通过浏览器加载并执行java小程序。Java使用Thread类及其子类表示线程5中状态:
新建(Newborn)->就绪(Runnable)->运行(Running)->阻塞(Blocked)->死亡(Dead)

线程实现

通过继承Thread类来创建并启动多线程:

  • 定义Thread类的子类并重载线程运行体run() 方法,run()方法中定义线程需要完成的任务。
  • 创建 Thread 子类的实例,即创建了线程对象。
  • 使用线程对象的 start() 方法来启动该线程。
    e.g.示例,该示例将会不停地输出两个线程地名字:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    //继承Thread类重写run方法
    package TreadTest;

    public class MyThread extends Thread{
    @Override
    public void run() {
    while(true) System.out.println(currentThread().getName()+"运行");
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //创建多线程
    package TreadTest;

    public class TreadTets {
    public static void main(String[] args) {
    MyThread myThread1 = new MyThread();
    myThread1.setName("thread_1");
    MyThread myThread2 = new MyThread();
    myThread2.setName("thread_2");
    myThread1.start();
    myThread2.start();
    }
    }

    使用多线程复制文件

    在Thread子类重写run方法,在其中定义复制文件即可
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    //创建Thread子类
    package TreadTest;

    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;

    public class MyThread extends Thread {
    public MyThread(String source,String target){
    this.souce = source;
    this.target = target;
    }
    private String souce;
    private String target;

    @Override
    public void run() {
    try {
    FileInputStream fis = new FileInputStream(souce);
    FileOutputStream fos = new FileOutputStream(target);
    byte b[] = new byte[1024];
    int len;
    while ((len = fis.read()) != -1) {
    fos.write(b, 0, len);
    }
    fis.close();
    fos.close();
    }catch (IOException e){
    System.out.println("复制失败");
    }
    }
    }



    //创建多线程
    package TreadTest;

    public class CopyThreadTest {
    public static void main(String[] args) {
    for(int i=1;i<=3;i++){
    String source = "D:/javaTestFile/"+ i + ".txt";
    String target = "D:/javaTestFile/"+ i + "(" + i + ")" + ".txt";
    MyThread myThread = new MyThread(source,target);
    myThread.start();
    }
    }
    }

    ```
    结果如下,多线程创建成功
    <div align=center><img src="https://cdn.jsdelivr.net/gh/sunyuzero/cdn/java/java39.png" width=600></div>

    ### 通过实现Runable接口实现多线程
    这种方法与与继承Thread类差不多,编写一个类实现 Runnable 接口,并实现该接口中的 run() 方法。然后创建 Runable 实现类的实例,并以此实例作为 Thread 的 target 来创建 Thread 对象,示例如下:
    ```java
    //实现runable接口
    package TreadTest;

    public class MyThread_1 implements Runnable{
    public void run(){
    while(true){
    System.out.println("线程:"+ Thread.currentThread().getName()+"运行!");
    }
    }

    }


    //创建多线程
    package TreadTest;

    public class Test_1 {
    public static void main(String[] args) {
    MyThread_1 target = new MyThread_1();
    Thread thread1 = new Thread(target);
    thread1.setName("线程——1");
    Thread thread2 = new Thread(target);
    thread1.setName("线程——2");
    thread1.start();
    thread2.start();
    }
    }

    两种创建多线程方式比较

    相比来说,实现Runanle接口更好:
  • 适合多个相同程序代码的线程去处理同一资源的情况,把线程同程序代码、数据分离较好地体现了面向对象的设计思想。
  • 可以避免由于 Java 的单继承特性带来的局限。
  • 有利于程序的健壮性,代码能够被多个线程共享。

同步synchronized

为了解决某些因为数据同步调用而产生地线程不安全问题,我们需要使用监视器实现线程同步,线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。监视器的原理如下::

Synchronized能够实现同步的原因是: - 当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例有关的锁。 - 当程序运行到synchronized同步方法或代码块时才该对象锁才起作用。 - 一个对象只有一个锁。所以,如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放(或返回)锁。这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放,即该线程退出了synchronized同步方法或代码块。

Synchronized与static类似,Synchronized代码块和Synchronized方法作用相同,形式不同,作用范围不同:
e.g.示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//Synchronized代码块
package TreadTest;

public class TicketThread implements Runnable{
private int count = 10;
public void run(){
while(true){
synchronized (this){
if (count > 0) {
try {Thread.sleep(1000);}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+"窗口卖出了第"+count--+"张票");
}
else {
System.out.println("票已经售完了!");
break;
}
}
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//Synchronized方法
package TreadTest;

public class TicketThread implements Runnable{
private int count = 10;
public synchronized void run(){
while(true){
if (count > 0) {
try {Thread.sleep(1000);}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+"窗口卖出了第"+count--+"张票");
}
else {
System.out.println("票已经售完了!");
break;
}
}
}
}

定时器

java提供了Timer计时器类,可以方便我们在指定时间执行任务,常用的用法如下:

  • timer.schedule(TimerTaskImpl tti,long ms) //延时ms毫秒执行任务
  • timer.schedule(TimerTaskImpl tti,long ms1,long ms2) //延时ms1毫秒后每隔ms2毫秒执行任务
  • timer.schedule(TimerTaskImpl tti,new Date(),long ms2) //每隔ms2毫秒后执行任务
    一般我们需要继承TimerTask并重写run方法,在通过Timer定时器调用,示例如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    //继承TimerTask并重写run方法
    package TreadTest;

    import java.util.Timer;
    import java.util.TimerTask;

    public class TimeTask extends TimerTask{
    public void run(){}
    }



    //实现定时器
    package TreadTest;

    import java.util.Date;
    import java.util.Timer;

    public class TimeTest {
    public static void main(String[] args) {
    Timer timer = new Timer();
    //延时定时器
    timer.schedule(new TimeTask(){
    @Override
    public void run() {
    System.out.println("延时5s");;
    }
    },2000);
    //延时后定期执行定时器
    timer.schedule(new TimeTask(){
    @Override
    public void run() {
    System.out.println("一秒后执行任务,之后每隔一秒执行一次");
    }
    },2000,1000);
    //定期执行定时器
    timer.schedule(new TimeTask(){
    @Override
    public void run() {
    System.out.println("每隔3s执行一次");
    }
    },new Date(),3000);
    }
    }
    部分运行结果如下: