1.线程的基本知识及原理
1.1进程和线程
(1)进程概念:
正在运行的程序。进程是处于正在运行状态的程序,具备一定的独立功能。
(2)线程
是一个进程的一个执行单元,负责当前进程中程序的执行。
一个进程中至少有一个线程,一个进程可以是多个线程,这个应用程序因此被称为多线程程序。
简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程。
(3)线程的概念深入
什么是多线程?
即就是一个程序中有多个线程在同时执行。
一个核心的CPU在多个线程之间进行着随即切换动作,由于切换时间很短(毫秒甚至是纳秒级别),导致我们感觉不出来。
(4)java程序必有的线程:主线程Main
1.2线程执行的随机性/线程的组成
由于Cpu分时间片交替运行,宏观并行,微观串行。
线程的组成:
1.CPU OS(操作系统负责调度)
2.数据 堆空间共享,栈空间独立
3.代码 实现Runnable接口,将代码实现在run方法中
1.3实现线程的两种方法:
(1)继承Thread类
步骤:
1.继承Thread类
2.覆盖run()方法
3.创建Thread对象,调用Thread对象的start()方法
案例:
package exception.io.thread.alltest;
public class ThreadTest01 {
public static void main(String[] args) {
SubThread st01=new SubThread();
SubThread st02=new SubThread();
st01.start();
st02.start();
for (int i = 0; i < 100; i++) {
System.out.println("main run"+i);
}
}
}
class SubThread extends Thread{
public void run(){
for (int i = 0; i < 50; i++) {
System.out.println("subThread run"+i);
}
}
}
(2)实现Runnable接口
步骤:
1.实现Runnable接口 覆盖run()方法;
2.创建Runnable对象作为Thread的构造方法的实参;
3.创建Thread对象,开启线程start()。
案例:
package exception.io.thread.alltest;
public class ThreadImplementsRunnableTest01 {
public static void main(String[] args) {
Runnables r1=new Runnables();
Thread t1=new Thread(r1);
t1.start();
}
}
class Runnables implements Runnable{
public void run(){
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+"线程"+i);
}
}
}
(3)匿名内部类实现线程
前提:继承或接口实现
new 父类或者接口(){
重写抽象方法
}
案例:
public class ThreadDemo {
public static void main(String[] args) {
//继承方式 XXX extends Thread{ public void run(){}}
new Thread(){
public void run(){
System.out.println("!!!");
}
}.start();
//实现接口方式 XXX implements Runnable{ public void run(){}}
Runnable r = new Runnable(){
public void run(){
System.out.println("###");
}
};
new Thread(r).start();
new Thread(new Runnable(){
public void run(){
System.out.println("@@@");
}
}).start();
}
}
1.4线程的名称获取方法
//两种方式 一种常规 一种简便
//1.
Thread t=Thread.currentThread();
System.out.println(t.getName());
//2.
//获取现在正在执行的线程
System.out.println(Thread.currentThread().getName());
1.5线程的状态变化
1.6线程池原理及实现(SinceJDK1.5)
1.6.1线程池的作用:
1.用于共享线程资源,
2.避免线程重复创建和销毁,
3.同时控制并发上线。
1.6.2线程池的原理 :
1.在java中,如果每个请求到达就创建一个新线程,开销是相当大的。
2.在实际使用中,创建和销毁线程花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多。
3.除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。 如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。 为了防止资源不足,需要采取一些办法来限制任何给定时刻处理的请求数目,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有对象来进行服务。 线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快。另外,通过适当的调整线程中的线程数目可以防止出现资源不足的情况。
1.6.3线程池的实现:
package exception.io.thread.alltest;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolTest01 {
public static void main(String[] args) {
//调用工厂类的静态方法,创建线程池对象
//返回线程池对象 是返回的接口
ExecutorService es=Executors.newFixedThreadPool(10);
//调用线程的实现类对象es中的方法submit提交线程任务
//将实现runnable接口的实现类对象 传递给线程池
es.submit(new RunnableThreadPool());
es.submit(new RunnableThreadPool());
es.submit(new RunnableThreadPool());
}
}
class RunnableThreadPool implements Runnable{
public void run(){
System.out.println(Thread.currentThread().getName()+"线程提交任务");
}
}
创建固定大小的线程:
ExecutorService es = Executors.newFixedThreadPool(int n);
创建变动长度的线程池:
ExecutorService es = Executors.newCachedThreadPool();
1.6.4如何获取当前计算机的核心数?并行的线程数?
获得CPU的可用线程数:
Runtime.getRuntime().availableProcessors()
1.7实现线程的Callable接口方式(实现线程程序的第三种方式)
实现步骤:
1.工厂类 Executors静态方法newFixedThreadPool方法,创建线程池对象;
2. 线程池对象ExecutorService接口实现类,调用方法submit提交线程任务 submit(Callable c)。
案例:
package exception.io.thread.alltest;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class CallableThreadPoolTest01 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//创建线程池对象
ExecutorService es=Executors.newFixedThreadPool(10);
//提交线程的方法submit返回Future接口的实现类
Future<String> f=es.submit(new ThreadPoolCallable());
String s=f.get();
System.out.println(s);
es.shutdown();
}
}
class ThreadPoolCallable implements Callable<String>{
public String call() throws Exception {
// TODO Auto-generated method stub
return "abcd";
}
}
2.多线程操作面临的问题及解决方案
2.1线程操作共享数据的安全问题
当多线程共同访问一个对象(临界资源)的时候, 如果破坏了不可分割的操作(原子操作),就会产生数据不一致的问题
2.2线程安全问题的引发原因
多线程并发访问同一个数据资源,【可能】会引发数据的泄露或垃圾问题
package exception.io.thread.alltest;
public class TicketsThreadTest01 {
public static void main(String[] args) {
TicketsThread tt01=new TicketsThread();
Thread t01=new Thread(tt01);
Thread t02=new Thread(tt01);
Thread t03=new Thread(tt01);
t01.start();
t02.start();
t03.start();
}
}
class TicketsThread implements Runnable{
//定义要出售的票源
private int ticket =100;
private Object obj=new Object();
public void run(){
while(true){
if(ticket>0){
try{
Thread.sleep(10); //加了休眠让其他线程有执行机会
}catch(Exception ex){}
System.out.println(Thread.currentThread().getName()+"出售第"+ticket--);
}
}
}
}
2.3同步代码块解决线程安全的问题
2.4JDK1.5新特性Lock接口
package thread.security.test;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ArrayListTest01 {
public static void main(String[] args) {
}
}
class MyArrayList{
private ReadWriteLock rwl=new ReentrantReadWriteLock();
private Lock r1=rwl.readLock();
private Lock w1=rwl.writeLock();
//影响集合中的数据 写方法
public void add(Object o){
w1.lock();
w1.unlock();
}
public void add(int pos, Object o){
w1.lock();
w1.unlock();
}
public void set(int pos,Object o){
w1.lock();
w1.unlock();
}
//获取集合的数据 不会影响到集合中的任何数据 读方法
public void get(){
r1.lock();
r1.unlock();
}
public void size(){
r1.lock();
r1.unlock();
}
}
2.5将不安全的集合变成安全的集合
//格式:
//集合类型 集合名称 = Collections.synchronized+集合类型(集合名称);
package thread.security.test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class CollectionSynchronizedTest01 {
public static void main(String[] args) {
//格式:
//集合类型 集合名称 = Collections.synchronized+集合类型(集合名称);
//ArrayList不安全 -> 线程安全
List list = new ArrayList();
List l2=Collections.synchronizedList(list);
//HashMap->线程安全
Map map01=new HashMap();
Map map02=Collections.synchronizedMap(map01);
//HashSet->线程安全
Set set01=new HashSet();
Set set02=Collections.synchronizedSet(set01);
}
}