实现线程的两种方法:
1. 类A继承Thread类: //具体例子:书《从零开始写Java web框架》P159
Thread thread1 = new Thread(){
// 代码块
}
//实际就是代码块继承new后声明的类,然后用对象接收(可重写覆盖某方法)
2.实现Runnable 接口:
Thread t=new Thread(
new Runnable() {
//定义匿名内部类,该类实现Runnable接口
@Override
public void run() {//实现run()
}
}
}
线程的创建:
new Thread()
new Thread(String name)
new Thread(Runnable target)
new Thread(Runnable target,String name)
Thread线程的方法:
Void start() //启动线程
//类A.start()--相当于执行类A里重写的run()方法,但即使多个run仍是单线程
//实现线程的两种方法都通过此方法启动线程
static void sleep(long millis) //线程休眠 多少毫秒
static void sleep(long millis,int nanos) //线程休眠 多少毫秒 精确到纳秒
void join() //使其他线程等待,等待当前线程停止
void join(long millis) //使其他线程等待多长毫秒
void join(long millis,int nanos) //使其他线程等待多长毫秒,可精确到纳秒
static void yield() //当前运行线程释放处理器的资源
static Thread currentThread() //返回当前运行的线程引用
//例 Thread.currentThread().getName()返回字符串 Thread-数(第几个线程数)
如何正确的停止线程的方法:(错误的停止的方法是stop()方法)
interrupt();//会使得线程Waiting和Timed_Waiting状态的线程抛出 interruptedException异常
Thread.interrupt();//其书写所在线程停止
A.interrupt() //类A线程停止 //类A继承 Thread类
A.isInterrupted() 返回true 表类A线程停止
Thread.currentThread.interrupt() //其书写所在线程是否停止
多个start()多个线程,从程序来说,是同时运行的,会执行方法1一部分,再执行方法2一部分,都没谱
对于cpu则是选择性执行方法,即
两个start()里都为无限循环,方法1执行某部分,方法2执行某部分,以此类推.(断点,循环次数,没谱任意)
wait 与 notifyAll:
示例
不同线程引用改变同一变量会变脏数据(即枪临界资源)
临界资源--就相当于打印机
synchronized(变量){
变量.wait();---不参与变量的抢夺
}
其后代码不运行,唤醒会执行唤醒在另一个run里
synchronized(变量){
变量.notifyAll();
}
//notify()方法仅唤醒一个线程(等待队列中的第一个线程)并允许他去获得锁.notifyAll()方法唤醒所有等待这个对象的线程并允许他们去竞争获得锁
sleep与 wait区别:
sleep需要设置过期时间(时间到了自然执行),wait可设置时间,或不设置时间,不设置时间时通过notify()|notifyAll()方法去唤醒
调用sleep()方法并不会释放锁.而wait()方法则不同,当调用wait()方法后,线程会释放掉他所占用的锁,从而使线程所在对象中的 其他synchronized数据可以被其他线程使用。
即sleep不会释放锁;而wait会,从而使synchronized数据可以被其他线程使用。
sleep()方法可以在任何地方使用。
wait()方法只能在synchronized方法或synchronized块中使用。
在main里执行start,要带new 关键字!?
run方法引用方法为例 public synchronized void name() {}
synchronized 会对包含此方法的'[类]'加锁。
synchronized:
即多个线程引用的'[类]'为同一'对象',当一个线程用带synchronized的方法时(其余方法不受影响),别的线程用不了,需等待其执 行完;不同'对象'不会等,会同时运行程序。
提示-当有需要解决的问题时,内部类(结合构造方法)可解决.。
static synchronized,找的是类,类同冲突会锁住的;不是 synchronized 的类对象;两者不会冲突static synchronized与 synchronized的方法。
争用条件 (race Condition):
当多个线程同时共享访问统一数据(内存区域)时,每个线程都尝试操作该数据,从而导致数据被破坏(corrupted),这 种现象被称为争用条件。
使用/示例:
public String getSyso(String js) {
final ArrayList<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
Thread thread1 = new Thread(){
public void run() {
Iterator<Integer> iterator = list.iterator();//list是变量名
while(iterator.hasNext()){
Integer integer = iterator.next();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Thread thread2 = new Thread(){
public void run() {
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()){
Integer integer = iterator.next();
}
}
}
thread1.start();
thread2.start();
}
ThreadLocal:
线程局部变量//使多个线程引入同一对象,其属性不互相影响
实现原理 线程做key的一个Map
使用/示例:
线程共享可变数据,看如下:
同步:多个线程引用的外部同一内容由关键字 synchronized 修饰,叫同步数据(即阻塞,见下157行), volatile 使读取时是实时的。
名词解释
原子性: 一个非long|double的变量,另个线程已经改过此变量(无 synchronized 修饰),这一瞬间此线程又读取此变量,可读取到已经改后的值 (即读取此变量时可获取在另线程更改后的值(但不保证,可用 volatile 修饰此原子性变量,解决使能"实时看到"此变量值))
非原子性:例(num++操作此会有多步骤;读写先后执行)如:第二个线程在第一个线程读后写回新值期间,读取会得到与第一线程读取相同的值,此时就要用 synchronized 修饰锁资源解决了(此时就是非原子 性了)
用volatile修饰的原子性变量,就不要synchronized修饰了;缺陷:此变量++操作时(是非原子性的),数据可能会出错
集合
CopyOnWriteArrayList 同list使用;CopyOnWriteArraySet同 set 使用;ConcurrentMap 猜同 map
用上别用同步在并发中
同步器 常用CountDownLatch和Semaphore //使线程能够等待另一个线程的对象
倒计数锁存器 CountDownLatch
是一次性的障碍,允许一个或者多个线程等待一个 ,或者多个其他线程来做某些事情。
CountDownLatch的唯一构造器带有一个int类型的参数,这个int参数是指允许所有在等待的线程被处理之前,必须在锁存器上调用countDown方法的次数。
工作队列:
创建
ExecutorService executor = Executors.newSingleThreadExecutor();
执行踢脚一个runnable方法
executor.execute(runnable);
终止
executor.shutdown();
线程池:
如果想让不止一个线程来处理来自这个队列的请求,只要调用一个不同的静态工厂,
这个工厂创建了一种不同的执行服务,称作线程池(thread pool)
通过线程池来创建和管理多线程: JDK5提供了 Concurrent 并发工具包
常用线程池
Executors.newCachedThreadPool 可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
Executors.newFixedThreadPool 固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。?
Executors.newSingleThreadExecutor 单线程的线程池,此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
Executors.newScheduledThreadPool 大小无限制的线程池。此线程池支持定时以及周期性执行任务,例 JAVA\其它\后台配置\定时任务.txt
线程池使用
1. 通过ExecutorService接口的submit方法提交一个实现Callable/Runnable接口的类,可获取一个Future接口的对象,通过Future接口的get方法获取Callable任务的执行结果,可设置超时时间。
2. 通过Executor接口的execute方法提交一个实现Runnable接口的类,不可通过Future获取执行结果,不可设置超时时间。
java.util.Timer被 Executor Framework中的 ScheduledThreadPoolExecutor替代(原因p240)
线程组(ThreadGroup)-已过时,毫无用处,所提供的方法都有更好的实现方法
线程和进程各自有什么区别和优劣呢?
进程是资源分配的最小单位,线程是程序执行的最小单位
进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。
线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点。
但是多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间
Java的线程生命周期有六种状态
New(初始化状态) 线程其实是还没被创建的,所以这个时候是不可能分配CPU执行这个线程的
Runnable(可运行/运行状态) 分配CPU执行了,在New状态时候我们调用start()方法后线程就处于这个状态
Blocked(阻塞状态) 不分配CPU执行,即被 synchronized 修饰的方法或者代码块同一时刻只能有一个线程执行,而其他竞争锁的线程就从Runnable到了Blocked状态!当某个线程竞争到锁了它就变成了Runnable状态。注意并发包中的 Lock,是会让线程属于等待状态而不是阻塞,只有synchronized是阻塞
Waiting(无时间限制的等待状态)
不分配CPU执行
调用无参的Object.wait()方法。等到notifyAll()或者notify()唤醒就会回到Runnable状态。
调用无参的Thread.join()方法。也就是比如你在主线程里面建立了一个线程A,调用A.join(),那么你的主线程是得等A执行完了才会继续执行,这是你的主线程就是等待状态。
调用LockSupport.park()方法。LockSupport是Java6引入的一个工具类Java并发包中的锁都是基于它实现的,再调用LocakSupport.unpark(Thread thread),就会回到Runnable状态
Timed_Waiting(有时间限制的等待状态)
就是有时间的等待,不分配CPU执行
Object.wait(long timeout)。
Thread.join(long millis)。
Thread.sleep(long millis)。注意 Thread.sleep(long millis, int nanos) 内部调用的其实也是Thread.sleep(long millis)。
LockSupport.parkNanos(Object blocked,long deadline)。
LockSupport.parkUntil(long deadline)
Terminated(终止状态)
在我们的线程正常run结束之后或者run一半异常了就是终止状态!
转载:https://blog.csdn.net/weixin_39728880/article/details/100832020