java语言之线程
 
 
一、进程和线程
 
(1)进程:程序是静止的,只有真正运行时的程序,才被称为进程。
 
(2)线程:又称轻量级进程。
 
 
 程序中的一个顺序控制流程,同时也是CPU的基本调度单位。 进程由多个线程组成,彼此间完成不同的工作,交替执行,称为多线程。
 
 
 
二、创建线程
 
 
 创建线程的2种方式:
 
 继承Thread类。实现Runnable接口。 
 
(1)继承Thread类
 
步骤:
 
1)自定义线程类继承Thread类,重写 run() 方法;
 
2)在需要开启线程的地方,实例化自定义线程类;
 
3)通过对象调用 start() 开启线程。
 
案例演示:
 
public class Demo02 {
    public static void main(String
[] args
) {
    	
        MyThread myThread 
= new MyThread();
        
        
        myThread
.start();
        for (int i 
= 0; i 
< 1000; i
++) {
            System
.out
.println("主线程:" + i
);
        }
    }
}
class MyThread extends Thread{
    @Override
    public void run() {
        for (int i 
= 0; i 
< 1000; i
++) {
            System
.out
.println("子线程:" + i
);
        }
    }
}
 
(2)实现Runnable接口
 
步骤:
 
1)自定义任务类实现Runnable接口,重写 run() 方法;
 
2)在需要开启线程的地方,实例化任务类;
 
3)实例化线程类的同时传递任务类的对象;
 
4)通过线程类的对象调用 start() 开启线程。
 
public class Demo02 {
    public static void main(String
[] args
) {
    	
        MyThread myThread 
= new MyThread();
        
        Thread thread 
= new Thread(myThread
);
        
        
        thread
.start();
        for (int i 
= 0; i 
< 1000; i
++) {
            System
.out
.println("主线程:" + i
);
        }
    }
}
class MyThread implements Runnable{
    @Override
    public void run() {
        for (int i 
= 0; i 
< 1000; i
++) {
            System
.out
.println("子线程:" + i
);
        }
    }
}
 
(3)课堂案例:10个窗口共同销售1000张票,票销售完毕则提示已售罄
 
public class Main {
    public static void main(String
[] args
) {
        
        for (int i 
= 1; i 
<= 10; i
++) {
            new Thread(new Ticketsales("窗口" + i
)).start();
        }
    }
}
class Ticketsales implements Runnable{
	
    static int num 
= 1;
    String name
;
    public Ticketsales(String name
){
        this.name 
= name
;
    }
    @Override
    public void run() {
        while (true) {
            if (num 
<= 1000) {
                System
.out
.println(name 
+ ":卖出第" + num
++ + "张票");
            } else {
                System
.out
.println(name 
+ ":1000张票卖完了!");
                break;
            }
        }
    }
}
 
 
三、线程安全
 
(1)同步锁:synchronized
 
 
 注:使用 synchronize 容易出现死锁
 
 
1)同步代码块
 
 
 语法: synchronized( 临界资源对象 ){ // 对临界资源对象加锁 // 代码( 原子操作 ) }
 
 
 
 执行过程: 在同一时间使用同步锁锁住的代码块,当监视器为同一对象时,每次只允许一条线程进入同步代码块访问资源,其他相同监视器对象的线程则无法进入,只有等待执行同步代码块的线程释放资源,退出同步代码块后,其他线程才可以获取cpu的资源。
 
 
public class Main {
    public static void main(String
[] args
) {
        
        for (int i 
= 1; i 
<= 10; i
++) {
            new Thread(new Ticketsales("窗口" + i
)).start();
        }
    }
}
class Ticketsales implements Runnable{
    
    static int num 
= 1;
    String name
;
    public Ticketsales(String name
){
        this.name 
= name
;
    }
    @Override
    public void run() {
        while (true) {
            try {
                
                Thread
.sleep(1);
            } catch (InterruptedException e
) {
                e
.printStackTrace();
            }
            
            synchronized ("") {
                if (num 
<= 1000) {
                    System
.out
.println(name 
+ ":卖出第" + num
++ + "张票");
                } else {
                    System
.out
.println(name 
+ ":1000张票卖完了!");
                    break;
                }
            }
        }
    }
}
 
2)同步方法
 
 
 语法: synchronized 返回值类型 方法名称( 形参列表 ){ // 对当前对象(this)加锁 // 代码(原子操作) }
 
 
 
 执行过程: 被 synchronized 修饰的方法为同步方法,不需要手动添加监视器监视器为方法本身,使用同步方法必须方法为同一个或者是对象为同一个使用同步锁的时候,如果同步锁里调用了 sleep() ,线程的资源不会被释放如果调用的是 wait() 使线程进入等待(阻塞状态),线程资源会被释放。
 
 
public class Main {
    public static void main(String
[] args
) {
        
        for (int i 
= 1; i 
<= 10; i
++) {
            new Thread(new Ticketsales("窗口" + i
)).start();
        }
    }
}
class Ticketsales implements Runnable{
    
    static int num 
= 1;
    String name
;
    public Ticketsales(String name
){
        this.name 
= name
;
    }
    @Override
    public void run() {
        Ticket();
    }
    
    public synchronized static void Ticket(){
        while (true) {
            try {
                
                Thread
.sleep(1);
            } catch (InterruptedException e
) {
                e
.printStackTrace();
            }
            if (num 
<= 1000) {
                System
.out
.println(name 
+ ":卖出第" + num
++ + "张票");
            } else {
                System
.out
.println(name 
+ ":1000张票卖完了!");
                break;
            }
        }
    }
}
 
(2)互斥锁:Lock
 
 
 Lock 是一个接口,需要显式地获取锁和释放锁,不容易出现死锁
 
 
public class Main {
    public static void main(String
[] args
) {
        
        for (int i 
= 1; i 
<= 10; i
++) {
            new Thread(new Ticketsales("窗口" + i
)).start();
        }
    }
}
class Ticketsales implements Runnable{
    
    static int num 
= 1;
    String name
;
    
    Lock lock 
= new ReentrantLock();
    public Ticketsales(String name
){
        this.name 
= name
;
    }
    @Override
    public void run() {
        while (true) {
            try {
                
                Thread
.sleep(1);
            } catch (InterruptedException e
) {
                e
.printStackTrace();
            }
            
            lock
.lock();
            if (num 
<= 1000) {
                System
.out
.println(name 
+ ":卖出第" + num
++ + "张票");
            } else {
                System
.out
.println(name 
+ ":1000张票卖完了!");
                break;
            }
            
            lock
.unlock();
        }
    }
}
 
 
四、守护线程
 
 
 概念: 当一条线程在其他线程中创建并设置为守护线程,被守护的线程一旦结束,守护线程跟着销毁,垃圾回收机制也是一个守护线程。
 
 
public class Main {
    public static void main(String
[] args
) throws InterruptedException 
{
        MyThread thread 
= new MyThread();
        
        
        thread
.setDaemon(true);
        thread
.start();
        Thread
.sleep(1);
        for (int i 
= 0; i 
<= 100; i
++) {
            System
.out
.println("主线程:"+i
);
        }
    }
}
class MyThread extends Thread{
    @Override
    public void run() {
        for (int i 
= 0; i 
<= 1000; i
++) {
            System
.out
.println("守护线程:"+i
);
        }
    }
}
 
 
五、线程池
 
 
 什么是线程池: 就是事先创建若干个可执行的线程放入一个池中,需要的时候就从池中获取线程不用自行创建,使用完毕不用销毁线程而是放回池中,从而减少创建和销毁线程对象的开销。
 
 
 
 线程池的作用: 使用线程池管理线程可以最大程度的利用线程,减少线程的创建和销毁,从而节省资源消耗,它通过利用已有的线程多次循环执行多个任务从而提高系统的处理能力。
 
 
 
 创建线程池的步骤: 1、创建线程池对象; 2、创建任务类实现Runnable接口; 3、通过线程池对象提交任务类对象; 4、关闭线程池。
 
 
(1)newSingleThreadExecutor,创建一个包含单条线程的线程池
 
public class Main {
    public static void main(String
[] args
) {
        
        ExecutorService pool 
= Executors
.newSingleThreadExecutor();
        
        Task task 
= new Task();
        for (int i 
= 0; i 
< 10; i
++) {
            
            pool
.submit(task
);
        }
        
        pool
.shutdown();
    }
}
class Task implements Runnable{
    @Override
    public void run() {
        try {
        	
            Thread
.sleep(5);
        } catch (InterruptedException e
) {
            e
.printStackTrace();
        }
        for (int i 
= 0; i 
< 1000; i
++) {
            System
.out
.println(Thread
.currentThread().getName()+":"+i
);
        }
    }
}
 
(2)创建一个可复用并固定数量线程的线程池
 
public class Main {
    public static void main(String
[] args
) {
        
        ExecutorService pool 
= Executors
.newFixedThreadPool(3);
        
        Task task 
= new Task();
        for (int i 
= 0; i 
< 10; i
++) {
            
            pool
.submit(task
);
        }
        
        pool
.shutdown();
    }
}
class Task implements Runnable{
    @Override
    public void run() {
        try {
        	
            Thread
.sleep(5);
        } catch (InterruptedException e
) {
            e
.printStackTrace();
        }
        for (int i 
= 0; i 
< 1000; i
++) {
            System
.out
.println(Thread
.currentThread().getName()+":"+i
);
        }
    }
}
 
(3)创建一个线程数量可变的线程池
 
public class Main {
    public static void main(String
[] args
) {
        
        ExecutorService pool 
= Executors
.newCachedThreadPool();
        Task task 
= new Task();
        for (int i 
= 0; i 
< 10; i
++) {
            pool
.submit(task
);
        }
        pool
.shutdown();
    }
}
class Task implements Runnable{
    @Override
    public void run() {
        try {
        	
            Thread
.sleep(5);
        } catch (InterruptedException e
) {
            e
.printStackTrace();
        }
        for (int i 
= 0; i 
< 1000; i
++) {
            System
.out
.println(Thread
.currentThread().getName()+":"+i
);
        }
    }
}
 
 
五、线程的合并
 
概念:运行中的线程遇到其他线程调用join(),会使当前线程进入等待状态,等待调用join()的线程运行结束,等待中的线程会结束等待状态。
 
public class Demo01 {
    public static void main(String
[] args
) {
        ThreadA threadA 
= new ThreadA();
        threadA
.start();
        new ThreadB(threadA
).start();
    }
}
class ThreadA extends Thread{
    @Override
    public void run() {
        int i 
= 1;
        while (i
<=100){
            try {
                Thread
.sleep(1);
            } catch (InterruptedException e
) {
                e
.printStackTrace();
            }
            System
.out
.println("线程A:"+i
++);
        }
    }
}
class ThreadB extends Thread{
    Thread thread
;
    public ThreadB(Thread thread
){
        this.thread 
= thread
;
    }
    @Override
    public void run() {
        int i 
= 1;
        while (i
<=200){
            try {
                Thread
.sleep(1);
            } catch (InterruptedException e
) {
                e
.printStackTrace();
            }
            
            if(i 
== 50){
                try {
                    thread
.join();
                } catch (InterruptedException e
) {
                    e
.printStackTrace();
                }
            }
            System
.out
.println("线程B:"+i
++);
        }
    }
}
 
 
六、线程的中断
 
概念:在线程中是没有可供调用的方法来结束线程的,只能改变线程的中断状态,然后在run方法中对中断状态进行处理;当线程处于阻塞状态时,无法改变线程的中断状态。
 
public class Demo01 {
    public static void main(String
[] args
) {
        ThreadB threadB 
= new ThreadB();
        threadB
.start();
        new ThreadA(threadB
).start();
    }
}
class ThreadA extends Thread{
    private Thread thread
;
    public ThreadA(Thread thread
){
        this.thread 
= thread
;
    }
    @Override
    public void run() {
        for (int i 
= 1; i 
<= 100; i
++) {
            System
.out
.println("线程A:"+i
);
            if(i 
== 50){
                
                thread
.interrupt();
            }
        }
    }
}
class ThreadB extends Thread{
    @Override
    public void run() {
        for (int i 
= 1; i 
<= 200; i
++) {
            
            if(isInterrupted()){
                return;
            }
            System
.out
.println("线程B:"+i
);
        }
    }
}
 
 
七、线程的优先级
 
概念:使用setPriority()方法设置线程的优先级别,但不是绝对优先,设置后不影响CPU的调度。
 
public class Demo01 {
    public static void main(String
[] args
) {
        MyThread thread1 
= new MyThread("线程A");
        MyThread thread2 
= new MyThread("线程B");
        
        thread1
.setPriority(Thread
.MIN_PRIORITY
);
        
        thread2
.setPriority(Thread
.MAX_PRIORITY
);
        thread1
.start();
        thread2
.start();
    }
}
class MyThread extends Thread{
    public MyThread(String name
){
        super.setName(name
);
    }
    @Override
    public void run() {
        for (int i 
= 0; i 
< 100; i
++) {
            System
.out
.println(super.getName()+":"+i
);
        }
    }
}
 
 
八、线程的礼让
 
概念:使用yield()方法后退出运行状态,回到就绪状态,与其他线程一起等待 CPU 的调度。
 
public class Demo01 {
    public static void main(String
[] args
) {
        new MyThread1().start();
        new MyThread2().start();
    }
}
class MyThread1 extends Thread{
    @Override
    public void run() {
        for (int i 
= 1; i 
<= 100; i
++) {
            try {
                Thread
.sleep(1);
            } catch (InterruptedException e
) {
                e
.printStackTrace();
            }
            if(i 
>=100){
                
                Thread
.yield();
            }
            System
.out
.println("线程A:"+i
);
        }
    }
}
class MyThread2 extends Thread{
    @Override
    public void run() {
        for (int i 
= 1; i 
<= 100; i
++) {
            try {
                Thread
.sleep(1);
            } catch (InterruptedException e
) {
                e
.printStackTrace();
            }
            System
.out
.println("线程B:"+i
);
        }
    }
}
 
 
九、线程安全的集合
 
 注:绿色代表新增知识,下划线代表线程安全集合。
 
案例演示:CopyOnWriteArrayList
 
 
 线程安全的ArrayList,加强版读写分离。 写有锁,读无锁,读写之间不阻塞,优于读写锁。 写入时,先copy一个容器副本、再添加新元素,最后替换引用。 使用方式与ArrayList无异。
 
 
public class Demo01 {
    public static void main(String
[] args
) {
        
        
        List
<String> list 
= new CopyOnWriteArrayList<>();
        list
.add("a");
        list
.add("b");
        list
.add("c");
        for (String str
:list
) {
            list
.add("1");
            System
.out
.println(str
);
        }
        System
.out
.println("--------------");
        for (String str
:list
) {
            System
.out
.println(str
);
        }
    }
}