JAVA多线程笔记
来源:互联网 发布:资金流水表设计 mysql 编辑:程序博客网 时间:2024/05/27 14:12
线程与进程:
一个程序至少有一个进程,一个进程至少有一个线程.
进程
是一个正在执行的程序,每个进程执行都有一个执行顺序,也叫执行路径,或者控制单元。
进程在执行过程中拥有独立的内存单元
线程
是一个进程中的一个控制单元,线程控制着进程的执行
多个线程共享内存,极大地提高了运行效率
在windows环境下JVM启动的时候会有一java.exe进程。该进程至少有一线程负责java程序的执行,该线程的代码存在于main方法中,main方法作为java程序初试线程的起点,其他的任何线程都是由这个初始线程启动的。
JVM有两种线程:守护线程和非守护线程,守护线程是有JVM自己使用的,比如垃圾回收机制。Java的初试线程是非守护线程。
多线程的意义在于,在一个应用程序中,可以有多个部分同时执行,而操作系统并没有将多个线程看做独立的应用,以实现进程的调度、管理和资源的分配。
线程的创建:
1) 方法一:定义类继承java.lang.Thread,复写 run() 方法,通过实例化对象调用 start() 方法。
public void start()
l 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
l 结果是两个线程并发地运行;当前线程(从调用返回给 start 方法)和另一个线程(执行其 run 方法)。
l 多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。
代码示例:
class ThreadDemo extends Thread
{
public void run()
{
for(int i=0; i<1000; i++) {
System.out.println("run---");
}
}
}
public class TestThread
{
public static void main(String [] args) {
ThreadDemo td = new ThreadDemo();
td.start();
for(int i=0; i<1000; i++) {
System.out.println("main---");
}
}
}
2) 方法二:
声明实现 java.lang.Runnable 接口的类。该类然后实现 run 方法。然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递给一个新的Thread对象,然后通过start()方法启动一个线程。
public Thread(Runnable target)
分配新的 Thread 对象。这种构造方法与 Thread(null, target,gname) 具有相同的作用,其中的 gname 是一个新生成的名称。自动生成的名称的形式为 “Thread-”+n,其中的 n 为整数。
代码示例:
class ThreadDemo implements Runnable
{
public void run() {
for(int i=0; i<100; i++) {
System.out.println(Thread.currentThread()+"running");
}
}
}
public class Test
{
public static void main(String [] args) {
new Thread(new ThreadDemo()).start();
}
}
两种线程的创建方式的对比:
方法一:继承Thread类
只能单继承Thread
存放在Thread子类的run()方法中
方法二:实现Runable接口
避免了单继承的局限性
存放在实现Runable接口类的run()方法中
线程的状态:
线程安全问题:
问题原因:
当多条语句在操作一个线程共享数据时,其中一个线程只对多条数据执行了一部分,还未执行完,另一线程启动,参与到共享数据中来,就会造成共享数据的错误。
解决办法:
操作线程,将操作共享数据的多条语句,在一个线程中全部执行完,执行过程中,其他线程不能参与。
Java提供了“同步代码块”来解决线程安全问题。
Synchronized (对象) {
//多条语句
}
语句中的对象如同锁,持有锁的线程可以再同步中执行,没有取得锁的线程即使获得cpu执行权,也无法执行同步代码。
同步方法:
public void synchronized run() {
}
静态方法与成员方法的对象锁:
函数需要被对象调用,那么函数都有一个所属对象的引用,同步函数的锁是this。
同步函数被静态修饰时,使用的线程锁是该类的字节码文件对象,类.class
线程安全前提:
至少两个以上线程。
是否用的同一个线程。
单例模式:懒汉式
class Single
{
private static Single s = null;
private Single() {}
public static Single getInstance() {
if(s==null) {
synchronized(Single.class) {
if(s==null) {
s = new Single();
}
}
}
return s;
}
}
线程间通信:
多个线程操作同一资源,但是操作的动作不同。
线程死锁问题:
两个线程同时使用相同的两个线程锁,但是每一个线程只拥有其中的一个线程锁,两个线程不放开自己的线程锁,于是便产生了线程死锁的问题。
等待唤醒机制:都使用在线程同步中,因为都要对持有对象锁的那个线程操作
l publicfinal void wait() throwsInterruptedException
导致当前的线程等待,直到其他线程调用此对象的notify()方法或notifyAll()方法。换句话说,此方法的行为就好像它仅执行 wait(0) 调用一样。 当前的线程必须拥有此对象锁。
l public final void notify()
唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。
易错点: 直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程。并不是使用notify();之后就执行新的线程,只有在当前线程放弃当前线程锁的时候,才随即选择等待在当前对象上的线程。
JDK1.5新特性
将同步关键字synchronized替换成现实Lock操作。Lock 替代了synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。
注意:一定要进行线程解锁。
void await() 造成当前线程在接到信号或被中断之前一直处于等待状态。
假定调用此方法时,当前线程保持了与此 Condition 有关联的锁定。这取决于确定是否为这种情况以及不是时,如何对此作出响应的实现。通常,将抛出一个异常(比如 IllegalMonitorStateException)并且该实现必须对此进行记录。
void signal() 唤醒一个等待线程。
如果所有的线程都在等待此条件,则选择其中的一个唤醒。在从 await 返回之前,该线程必须重新获取锁定。
void signalAll() 唤醒所有等待线程。
如果所有的线程都在等待此条件,则唤醒所有线程。在从 await 返回之前,每个线程都必须重新获取锁定。
javaAPI示例:
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
守卫线程:
守卫线程是JVM的后台线程,例如垃圾回收机制,随着JVM的线程启动而启动,JVM的所有非守卫线程结束而结束。
public final void setDaemon(boolean on)
将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。
该方法必须在启动线程前调用。
该方法首先调用该线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException(在当前线程中)。
on - 如果为 true,则将该线程标记为守护线程。
线程的常用方法:
public final int getPriority() 返回线程的优先级。
public final void setPriority(int newPriority)
更改线程的优先级。
首先调用线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException。
在其他情况下,线程优先级被设定为指定的 newPriority 和该线程的线程组的最大允许优先级相比较小的一个。
优先级:public static final int MIN_PRIORITY/NORM_PRIORITY/MAX_PRIORITY
public void interrupt() 中断线程。
public static void yield() 暂停当前正在执行的线程对象,并执行其他线程。
使用Executor(扩展)
通过使用java.util.concurrent包中的执行器(Executor)管理Thread对象,简化多线程编程。
Executor 在客户端和任务执行之间提供了一个间接层;与客户端直接执行任务不同,这个中介对象将执行任务。Excutor允许你管理异步任务的执行,而无须显示的管理线程的生命周期。
Excutor是启动任务的优选方法。
java.util.concurrent 下 接口 Executor
执行已提交的 Runnable 任务的对象。
此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节、调度等)分离开来的方法。通常使用 Executor 而不是显式地创建线程。
例如,可能会使用以下方法,而不是为一组任务中的每个任务调用 new Thread(new(RunnableTask())).start():
Executor executor = anExecutor;
executor.execute(new RunnableTask1());
executor.execute(new RunnableTask2());
...
构造方法
void execute(Runnable command)
在未来某个时间执行给定的命令。该命令可能在新的线程、已入池的线程或者正调用的线程中执行,这由 Executor 实现决定。
ExecutorService是具有生命周期的Executor。
代码示例
Import java.util.cincurrent.*;
class Thre implements Runnable
{
public void run() {}
}
class ThreadTest
{
public static void main(String[] args)
{
ExecutorService exec = Exectors.newCachedThreadPool();
;
for(int i=0; i<5; i++) {
exec.execute(new Thre());
exec.shutdown();
}
}
}
java.util.concurrent
类 Executors:
此包中所定义的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 类的工厂和实用方法。此类支持以下各种方法:
创建并返回设置有常用配置字符串的ExecutorService 的方法。
创建并返回设置有常用配置字符串的ScheduledExecutorService 的方法。
创建并返回“包装的”ExecutorService方法,它通过使特定于实现的方法不可访问来禁用重新配置。
创建并返回 ThreadFactory 的方法,它可将新创建的线程设置为已知的状态。
创建并返回非闭包形式的 Callable的方法,这样可将其用于需要 Callable 的执行方法中
- java笔记--java多线程
- Java多线程笔记
- Java多线程学习笔记
- java笔记8多线程
- Java多线程学习笔记
- Java多线程阅读笔记
- java 多线程笔记。
- Java学习笔记---多线程
- java多线程笔记
- JAVA多线程笔记
- java多线程笔记
- java笔记 多线程总结
- JAVA 多线程 笔记
- java多线程学习笔记
- Java多线程讲解笔记
- JAVA笔记:多线程入门
- Java多线程学习笔记
- Java多线程笔记
- C语言中动态分配二维数组
- 单例/单体模式(Singleton)
- 链表的创建,插入,删除,显示,反转
- UVa 10010
- CF 208E
- JAVA多线程笔记
- 在openbox的menu.xml中配置需要在终端中运行的程序
- 【动态规划】【单调队列】最大子序列的和 (max.c/cpp/pas)
- Android SmsMessage类
- 去消eclipse中js报错
- 如何解决iOS模拟器调试时不能出现iPad/iPhone Simulator
- 虎嗅网 《美国到底抓住了华为什么痛脚?》
- ARM实验中的小问题
- 链栈